一、继承存在的意义
一、核心目的 1:代码复用(Code Reuse)
🌰 问题场景:
假设你要开发 Dog、Cat、Bird 类,它们都有:
-
名字(
name) -
吃东西(
eat()) -
睡觉(
sleep())
如果不用继承,每个类都要重复写这些字段和方法:
java
class Dog {
String name;
void eat() { ... }
void sleep() { ... }
}
class Cat {
String name;
void eat() { ... }
void sleep() { ... }
}
// 重复!冗余!难维护!
✅ 用继承解决:
java
class Animal {
String name;
void eat() { System.out.println("Eating..."); }
void sleep() { System.out.println("Sleeping..."); }
}
class Dog extends Animal { /* 自动拥有 name, eat(), sleep() */ }
class Cat extends Animal { /* 同上 */ }
💡 继承让子类"免费"获得父类的字段和方法,避免重复造轮子。
二、核心目的 2:建立"is-a"类型关系(Type Hierarchy)
人类理解世界的方式是分类:
-
"狗是一种动物"
-
"轿车是一种交通工具"
-
"管理员是一种用户"
Java 通过继承模拟这种 "is-a" 关系:
java
Dog dog = new Dog();
Animal animal = dog; // 合法!因为 Dog is an Animal
这种关系带来两大好处:
1. 语义清晰
代码更贴近现实逻辑,易于理解和建模。
2. 支持多态(Polymorphism)
这是继承最重要的衍生价值(见下文)。
三、核心目的 3:实现多态(Polymorphism)
多态 = 同一个接口,多种实现。
🌰 示例:
java
abstract class Shape {
public abstract double area();
}
class Circle extends Shape {
private double radius;
public double area() { return Math.PI * radius * radius; }
}
class Rectangle extends Shape {
private double width, height;
public double area() { return width * height; }
}
使用时:
java
Shape s1 = new Circle(5);
Shape s2 = new Rectangle(3, 4);
System.out.println(s1.area()); // 调用 Circle 的实现
System.out.println(s2.area()); // 调用 Rectangle 的实现
二、接口的特点
在 Java 中,接口(Interface) 是一种引用类型,用于定义行为契约(Contract) ,是实现多态、解耦和模块化设计的核心机制之一。以下是接口的 核心特点,结合语法、语义和设计思想进行系统说明:
✅ 1. 使用 interface 关键字定义
java
public interface Flyable {
void fly(); // 方法声明
}
✅ 2. 所有方法默认是 public abstract(Java 8 之前)
-
在 Java 8 之前,接口中只能有抽象方法;
-
方法自动具有
public abstract修饰(即使不写); -
不能有方法体。
java
interface Drawable {
void draw(); // 等价于 public abstract void draw();
}
✅ 3. 字段默认是 public static final(即常量)
接口中定义的变量必须初始化,且不可修改:
java
interface MathConstants {
double PI = 3.14159; // 等价于 public static final double PI = 3.14159;
}
❌ 不能有普通实例变量(无法保存对象状态)。
✅ 4. 支持多实现(Multiple Inheritance of Type)
一个类可以 实现多个接口,解决 Java 单继承的限制:
java
class Bird implements Flyable, Singable, Runnable {
public void fly() { ... }
public void sing() { ... }
public void run() { ... }
}
💡 这是接口最重要的优势之一:一个对象可以具备多种能力。
✅ 5. Java 8+:支持 default 方法(提供默认实现)
为了解决接口演进问题(避免破坏已有实现),Java 8 引入 default 方法,这是一个重大改进,解决了接口在演进过程中破坏已有实现类的问题。下面我们深入讲解:
一、什么是 default 方法?
-
在接口中使用
default关键字定义的带有方法体的方法; -
实现类可以选择重写它,也可以直接继承使用;
-
它让接口可以在不破坏二进制兼容性的前提下添加新功能。
📌 基本语法:
java
public interface MyInterface {
// 抽象方法(必须由实现类提供)
void requiredMethod();
// default 方法(有默认实现)
default void optionalMethod() {
System.out.println("This is a default implementation.");
}
}
二、为什么需要 default 方法?
❌ 问题:接口演进困难(Java 8 之前)
假设你有一个接口和多个实现类:
java
// v1 版本
interface Vehicle {
void start();
}
class Car implements Vehicle {
public void start() { System.out.println("Car starting"); }
}
现在你想给 Vehicle 新增一个 stop() 方法:
java
// v2 版本(Java 8 之前)
interface Vehicle {
void start();
void stop(); // 新增方法
}
➡️ 后果 :所有已有的实现类(如 Car)立即编译失败 !因为它们没有实现 stop()。
这在大型系统或公共库(如 JDK 自身)中是灾难性的。
解决方案:用 default 提供默认实现
java
interface Vehicle {
void start();
// 新增方法,但提供默认实现
default void stop() {
System.out.println("Vehicle stopping safely.");
}
}
➡️ 结果:
-
已有实现类(如
Car)无需修改,依然能编译运行; -
新代码可以调用
stop(); -
如果某个子类需要特殊行为,可以选择重写:
java
class EmergencyCar implements Vehicle {
public void start() { ... }
@Override
public void stop() {
System.out.println("Emergency stop with sirens!");
}
}
✅ 6. Java 8+:支持 static 方法
java
interface MathUtils {
static int max(int a, int b) {
return a > b ? a : b;
}
}
-
通过接口名调用:
MathUtils.max(3, 5); -
不能被子类继承或重写。
✅ 7. Java 9+:支持 private 方法(辅助 default/static 方法)
用于封装接口内部的公共逻辑:
java
interface DataProcessor {
default void processA() {
commonLogic();
}
default void processB() {
commonLogic();
}
private void commonLogic() {
System.out.println("Shared logic here");
}
}
🔒 private 方法不能被实现类访问,仅用于接口内部复用。
✅ 8. 不能被实例化
java
Flyable f = new Flyable(); // ❌ 编译错误!
但可以通过匿名内部类 或Lambda 表达式(函数式接口)创建实例:
java
// 匿名类
Flyable bird = new Flyable() {
public void fly() { System.out.println("Flying"); }
};
// Lambda(仅适用于函数式接口)
Runnable r = () -> System.out.println("Running");
✅ 9. 可以继承其他接口(接口的"继承")
接口使用 extends 继承其他接口(可多继承):
java
interface A { void methodA(); }
interface B { void methodB(); }
interface C extends A, B { void methodC(); } // C 同时拥有 A 和 B 的方法
✅ 10. 设计语义:表达"能力"而非"身份"
-
接口表示 "can-do" (能做什么),如
Comparable、Serializable、Runnable; -
与抽象类的 "is-a"(是什么)形成互补。
🌰
Dog is an Animal→ 用抽象类;Dog can Run and Bark→ 用接口Runnable,Barkable。
✅ 11. 支持函数式编程(Java 8+)
如果接口只有一个抽象方法 ,称为 函数式接口(Functional Interface),可用 Lambda 表达式简化:
java
@FunctionalInterface
interface Calculator {
int calculate(int a, int b);
}
// 使用
Calculator add = (a, b) -> a + b;
📌 总结:接口的 11 大特点
| 特点 | 说明 |
|---|---|
1. 用 interface 定义 |
行为契约的载体 |
2. 方法默认 public abstract |
Java 8 前只能有抽象方法 |
3. 字段是 public static final |
只能定义常量 |
| 4. 支持多实现 | 一个类可实现多个接口 |
5. 支持 default 方法 |
提供默认实现,便于演进 |
6. 支持 static 方法 |
工具方法,通过接口调用 |
7. 支持 private 方法(Java 9+) |
封装内部逻辑 |
| 8. 不能实例化 | 但可通过匿名类/Lambda 创建 |
| 9. 接口可继承接口 | 支持多继承 |
| 10. 表达"能力"语义 | "can-do" 而非 "is-a" |
| 11. 支持函数式编程 | 函数式接口 + Lambda |
三、接口的使用方式
在 Java 中,接口(Interface) 是定义行为契约的核心机制。它的使用方式贯穿于类设计、多态、解耦和函数式编程等多个层面。下面从 定义 → 实现 → 使用 → 高级用法 四个维度,系统讲解 接口的使用方式。
1. 定义接口
使用 interface 关键字声明,可包含:
-
抽象方法(默认)
-
default方法(带实现) -
static方法 -
常量(
public static final字段)
java
public interface Drawable {
// 抽象方法(必须由实现类提供)
void draw();
// default 方法(可选实现)
default void resize(double factor) {
System.out.println("Resizing by " + factor);
}
// static 方法(工具方法)
static void printInfo() {
System.out.println("This is a Drawable object");
}
// 常量
String TYPE = "Vector"; // 等价于 public static final String TYPE = "Vector";
}
✅ 接口中的所有成员自动具有 public 权限(即使不写)。
2. 实现接口(implements)
类通过 implements 关键字实现一个或多个接口,并必须提供所有抽象方法的实现。
单接口实现:
java
class Circle implements Drawable {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
}
多接口实现:
java
interface Resizable { void resize(); }
interface Rotatable { void rotate(double angle); }
class Rectangle implements Drawable, Resizable, Rotatable {
public void draw() { ... }
public void resize() { ... }
public void rotate(double angle) { ... }
}
⚠️ 如果类没有实现所有抽象方法,该类必须声明为
abstract。
3. 使用接口(核心:多态)
接口最大的价值在于多态性 :用接口类型引用具体实现对象。
✅ 方式 1:接口引用指向实现类对象
java
Drawable d1 = new Circle();
Drawable d2 = new Rectangle();
d1.draw(); // 调用 Circle 的 draw()
d2.draw(); // 调用 Rectangle 的 draw()
d1.resize(1.5); // 调用 default 方法
✅ 方式 2:作为方法参数(面向接口编程)
java
public void render(Drawable obj) {
obj.draw(); // 传入任何 Drawable 实现都行
}
render(new Circle()); // ✅
render(new Triangle()); // ✅ 只要 Triangle 实现了 Drawable
💡 这是 "依赖倒置原则" 的体现:高层模块依赖抽象(接口),不依赖具体实现。
✅ 方式 3:作为返回类型
java
public Drawable createShape(String type) {
if ("circle".equals(type)) return new Circle();
if ("rect".equals(type)) return new Rectangle();
throw new IllegalArgumentException();
}
调用者只关心 Drawable 接口,不关心具体类型。
4. 高级使用方式
4.1 匿名内部类实现接口
适用于一次性使用、逻辑简单的场景:
java
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("Task running...");
}
};
new Thread(task).start();
4.2 Lambda 表达式(函数式接口)
如果接口是 函数式接口(只有一个抽象方法),可用 Lambda 简化:
java
// 函数式接口
@FunctionalInterface
interface Calculator {
int compute(int a, int b);
}
// 使用 Lambda
Calculator add = (a, b) -> a + b;
Calculator multiply = (a, b) -> a * b;
System.out.println(add.compute(3, 4)); // 7
✅ JDK 内置大量函数式接口:Runnable, Comparator, Predicate, Function 等。
4.3 接口继承(extends)
接口可以继承其他接口,支持多继承:
java
interface A { void methodA(); }
interface B { void methodB(); }
interface C extends A, B {
void methodC();
}
class MyClass implements C {
public void methodA() { ... }
public void methodB() { ... }
public void methodC() { ... }
}
4.4 调用 default 和 static 方法
-
default方法通过实例调用:javaDrawable d = new Circle(); d.resize(2.0); // 调用 default 方法
-
static方法通过接口名调用:javaDrawable.printInfo(); // 调用 static 方法
4.5 解决多重继承冲突
当两个接口有同名 default 方法时,实现类必须显式重写并指定调用哪个:
java
interface Flyable {
default void move() { System.out.println("Flying"); }
}
interface Walkable {
default void move() { System.out.println("Walking"); }
}
class Bird implements Flyable, Walkable {
@Override
public void move() {
Flyable.super.move(); // 明确调用 Flyable 的版本
}
}