引言
在Java面向对象编程中,抽象类(Abstract Class)
和接口(Interface)
是两个非常重要的概念,它们都是实现多态和代码抽象
的重要机制。虽然Java 8之后接口的功能得到了极大增强,但抽象类和接口仍然有着本质的区别和各自适用的场景。
一、抽象类(Abstract Class)
1、基本概念
- 抽象类是一种
不能被实例化
的类,它通常作为其他类的基类(父类),用来定义一些通用的属性和行为
- 抽象类的主要目的是为子类提供一个公共的模板或部分实现,同时强制子类去实现某些特定的方法(即抽象方法)
- 要定义一个抽象类,需要使用关键字
abstract
修饰类
java
public abstract class Animal {
// 可以有字段属性
protected String name;
// 可以有构造方法(但不能直接实例化)
public Animal(String name) {
this.name = name;
}
// 具体方法:有实现
public void sleep() {
System.out.println("The animal is sleeping");
}
// 抽象方法:只有声明,没有实现,必须使用 abstract关键字修饰
public abstract void makeSound();
}
2、抽象类的特性
不能实例化
:无法创建抽象类的对象- 可以包含
抽象方法
和具体方法(包括静态方法)
:抽象方法没有实现,具体方法有实现 - 可以包含
实例变量
、静态变量
- 可以有
构造方法
:虽然不能直接实例化,但子类有默认的super()
或手动的super(实参列表)
- 子类继承抽象类后,
必须实现所有的抽象方法
(除非子类也是抽象类) 单继承限制
:一个类只能继承一个抽象类
3、抽象类的使用
java
// 子类继承抽象类
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println("Woof! Woof!");
}
}
// 使用
public class Main {
public static void main(String[] args) {
// Animal animal = new Animal("Generic"); // 错误!不能实例化抽象类
Animal myDog = new Dog("Buddy");
myDog.makeSound(); // 输出: Woof! Woof!
myDog.sleep(); // 输出: The animal is sleeping
}
}
二、接口(Interface)
1、基本概念
- 接口是一种完全抽象的引用类型,它定义了一组方法规范而不提供实现(Java 8 之前),也不能包含成员变量(除了常量)
- 接口的主要目的是定义一组规范或契约,让不同的类去实现这些规范,从而实现多态和解耦
- 使用关键字
interface
定义接口
java
public interface Swimmable {
// 常量 默认是 public static final
int MAX_SPEED = 120;
// 抽象方法 默认是 public abstract
void swim();
// Java 8+ 默认方法
default void floatOnWater() {
System.out.println("Floating on water");
}
// Java 8+ 静态方法
static boolean canSwim(Object obj) {
return obj instanceof Swimmable;
}
// Java 9+ 私有方法
private void secretSwimTechnique() {
System.out.println("Secret technique");
}
}
2、接口的特性
Java 7 及以前
:只能包含抽象方法
和常量
- 公共的静态的常量:其中
public static final
可以省略 - 公共的抽象的方法:其中
public abstract
可以省略
- 公共的静态的常量:其中
Java 8
:引入了默认方法
(default methods)和静态方法
- 公共的默认的方法:其中
public
可以省略,但default
不能省略 - 公共的静态的方法:其中
public
可以省略,但static
不能省略 - 使用
default
关键字可以为接口提供默认实现,这样实现类可以选择是否重写该方法 - 接口中可以定义静态方法,并通过接口名直接调用
- 公共的默认的方法:其中
Java 9
:引入了私有方法
(private methods)- 接口中可以定义私有方法,用于在接口内部复用代码逻辑
没有构造方法
:不能实例化多实现
:一个类可以实现多个接口
3、接口的使用
java
public class Duck extends Animal implements Swimmable, Flyable {
public Duck(String name) {
super(name);
}
// 实现抽象类Animal中抽象方法
@Override
public void makeSound() {
System.out.println("Quack! Quack!");
}
// 实现接口Swimmable中抽象方法
@Override
public void swim() {
System.out.println("Duck is swimming");
}
// 实现接口Flyable中抽象方法
@Override
public void fly() {
System.out.println("Duck is flying");
}
}
// 使用
public class Main {
public static void main(String[] args) {
Duck duck = new Duck("Donald");
duck.swim(); // 调用实现的接口方法 输出: Duck is swimming
duck.floatOnWater(); // 调用接口的默认方法 输出: Floating on water
Swimmable.canSwim(duck); // 调用接口的静态方法 返回: true
}
}
三、抽象类 vs 接口
1、语法层面对比
特性 | 抽象类 | 接口 |
---|---|---|
实例化 |
不能 | 不能 |
方法类型 |
抽象方法和具体方法 | 抽象方法、默认方法、静态方法、私有方法 |
字段 |
可以有各种字段 | 只能有静态常量 |
构造方法 |
有 | 无 |
继承性 |
单继承 | 多实现 |
访问修饰符 |
各种修饰符 | 默认 public |
2、设计理念对比
抽象类
表示"是一个"的关系,用于定义相关对象的基本共性
。例如:Dog是一种Animal共享代码
:多个相关类需要共享相同的代码和字段扩展基类功能
:需要定义非静态、非final的字段或非public方法定义模板
:提供模板方法模式的基础实现
接口
表示"具有能力"的关系,用于定义对象的特定能力
。例如:Duck具有Swimmable和Flyable的能力定义契约
:定义类必须实现的行为,而不关心如何实现多重能力
:一个类需要具备多种不相关的能力解耦
:实现与定义分离,提高代码灵活性
3、代码示例对比
java
// 抽象类示例:图形类层次结构
public abstract class Shape {
protected String color;
public Shape(String color) {
this.color = color;
}
public abstract double area();
public abstract double perimeter();
public String getColor() {
return color;
}
}
public class Circle extends Shape {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
@Override
public double perimeter() {
return 2 * Math.PI * radius;
}
}
// 接口示例:多能力组合
public interface Drawable {
void draw();
}
public interface Resizable {
void resize(double factor);
}
public class ResizableCircle extends Circle implements Drawable, Resizable {
public ResizableCircle(String color, double radius) {
super(color, radius);
}
@Override
public void draw() {
System.out.println("Drawing a circle with color: " + getColor());
}
@Override
public void resize(double factor) {
// 调整大小的实现
}
}
四、Java 8+ 的新特性对设计的影响
Java 8 的默认方法使接口能够向后兼容地添加新功能,减少了破坏性变更
java
public interface Vehicle {
void start();
// 新添加的默认方法,不影响已有实现
default void stop() {
System.out.println("Vehicle stopped");
}
static void describe() {
System.out.println("This is a vehicle interface");
}
}
但是,这也带来了"多重继承"的复杂性,当多个接口有相同签名的默认方法
时:会出现冲突
java
public interface A {
default void hello() {
System.out.println("Hello from A");
}
}
public interface B {
default void hello() {
System.out.println("Hello from B");
}
}
public class C implements A, B {
// 必须重写 hello() 方法解决冲突
@Override
public void hello() {
A.super.hello(); // 显式选择调用 A 的实现
}
}