问题
接口和抽象类的区别,如何选择?
答案
1. 核心区别对比
| 维度 | 接口(Interface) | 抽象类(Abstract Class) |
|---|---|---|
| 关键字 | interface | abstract class |
| 继承/实现 | 一个类可实现多个接口 | 一个类只能继承一个抽象类 |
| 方法 | JDK 8前只能是抽象方法 JDK 8+可有默认方法和静态方法 | 可以有抽象方法和具体方法 |
| 成员变量 | 只能是public static final常量 | 可以有任意类型的成员变量 |
| 构造方法 | 不能有构造方法 | 可以有构造方法 |
| 访问修饰符 | 方法默认public abstractJDK 9+可有 private方法 | 可以有任意访问修饰符 |
| 设计目的 | 定义行为规范(What to do) | 定义模板和共性(Is-A关系) |
| 使用场景 | 不相关类实现相同行为 | 有共同父类的类层次 |
2. 接口详解
基本语法
// JDK 8之前的接口
public interface Flyable {
// 常量(默认public static final)
int MAX_SPEED = 1000;
// 抽象方法(默认public abstract)
void fly();
void land();
}
JDK 8接口增强
public interface Flyable {
// 抽象方法
void fly();
// 默认方法(default)
default void land() {
System.out.println("Landing...");
}
// 静态方法
static void checkWeather() {
System.out.println("Checking weather...");
}
}
JDK 9接口增强
public interface Flyable {
void fly();
default void performFlight() {
checkFuel(); // 调用private方法
fly();
}
// private方法(JDK 9+)
private void checkFuel() {
System.out.println("Checking fuel...");
}
// private静态方法
private static void log(String msg) {
System.out.println("Log: " + msg);
}
}
3. 抽象类详解
public abstract class Animal {
// 成员变量(可以是任意类型)
private String name;
protected int age;
// 构造方法
public Animal(String name) {
this.name = name;
}
// 抽象方法(子类必须实现)
public abstract void makeSound();
// 具体方法(子类可直接使用)
public void sleep() {
System.out.println(name + " is sleeping");
}
// Getter/Setter
public String getName() {
return name;
}
}
// 子类
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(getName() + " barks");
}
}
4. 设计原则视角
接口:定义能力(Can-Do关系)
// 定义"会飞"的能力
public interface Flyable {
void fly();
}
// 定义"会游泳"的能力
public interface Swimmable {
void swim();
}
// 鸟类可以飞
public class Bird implements Flyable {
@Override
public void fly() {
System.out.println("Bird flies");
}
}
// 鸭子既会飞又会游
public class Duck implements Flyable, Swimmable {
@Override
public void fly() {
System.out.println("Duck flies");
}
@Override
public void swim() {
System.out.println("Duck swims");
}
}
抽象类:定义本质(Is-A关系)
// 定义动物的本质
public abstract class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
// 所有动物都有的共性行为
public void breathe() {
System.out.println(name + " is breathing");
}
// 不同动物的差异行为
public abstract void makeSound();
}
// 狗是一种动物
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + " barks");
}
}
5. 如何选择?
选择接口的场景
✅ 多种不相关的类需要实现相同行为
// Serializable接口:String、ArrayList、HashMap都可序列化
public class User implements Serializable { }
public class Order implements Serializable { }
✅ 需要多重继承能力
public class Smartphone implements Callable, Camera, MusicPlayer {
// 手机同时具备打电话、拍照、播放音乐的能力
}
✅ 定义服务契约(API设计)
// Service层接口
public interface UserService {
User getUserById(Long id);
void saveUser(User user);
}
// 可以有多种实现
public class DatabaseUserService implements UserService { }
public class CachedUserService implements UserService { }
选择抽象类的场景
✅ 需要共享代码(状态和行为)
public abstract class HttpServlet {
// 共享的成员变量
protected ServletConfig config;
// 模板方法
public void service(HttpRequest req, HttpResponse res) {
if ("GET".equals(req.getMethod())) {
doGet(req, res);
} else if ("POST".equals(req.getMethod())) {
doPost(req, res);
}
}
// 子类实现具体逻辑
protected abstract void doGet(HttpRequest req, HttpResponse res);
protected abstract void doPost(HttpRequest req, HttpResponse res);
}
✅ 需要构造函数初始化
public abstract class Vehicle {
private String brand;
// 构造方法强制子类初始化
public Vehicle(String brand) {
this.brand = brand;
}
public abstract void start();
}
✅ 需要非public成员
public abstract class BaseDao<T> {
// protected方法,仅子类可访问
protected Connection getConnection() {
// 获取数据库连接
}
public abstract T findById(Long id);
}
6. 实际应用案例
案例1:JDK中的设计
// 接口:定义能力
List<String> list = new ArrayList<>(); // List是接口
Map<String, Object> map = new HashMap<>(); // Map是接口
// 抽象类:提供通用实现
public abstract class AbstractList<E> implements List<E> {
// 提供通用的add、remove等方法实现
}
public class ArrayList<E> extends AbstractList<E> {
// 继承通用实现,专注于自己的特性
}
案例2:Spring框架
// 接口定义服务契约
public interface UserService {
User findById(Long id);
}
// 抽象类提供事务、日志等通用功能
public abstract class BaseService {
@Autowired
protected Logger logger;
protected void logOperation(String operation) {
logger.info("Executing: " + operation);
}
}
// 实现类
@Service
public class UserServiceImpl extends BaseService implements UserService {
@Override
public User findById(Long id) {
logOperation("findById: " + id);
// 具体实现
}
}
案例3:模板方法模式
// 抽象类定义算法骨架
public abstract class DataProcessor {
// 模板方法(final防止被重写)
public final void process() {
readData();
processData();
writeData();
}
// 具体步骤由子类实现
protected abstract void readData();
protected abstract void processData();
protected abstract void writeData();
}
public class CsvDataProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("Reading CSV data");
}
@Override
protected void processData() {
System.out.println("Processing CSV data");
}
@Override
protected void writeData() {
System.out.println("Writing CSV data");
}
}
7. JDK 8+的接口变化影响
接口与抽象类的界限模糊
// JDK 8后,接口也可以有方法实现
public interface Flyable {
// 抽象方法
void fly();
// 默认方法(有实现)
default void takeOff() {
System.out.println("Taking off...");
fly();
}
// 静态方法
static void printManual() {
System.out.println("Flight manual...");
}
}
接口的优势进一步增强:
- 可以有默认实现,减少子类重复代码
- 保持多重继承的能力
- 更适合函数式编程(Lambda表达式)
8. 选择决策树
需要定义行为规范?
├─ 是 → 考虑接口
└─ 否 → 考虑抽象类
需要多重继承?
├─ 是 → 必须用接口
└─ 否 → 可用抽象类
需要共享状态(成员变量)?
├─ 是 → 抽象类
└─ 否 → 接口
需要构造方法?
├─ 是 → 抽象类
└─ 否 → 接口
实现类之间是Is-A关系?
├─ 是 → 抽象类
└─ 否 → 接口
9. 面试答题要点
标准回答结构:
- 核心区别:
- 接口:定义行为规范,支持多实现,JDK 8+可有默认方法
- 抽象类:定义模板,共享代码,支持构造方法和成员变量
- 选择原则:
- 接口:多重继承、定义能力、服务契约
- 抽象类:代码复用、模板方法、Is-A关系
-
JDK版本影响:JDK 8引入default和static方法,接口更强大
- 实际案例:JDK集合框架、Spring框架、模板方法模式
加分点:
- 说明JDK 8/9对接口的增强
- 了解设计原则(Is-A vs Can-Do)
- 能举实际项目中的应用案例
- 知道接口与抽象类可以配合使用
10. 总结
记忆口诀:
- 接口定能力,抽象定本质
- 接口多实现,抽象单继承
- 接口轻契约,抽象重复用
- JDK8后,接口更强大
最佳实践:
- 优先使用接口(灵活性更高)
- 需要代码复用时用抽象类
- 接口 + 抽象类组合使用效果最佳
// 推荐模式:接口定义契约 + 抽象类提供通用实现
public interface List<E> { } // 接口
public abstract class AbstractList<E> implements List<E> { } // 抽象类
public class ArrayList<E> extends AbstractList<E> { } // 具体类