JavaSE4️⃣OOP - 抽象类 & 接口
1、面向抽象编程
含义:引用抽象类型,避免引用具体类型。
- 上层定义规范。
- 不同子类实现具体业务逻辑。
- 调用者只需关心接口定义,无需考虑具体实现。
实现方式
- 抽象类
- 接口
2、抽象类
2.1、abstract
含义
-
抽象类:无法被实例化,只能被继承的类。
访问修饰符 abstract class 类名 { }
-
抽象方法:只定义方法签名,没有执行代码的方法。
-
使用场景:父类的方法本身不需要实现功能,目的是让子类重写。
-
作用:定义接口规范,面向抽象编程。
-
要求:抽象方法所在的类必须是抽象类。
访问修饰符 abstract 返回值类型 方法名(参数);
-
注意
- 存在关系:有抽象方法必有抽象类,反之不成立。
- 抽象方法:必须声明在抽象类中。
- 抽象类:可以不声明抽象方法,可以声明普通方法。
- 抽象类的子类:根据其是否实现了所有的抽象方法。
- 是:普通类。
- 否:必须定义为抽象类,由其子类实现抽象方法。
2.2、应用示例
需求示例:实现一个税额计算方法,计算每种收入的税额。
假定有普通类型、工资类型、免税类型。
抽象类 & 抽象方法
定义抽象方法,无需实现。
public abstract class Income {
protected double income;
public abstract double getTax();
}
调用者
无需关心实现类,只需专注于目标功能。
public double totalTax(Income... imcomes) {
double total = 0.0;
for (Income income : imcomes) {
total += income.getTax();
}
return total;
}
子类
实现父类中定义的抽象方法。
class GeneralIncome extends Income {
@Override
public double getTax() {
// 税率10%
return income * 0.1;
}
}
class SalaryIncome extends Income {
@Override
public double getTax() {
// 5000元起征
return income <= 5000 ? 0 : (income - 5000) * 0.2;
}
}
class TaxExemptIncome extends Income {
@Override
public double getTax() {
// 免税
return 0;
}
}
3、接口
抽象类的本质是类,可定义成员变量、构造方法、普通方法和抽象方法。
若只定义抽象方法,不需要其它结构,则使用 Java 接口。
3.1、interface
3.1.1、语法
接口(interface):抽象方法的集合。
本身是一种抽象类型。
-
定义接口:
-
接口:使用关键字
interface
定义,指定接口名。 -
接口方法:默认使用
public abstract
修饰。public interface 接口名 { [public abstract] 返回值类型 方法名(参数列表); }
-
-
实现接口:
-
实现类:使用
implements
实现接口,作为实现类。 -
要求:必须实现所有抽象接口,否则需要定义为抽象类。
class 类名 implements 接口名 { // 实现了所有抽象方法 } abstract class 类名 implements 接口名 { // 存在未实现的抽象方法 }
-
3.1.2、术语区分
接口
- Java 接口:特指
interface
的概念。 - 编程接口:泛指接口规范(如方法签名、数据格式、网络协议)。
重写/实现
类和接口中都有这两个概念,均使用
@override
注解。
假设已有方法 A,方法 B 对其重写或实现。
- 重写:A 已定义方法体,B 对其逻辑进行重写。
- 实现:A 仅定义方法签名,B 负责实现逻辑。
3.2、特点
- 多实现:Java 类只能继承自一个类,但可以实现多个接口。
- 多继承:
- 一个接口可以继承自另一个接口。
- 接口之间可以是多继承关系。
3.3、版本功能
随着 Java 版本更新,针对接口引入的新功能。
Java 7
全局静态常量
接口中可以定义成员变量,但只能是全局静态常量。
[public static final ]变量类型 变量名 = 常量值;
- 类型:
public static final
,无需显式声明。 - 特点:即静态(static)常量(final)的特点。
Java 8
默认方法(default)
提供方法默认实现,避免所有实现类都需要修改代码。
[public ]default 返回值类型 方法名(参数) {
// 方法体
}
- 类型:
- 使用
default
修饰。 public
,无需显式声明。
- 使用
- 特点:
- 需要提供方法实现。
- 不强制实现类进行重写,但可以重写。
静态方法(static)
提供方法实现,类似全局方法。
[public ]static 返回值类型 方法名(参数) {
// 方法体
}
- 类型:
- 使用
static
修饰。 public
,无需显式声明。
- 使用
- 特点:
- 需要提供方法实现。
- 通过接口名调用,而不是实现类名称。
Java 9
私有方法
提供方法实现,仅接口内部可访问。
// 私有方法
private 返回值类型 方法名(参数) {
// 方法体
}
// 私有静态方法
private static 返回值类型 方法名(参数) {
// 方法体
}
- 类型:
- 使用
private
修饰。 - 若声明为静态,则使用
private static
修饰。
- 使用
- 特点:
- 需要提供方法实现。
- 接口内部才能访问。
4、抽象类 vs 接口
4.1、对比
相同点
- 多态。
- 面向抽象编程。
- 不能直接实例化,需实现所有抽象方法。
不同点
从类和接口的区别理解。
抽象类 | 接口 | |
---|---|---|
构造方法 | 有 | 无 |
成员变量 | 任意类型 | public static final |
方法 | 普通方法、抽象方法 | 抽象方法 |
继承 | 单继承 | 多实现,多继承 |
抽象程度 | - | 接口比抽象类更抽象 |
4.2、最佳实践
原则:接口定义行为,类定义逻辑。
- 接口定义行为规范,层次代表抽象程度。
- 抽象类处理公共逻辑,子类实现具体逻辑。
- 调用者引用接口类型,而不是引用抽象类或具体子类型。
示例:Java 集合
┌───────────────┐
│ Iterable │
└───────────────┘
▲ ┌───────────────────┐
│ │ Object │
┌───────────────┐ └───────────────────┘
│ Collection │ ▲
└───────────────┘ │
▲ ▲ ┌───────────────────┐
│ └──────────│AbstractCollection │
┌───────────────┐ └───────────────────┘
│ List │ ▲
└───────────────┘ │
▲ ┌───────────────────┐
└──────────│ AbstractList │
└───────────────────┘
▲
│
│
┌────────────┐
│ ArrayList │
└────────────┘
public static void main(String[] args) {
List list = new ArrayList();
handle(list); // 自动向上转型
}
// 调用者:通过接口类型引用
public static void handle(Iterable it) {
}