1 抽象类
1.1 语法规则
在之前的打印图形例子中, 我们发现, 父类 Shape 中的 draw 方法好像并没有什么实际工作, 主要的绘制图形都是由Shape 的各种子类的 draw 方法来完成的. 像这种没有实际工作的方法, 我们可以把它设计成一个 抽象方法(abstractmethod), 包含抽象方法的类我们称为抽象类(abstract class).
abstract class Shape {
abstract public void draw();
}
- 在 draw 方法前加上 abstract 关键字, 表示这是一个抽象方法. 同时
抽象方法没有方法体 (没有 { }, 不能执行具体代码). 对于包含抽象方法的类, 必须加上 abstract 关键字表示这是一个抽象类.
注意事项
- 抽象类不能实例化
2.抽象方法不能是 private 的()
3.抽象类中可以包含其他的非抽象方法, 也可以包含字段. 这个非抽象方法和普通方法的规则都是一样的, 可以被重写,也可以被子类直接调用.(抽象类除了包含抽象方法和不能实例化之外和普通类相似 ) 4.final修饰的抽象方法也不能被重写,因为final修饰就代表方法不能进行修改即不能重写.
🔑打印图形栗子:
abstract class Printf{
String name = "ant";
abstract public void printf();
}
class Rect extends Printf{
@Override
public void printf() {
System.out.println("△");
}
}
class Cur extends Printf{
@Override
public void printf() {
System.out.println("○");
}
}
class Flower extends Printf{
@Override
public void printf() {
System.out.println("?");
}
}
public class Test {
public static void printY(Printf pintf){
pintf.printf();
}
public static void main(String[] args) {
printY(new Cur());
printY(new Rect());
printY(new Flower());
}
}
1.2 抽象类的作用
抽象类存在的最大意义就是为了被继承. 抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法. 使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类了,使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题.
💡小知识: 使用抽象类相当于多了一重编译器的校验.
2 接口
接口是抽象类的更进一步. 抽象类中还可以包含非抽象方法, 和字段. 而接口中包含的方法都是抽象方法, 字段只能包含静态常量.
2.1 语法规则
我们仍然通过打印图案的栗子来看:
interface Printf{
public static final String name = "ant";
abstract public void printf();
}
class Rect implements Printf{
@Override
public void printf() {
System.out.println("△");
}
}
class Cur implements Printf{
@Override
public void printf() {
System.out.println("○");
}
}
class Flower implements Printf{
@Override
public void printf() {
System.out.println("?");
}
}
public class Test {
public static void printY(Printf printf){
printf.printf();
}
public static void main(String[] args) {
printY(new Cur());
printY(new Rect());
printY(new Flower());
}
}
💡知识点: 接口中的方法一定是抽象方法, 因此可以省略 abstract。 接口中的方法一定是 public , 因此可以省略 public。 接口中可以有static方法。 抽象方法默认是public abstract的 接口中成员变量默认是public static final修饰的 在调用的时候同样可以创建一个接口的引用, 对应到一个子类的实例。 接口不能单独被实例化 当一个类实现了一个接口后,重写方法必须加上public。 一个类可以通过关键字extends继承一个抽象类或普通类,但是只能继承一个类,同时可以通过implement实现多个接口,接口之间用逗号隔开。
🐾我们来看一个错误案例:
interface IShape {
abstract void draw() ;
}
class Rect implements IShape {
void draw() {
System.out.println("□") ;
}
}
这里报错就是因为接口中方法默认是public的,而Rect中的draw方法是包访问修饰符修饰的,作用范围比public小,由于重写方法的作用范围要大于等于父类的方法,所以程序就报错了。
2.2 接口使用实例
接下来我们用接口来实现给对象数组排序 方法一:实现Comparator接口
Comparator接口中含有比较方法,所以我们只需要创建几个类然后实现Comparator接口并重写compara方法就能实现排序功能。
💫代码实现:
package demo3;
import java.util.Arrays;
import java.util.Comparator;
class Student {
String name;
int age;
double score;
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
class AgeComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.age-o2.age;
}
}
class NameComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
class ScoreComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return (int)(o1.score-o2.score);
}
}
public class Test {
public static void main(String[] args) {
Student[] student = new Student[3];
student[0] = new Student("zhangsan",12,98.9);
student[1] = new Student("lisi",6,95.9);
student[2] = new Student("wangwu",18,88.9);
AgeComparator ageComparator = new AgeComparator();
NameComparator nameComparator = new NameComparator();
ScoreComparator scoreComparator = new ScoreComparator();
System.out.println(Arrays.toString(student));
Arrays.sort(student,ageComparator);
System.out.println(Arrays.toString(student));
Arrays.sort(student,nameComparator);
System.out.println(Arrays.toString(student));
Arrays.sort(student,scoreComparator);
System.out.println(Arrays.toString(student));
}
}
方法一:实现Comparable接口
在 sort 方法中会自动调用 compareTo 方法.所以我们只需要在Student类中实现Comparable接口并重写compara To方法即可实现排序。
package demo3;
import java.util.Arrays;
import java.util.Comparator;
class Student implements Comparable<Student>{
String name;
int age;
double score;
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
@Override
public int compareTo(Student o) {
return this.age-o.age;
}
}
public class Test {
public static void main(String[] args) {
Student[] student = new Student[3];
student[0] = new Student("zhangsan",12,98.9);
student[1] = new Student("lisi",6,95.9);
student[2] = new Student("wangwu",18,88.9);
System.out.println(Arrays.toString(student));
Arrays.sort(student);
System.out.println(Arrays.toString(student));
}
}
注意: 实现Comparable接口有个局限性,即只能排序指定的类型,若要根据其他规则去排序则需要重新修改compara To函数的内容。
2.3 克隆接口
前面我们已经知道创建对象的方法有new,克隆的方式就是第二种创建对象的方式。
Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 “拷贝”. 但是要想合法调用 clone 方法, 必须要先实现 Clonable 接口, 否则就会抛出CloneNotSupportedException 异常.
代码实现:
class Person implements Cloneable{
int age;
public Person(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test {
public static void main(String[] args)throws CloneNotSupportedException {
Person person = new Person(20);
Person person1 = (Person) person.clone();
System.out.println(person.age);
System.out.println(person1.age);
}
}
注意: 1.类必须要实现克隆接口 才能进行克隆 2.类中必须要重写clone方法 3.主函数中必须要进行异常抛出 4.当前克隆属于浅拷贝 (不会克隆引用指向的对象)后面会实现深拷贝的克隆
深拷贝克隆实现:
class Money implements Cloneable{
int num = 10;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Person extends Money implements Cloneable{
int age;
Money money = new Money();
@Override
public String toString() {
return "Person{" +
"age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person tep = (Person) super.clone();
tep.money = (Money)super.clone();
return tep;
}
}
public class Test {
public static void main(String[] args)throws CloneNotSupportedException {
Person person = new Person();
Person person1 = (Person) person.clone();
person1.money.num = 13;
System.out.println(person.money.num);
System.out.println(person1.money.num);
}
}
此时Person和Person1中的money引用指向不同的对象。
小知识: 深拷贝浅拷贝不是方法自带,是看编程人员如何去设计的。
总结: 1.抽象类可以有普通成员函数,接口一般只存在public abstract方法(jdk1.8中接口也可以有静态方法)。 2.抽象类中的成员变量可以是各种类型的,接口中只能是public static final类型。 3.抽象类只能继承一个,接口可以实现多个。 4.抽象类和接口都不能实例化。 5.抽象类不一定有抽象方法(但是声明为抽象类没有抽象方法也不能实例化),但是包含抽象方法的类一定为抽象类。 6.抽象类是对类本质的抽象,表达的是 is a的关系,抽象类包含并实现子类的通用特性,将子类存在差异化的地方进行抽象,交给子类去实现。 7.接口是对行为的抽象,表达的是 like a的关系,比如鸟可以像飞行器一样飞,但是其本质还是鸟。接口的核心是定义行为即功能,也就是实现类可以做什么,至于主体是谁、如何实现接口都不关心。
作者水平有限,若文章有任何问题欢迎私聊或留言,希望和大家一起学习进步!!! 创作不易,再次希望大家👍支持下,谢谢大家🙏
|