今日学习内容:抽象类、接口基础、二者区别,以及两个对象比较接口Comparable与Comparator。
一、抽象类 abstract
如果一个类没办法描述一个具体的事物,就可以定义为抽象类。
举例子:Animal动物类,是抽象概念,不能直接new,必须被子类继承。
1. 抽象方法
public abstract void draw();
没有方法体,没有具体实现,用abstract修饰。
规则:抽象方法必须写在抽象类中,方法要加abstract
2. 重要知识点
1. 抽象类不一定包含抽象方法。一个类只要加了abstract,它就是抽象类,可以全部都是普通成员。
2. 抽象类里面,可以和普通类一样,拥有成员变量、普通成员方法。
3. 抽象类不能直接实例化(不能new)。
◦ 但是它有构造方法,供子类super()调用。
◦ 抽象类最大的意义:用来被继承,作为父类,定义规范。
4. 继承规则(考试重点)
◦ ✅ 普通类继承抽象类:如果父抽象类含有抽象方法,普通子类必须重写全部抽象方法,其余普通方法按需选择。
◦ ✅ 抽象类继承抽象类:可以不重写父类抽象方法,重写也可以,自由选择。
◦ 多层继承举例:普通C → 抽象B → 抽象A
最终普通类C,必须重写A、B里面所有未被重写过的抽象方法。一旦某一层已经重写,子类就不用重复重写。
5. 限制:static、final、private不能修饰抽象方法。
抽象方法的目的就是留给子类重写,这三个关键字都会阻止重写,冲突。
6. 抽象类同样支持向上转型、多态,和普通类继承逻辑一致。
二、接口 interface
接口可以理解为类的公共规范,属于一种引用数据类型。
使用关键字interface定义。
接口核心规则
1. 接口内的抽象方法,默认自带 public abstract,可以省略不写。
void draw(); //等价 public abstract void draw();
2. 接口的成员变量,默认自带 public static final,是常量,定义就要赋值。
int a = 10; //等价 public static final int a = 10;
3. JDK8新增:default默认方法,可以在接口里写带有方法体的实现,实现类可以直接继承,也可以重写。
4. 接口不能实例化(不能new),但是支持向上转型。
5. 类实现接口,使用关键字 implements
public class Rect implements IShape{
//必须重写接口里面全部抽象方法
}
规则:如果类实现接口,但是没有实现接口中全部抽象方法,这个类必须定义成抽象类
接口的强大之处:解决Java单继承局限
Java的类和类之间只能单继承,但是一个类可以同时实现多个接口。
写法:class A extends B implements C,D
先继承父类,再实现多个接口。
继承 vs 实现(思想区别)
• 继承extends:关注 是不是属于这个种类(is-a)
• 接口implements:关注 有没有具备这个功能(has-a)
类和类:继承;类和接口:实现;接口和接口:继承(接口之间可以多继承)
三、对象比较:Comparable & Comparator
普通引用对象不能直接用> <比较,需要专门定义比较规则,两套方案:内部比较器、外部比较器。
1. Comparable 内部比较器
让实体类自己实现Comparable接口,属于内置比较规则。
• 方法:compareTo(Object o)
• 使用:student1.compareTo(student2)
// 写在Student类内部
@Override
public int compareTo(Student o){
return this.age - o.age;
}
返回值规则:
• 返回正数:this > o
• 返回负数:this < o
• 返回0:两者相等
缺点:规则写死在实体类内部,不方便随时切换比较逻辑(比如一会比年龄,一会比姓名)
2. Comparator 外部比较器(更灵活)
单独新建一个比较器类,第三方来比较两个对象,不改动原来Student类代码。
• 方法:compare(o1, o2)
• 可以创建多个比较器:年龄比较器AgeComparator、姓名比较器NameComparator,随时切换。
示例:按名字(String)比较
public class NameComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName());
}
}
注意:数字属性直接相减;String字符串属性,调用compareTo(),不能直接相减!
四、拓展:Cloneable接口与对象拷贝
1. Cloneable接口介绍
Cloneable是标记接口(接口里面没有任何抽象方法)。
如果想要一个类支持克隆,就需要实现Cloneable接口,否则调用clone()方法会抛出CloneNotSupportedException克隆不支持异常。
• 调用方式:对象.clone(),native本地方法,底层由C/C++实现,快速复制对象。
2. 浅拷贝 vs 深拷贝
✅ 浅拷贝(默认clone就是浅拷贝)
1. 拷贝基本数据类型:直接复制值,互不干扰。
2. 拷贝引用类型成员变量:只复制引用地址,不会新建内部对象。
原始对象和克隆后的对象,共享同一个内部子对象。如果修改内部引用对象的属性,两个对象会一起变化。
举个例子:Student里面有Address地址对象,浅拷贝后,原学生和克隆学生指向同一个Address。修改地址,两个学生的地址同时改变。
✅ 深拷贝
复制的时候,所有引用类型成员,全部新建独立对象。
原始对象与克隆对象完全独立,修改任意一方内部的引用对象,不会互相影响。
3. 两者核心对比
浅拷贝 深拷贝
基本类型 复制值 复制值
引用类型 复制地址,共用子对象 创建全新子对象,完全隔离
默认clone() 浅拷贝 需要自己手动实现
4. 小结
1. Cloneable是标记接口,没有抽象方法,仅做标记。
2. 不实现Cloneable,调用clone会抛异常。
3. Object自带的clone()默认是浅拷贝。
4. 深拷贝需要手动处理内部引用类型,重新new对象