接口与抽象类¶
写过 TypeScript 的 interface?Java 的接口你会秒懂。但 Java 8 给接口加了默认方法,能力更强。
接口 interface¶
interface Flyable {
void fly(); // 默认就是 public abstract,可省略
}
interface Swimmer {
default void swim() { // JDK 8:接口可以有默认实现
System.out.println("游泳中...");
}
}
class Duck implements Flyable, Swimmer { // 实现多个接口
@Override
public void fly() { }
}
关键:Java 类只能 extends 一个父类(单继承),但可以 implements 多个接口——这就是 Java 的"多继承"替代方案。这也是 Spring 里大量用接口的原因。
接口 vs 抽象类¶
| 接口 interface | 抽象类 abstract class | |
|---|---|---|
| 能 new 吗 | 不能 | 不能 |
| 字段 | 只能是常量 | 普通字段 |
| 方法 | 抽象 + default(JDK8) | 抽象 + 具体方法 |
| 继承 | 可实现多个 | 只能继承一个 |
| 对标前端 | TS 的 interface | 抽象程度更高的父类 |
什么时候用哪个¶
- 用接口:定义"能力/契约"。比如
Comparable(可比较)、Runnable(可运行)。Spring 服务层通常定义UserService接口 +UserServiceImpl实现。 - 用抽象类:多个子类有共享的字段和具体方法,只是个别行为不同。比如
AbstractController。
classDiagram
class Flyable { <<interface>> +fly() }
class Swimmer { <<interface>> +swim() }
class Duck
Duck ..|> Flyable : implements
Duck ..|> Swimmer : implements
class Shape { <<abstract>> +area()* }
class Circle { +area() }
Circle --|> Shape : extends
完整可运行示例¶
package com.javaglm.language.ch09;
/**
* 第 09 章 · 接口与抽象类。
* 接口(interface)对标 TS 的 interface,但 Java 8 的接口还能有 default 默认实现。
* 一个类可以实现多个接口——这就是 Java 替代"多继承"的方案。
* 抽象类(abstract class):不能 new,介于普通类和接口之间。
*/
public class InterfaceDemo {
public static void main(String[] args) {
Duck duck = new Duck();
duck.fly(); // 实现的抽象方法
duck.swim(); // 接口的 default 方法(没重写就直接用)
// 接口类型引用 = 多态
Flyable f = new Duck();
f.fly();
// 抽象类:用子类实例化
Shape circle = new Circle(2);
System.out.println("圆面积 = " + circle.area());
}
}
/** 接口:方法默认 public abstract(不用写也行)。 */
interface Flyable {
void fly();
}
/** 接口的 default 方法:JDK 8 起接口可以带默认实现。 */
interface Swimmer {
default void swim() {
System.out.println("游泳中...");
}
}
/** 一个类可以实现多个接口——这是 Java 做不到多继承的替代方案。 */
class Duck implements Flyable, Swimmer {
@Override
public void fly() {
System.out.println("鸭子飞起来了");
}
}
/** 抽象类:不能 new,可以有抽象方法(子类必须实现)和具体方法。 */
abstract class Shape {
abstract double area(); // 抽象方法,没有方法体
}
class Circle extends Shape {
private double r;
Circle(double r) { this.r = r; }
@Override
double area() { return Math.PI * r * r; }
}
:octicons-arrow-left-16: 上一章:封装·继承·多态 | 下一章:包与访问修饰符