【Java】抽象类和接口对比+详解
1. 定义与基本语法
抽象类:
- 使用
abstract
关键字声明 - 可以包含抽象方法(无实现)和具体方法(有实现)
- 可以有成员变量、构造方法
- 子类通过
extends
关键字继承抽象类,必须实现所有抽象方法
接口:
- 使用
interface
关键字声明 - Java 8 之前只能包含抽象方法和常量;Java 8 及以后可以有默认方法(
default
)和静态方法(static
) - 成员变量默认是
public static final
(常量) - 类通过
implements
关键字实现接口,必须实现所有抽象方法
2. 核心区别对比
特性 | 抽象类 | 接口 |
---|---|---|
继承方式 | 单继承(一个类只能继承一个抽象类) | 多实现(一个类可以实现多个接口) |
方法类型 | 可以包含抽象方法和具体方法 | Java 8 后有默认方法和静态方法 |
成员变量 | 可以有各种类型的成员变量 | 只能是public static final 常量 |
构造方法 | 有构造方法(供子类调用) | 没有构造方法 |
访问修饰符 | 可以使用private 、protected 、public | 方法默认public ,变量默认public static final |
使用场景 | 表示 "is-a" 关系,用于抽取类间的共性实现 | 表示 "has-a" 能力,用于定义类的行为规范 |
3. 示例代码
抽象类示例
// 抽象类 - 表示动物
abstract class Animal {// 成员变量protected String name;// 构造方法public Animal(String name) {this.name = name;}// 抽象方法(无实现)public abstract void makeSound();// 具体方法(有实现)public void eat() {System.out.println(name + "正在吃东西");}
}// 子类继承抽象类
class Dog extends Animal {public Dog(String name) {super(name);}// 必须实现抽象方法@Overridepublic void makeSound() {System.out.println(name + "汪汪叫");}
}class Cat extends Animal {public Cat(String name) {super(name);}@Overridepublic void makeSound() {System.out.println(name + "喵喵叫");}
}public class Test {public static void main(String[] args) {Animal dog = new Dog("阿黄");Animal cat = new Cat("咪咪");dog.makeSound();dog.eat();cat.makeSound();cat.eat();}
}
super(name)
的作用是调用父类(Animal
类)的构造方法,并将参数name
传递给父类的构造方法。
Dog
和Cat
作为Animal
的子类,它们的构造方法中必须通过super(name)
显式调用父类的构造方法。这是因为父类Animal
定义了带参数name
的构造方法,而 Java 规定:当父类没有无参构造方法时,子类构造方法必须在第一行通过super(参数)
调用父类对应的有参构造方法,以确保父类的成员变量(如name
)能被正确初始化。
通过这种方式,子类创建对象时传入的name
会被传递到父类,完成Animal
类中name
成员变量的赋值,使子类对象能继承并使用该属性(如在makeSound()
和eat()
方法中通过name
输出动物名称)。
接口示例
// 接口 - 表示会飞的能力
interface Flyable {// 常量int MAX_HEIGHT = 1000; // 等价于 public static final int MAX_HEIGHT = 1000;// 抽象方法void fly();// 默认方法(Java 8+)default void land() {System.out.println("降落到地面");}// 静态方法(Java 8+)static void showMaxHeight() {System.out.println("最大飞行高度: " + MAX_HEIGHT + "米");}
}// 接口 - 表示会游泳的能力
interface Swimmable {void swim();
}// 类实现多个接口
class Duck extends Animal implements Flyable, Swimmable {public Duck(String name) {super(name);}@Overridepublic void makeSound() {System.out.println(name + "嘎嘎叫");}@Overridepublic void fly() {System.out.println(name + "在天空飞翔");}@Overridepublic void swim() {System.out.println(name + "在水里游泳");}
}
4. 使用场景分析
使用抽象类的情况:
- 多个类共享相同的实现逻辑时(代码复用)
- 需要定义类的基础行为并为子类提供默认实现
- 需要包含成员变量和构造方法
- 表示类之间的继承关系("is-a")
使用接口的情况:
- 定义类的行为规范但不关心具体实现
- 需要实现多继承的效果(一个类具备多种能力)
- 作为不同模块之间的契约(API 设计)
- 表示类具备某种能力("has-a")
5. Java 8+ 接口新特性带来的变化
Java 8 引入的默认方法和静态方法模糊了抽象类和接口的界限,但核心区别依然存在:
- 接口不能有构造方法和普通成员变量
- 一个类只能继承一个抽象类,但可以实现多个接口
- 接口的默认方法可以被实现类重写,而抽象类的具体方法也可以被重写
6. 综合示例
// 抽象类 - 交通工具
abstract class Vehicle {protected String brand;protected int speed;public Vehicle(String brand) {this.brand = brand;this.speed = 0;}// 抽象方法public abstract void accelerate();// 具体方法public void brake() {speed = Math.max(0, speed - 10);System.out.println(brand + "减速,当前速度: " + speed + "km/h");}public void displayInfo() {System.out.println("品牌: " + brand + ", 当前速度: " + speed + "km/h");}
}// 接口 - 可充电
interface Chargeable {int MAX_BATTERY = 100; // 最大电量void charge();default void showBatteryLevel(int level) {System.out.println("当前电量: " + level + "%");}
}// 接口 - 有导航
interface Navigable {void navigate(String destination);
}// 电动汽车 - 继承抽象类并实现多个接口
class ElectricCar extends Vehicle implements Chargeable, Navigable {private int batteryLevel;public ElectricCar(String brand) {super(brand);this.batteryLevel = 50; // 初始电量50%}@Overridepublic void accelerate() {if (batteryLevel > 0) {speed = Math.min(120, speed + 20);batteryLevel = Math.max(0, batteryLevel - 5);System.out.println(brand + "加速,当前速度: " + speed + "km/h");showBatteryLevel(batteryLevel);} else {System.out.println("电量耗尽,无法加速");}}@Overridepublic void charge() {batteryLevel = Math.min(MAX_BATTERY, batteryLevel + 30);System.out.println(brand + "正在充电");showBatteryLevel(batteryLevel);}@Overridepublic void navigate(String destination) {System.out.println("从当前位置导航到: " + destination);System.out.println("预计行驶时间: 30分钟");}
}// 测试类
public class AbstractInterfaceDemo {public static void main(String[] args) {ElectricCar tesla = new ElectricCar("特斯拉Model 3");tesla.displayInfo();tesla.accelerate();tesla.accelerate();tesla.brake();tesla.navigate("中央公园");tesla.charge();}
}
7. 总结
- 抽象类注重代码复用和继承关系,适合定义类的基础结构
- 接口注重行为规范和多实现,适合定义类的能力
- 抽象类是 "是什么",接口是 "能做什么"
- 在设计时,应优先考虑使用接口,当需要共享代码实现时再考虑抽象类
- Java 8 + 的接口默认方法使得接口可以提供默认实现,但不会改变接口作为行为规范的本质