1. Java继承机制深度解析
在面向对象编程中,继承是最基础也最重要的特性之一。今天我们就来深入探讨JavaSE中继承机制的第二部分内容,这是构建复杂Java程序的关键基石。继承允许我们基于现有类创建新类,实现代码复用和层次化设计,对于任何Java开发者都是必须掌握的技能。
提示:建议在阅读本文前先掌握Java类与对象的基本概念,以及简单的继承用法(extends关键字的使用)
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 继承的核心特性详解
2.1 方法重写(Override)的完整规则
方法重写是继承中最常用的特性之一,但很多初学者对其规则理解不够全面。以下是方法重写必须遵守的完整规则:
- 方法签名必须完全相同:包括方法名、参数列表和返回类型(Java5之后允许返回子类类型)
- 访问权限不能更严格:子类方法的访问修饰符权限不能低于父类方法
- 异常抛出范围不能扩大:子类方法抛出的异常不能比父类方法抛出的异常更宽泛
- 不能重写final方法:被final修饰的方法无法被重写
- 静态方法不能被重写:静态方法属于类而非对象,只能被隐藏
java复制class Animal {
public void move() {
System.out.println("动物可以移动");
}
}
class Dog extends Animal {
@Override
public void move() {
System.out.println("狗可以跑和走");
}
}
2.2 super关键字的四种用法
super关键字在继承中有多种重要用途,很多开发者只了解其中部分用法:
-
调用父类构造方法:必须在子类构造方法的第一行
java复制public ChildClass() { super(); // 调用父类无参构造 } -
访问父类成员变量:当子类有同名变量时
java复制super.variableName; -
调用父类普通方法:当子类重写了父类方法时
java复制super.methodName(); -
在匿名内部类中:访问外部类的父类成员
注意:super不能用于静态上下文(static方法或static块),因为super与对象实例相关
3. 继承中的构造方法机制
3.1 构造方法调用链
Java中构造方法的调用遵循严格的链式规则:
- 子类构造方法必须直接或间接调用父类构造方法
- 如果没有显式调用,编译器会自动插入super()
- 如果父类没有无参构造,必须显式调用其他构造方法
- 构造方法调用必须是第一条语句
java复制class Parent {
Parent(int x) {
System.out.println("Parent constructor");
}
}
class Child extends Parent {
Child() {
super(10); // 必须显式调用
System.out.println("Child constructor");
}
}
3.2 构造方法执行顺序
理解对象初始化时的执行顺序非常重要:
- 父类静态代码块和静态变量初始化
- 子类静态代码块和静态变量初始化
- 父类实例变量初始化和普通代码块
- 父类构造方法
- 子类实例变量初始化和普通代码块
- 子类构造方法
4. 继承的高级特性
4.1 类型转换与instanceof
在继承体系中,类型转换是常见操作:
-
向上转型(Upcasting):自动进行,子类转父类
java复制Animal animal = new Dog(); // 自动转换 -
向下转型(Downcasting):需要强制转换,可能抛出ClassCastException
java复制Dog dog = (Dog) animal; // 需要显式转换 -
instanceof操作符:安全的类型检查方式
java复制if (animal instanceof Dog) { Dog dog = (Dog) animal; }
4.2 继承与组合的选择
继承不是代码复用的唯一方式,组合(Composition)也是重要选择:
| 特性 | 继承 | 组合 |
|---|---|---|
| 关系 | "is-a"关系 | "has-a"关系 |
| 耦合度 | 高耦合 | 低耦合 |
| 灵活性 | 编译时确定 | 运行时可以改变 |
| 多态性 | 支持 | 需要通过接口实现 |
| 代码复用 | 直接复用父类实现 | 需要显式调用包含对象的方法 |
设计原则:优先使用组合而非继承,除非确实需要"is-a"关系
5. 继承中的常见问题与解决方案
5.1 菱形继承问题
Java不支持多继承,但通过接口可以实现类似效果:
java复制interface A {
default void show() {
System.out.println("A");
}
}
interface B extends A {
default void show() {
System.out.println("B");
}
}
interface C extends A {
default void show() {
System.out.println("C");
}
}
class D implements B, C {
// 必须重写show方法,否则编译错误
@Override
public void show() {
B.super.show(); // 明确指定调用哪个接口的默认方法
}
}
5.2 继承破坏封装性
不当的继承可能破坏封装性,解决方法:
- 尽量将成员变量声明为private
- 使用final防止方法被重写
- 考虑使用组合替代继承
- 遵循"要么为继承设计并提供文档,要么禁止继承"原则
6. 继承的最佳实践
6.1 何时使用继承
适合使用继承的场景:
- 真正的"is-a"关系(如Dog是Animal)
- 需要多态特性的场合
- 框架设计中需要扩展点的情况
- 父类是抽象类或专门设计用于继承
6.2 设计可继承类的要点
如果设计可能被继承的类,应该:
- 文档清楚地说明可重写方法的行为
- 尽量少提供可重写的方法
- 构造方法不要调用可重写的方法
- 考虑将类或方法声明为final
- 使用protected方法而非protected字段
java复制/**
* 设计用于继承的抽象类示例
*/
public abstract class AbstractList<E> {
/**
* 添加元素到列表末尾
* @param e 要添加的元素
* @return 总是返回true
* @throws NullPointerException 如果元素为null
*/
public boolean add(E e) {
add(size(), e);
return true;
}
// 抽象方法需要子类实现
public abstract void add(int index, E element);
public abstract int size();
}
7. Java继承的特殊情况处理
7.1 继承与序列化
当涉及Serializable接口时,继承需要特别注意:
- 父类不实现Serializable时,子类可以单独实现
- 父类字段不会被自动序列化,需要在子类中手动处理
- 反序列化时父类构造方法不会被调用
java复制class Parent {
protected int parentField;
public Parent() {
parentField = 10; // 反序列化时不会执行
}
}
class Child extends Parent implements Serializable {
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeInt(parentField); // 手动序列化父类字段
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
parentField = in.readInt(); // 手动反序列化父类字段
}
}
7.2 继承与克隆
实现Cloneable接口时的继承问题:
- 深拷贝与浅拷贝的选择
- 父类字段的克隆处理
- CloneNotSupportedException的处理
java复制class Parent implements Cloneable {
protected int[] data;
@Override
public Parent clone() throws CloneNotSupportedException {
Parent cloned = (Parent) super.clone();
cloned.data = data.clone(); // 数组的深拷贝
return cloned;
}
}
class Child extends Parent {
private String name;
@Override
public Child clone() throws CloneNotSupportedException {
Child cloned = (Child) super.clone(); // 调用父类clone方法
cloned.name = new String(name); // String的深拷贝
return cloned;
}
}
8. 继承在Java标准库中的应用实例
8.1 集合框架中的继承
Java集合框架是继承应用的经典案例:
java复制// 简化版的集合框架继承关系
public abstract class AbstractCollection<E> implements Collection<E> {
// 实现部分通用方法
public abstract Iterator<E> iterator();
public abstract int size();
public boolean isEmpty() {
return size() == 0;
}
// 其他通用实现...
}
public abstract class AbstractList<E> extends AbstractCollection<E>
implements List<E> {
// 添加基于索引的操作
public abstract E get(int index);
public Iterator<E> iterator() {
return new Itr();
}
// 其他实现...
}
public class ArrayList<E> extends AbstractList<E>
implements RandomAccess, Cloneable, Serializable {
// 具体实现...
}
8.2 IO流中的继承
Java IO包是另一个继承的典型应用:
java复制// 输入流的部分继承体系
public abstract class InputStream implements Closeable {
public abstract int read() throws IOException;
// 其他方法...
}
public class FileInputStream extends InputStream {
// 实现文件读取的具体逻辑
public int read() throws IOException {
// 实际读取文件的实现
}
}
public class BufferedInputStream extends FilterInputStream {
// 添加缓冲功能
public int read() throws IOException {
// 缓冲读取的实现
}
}
9. Java8+中继承的新特性
9.1 默认方法带来的变化
Java8引入的默认方法改变了继承的某些行为:
- 接口可以包含具体实现
- 类优先原则:类中的方法优先于接口默认方法
- 冲突解决方法:必须显式指定使用哪个接口的默认方法
java复制interface Flyable {
default void fly() {
System.out.println("Flying");
}
}
interface Swimmable {
default void swim() {
System.out.println("Swimming");
}
}
class Duck implements Flyable, Swimmable {
// 可以同时继承两个接口的默认方法
// 如果有冲突的方法,必须重写
}
9.2 私有接口方法
Java9开始,接口可以包含私有方法:
java复制interface Calculator {
default int add(int a, int b) {
return a + b;
}
default int subtract(int a, int b) {
return a - b;
}
private void log(String operation) {
System.out.println("Performing: " + operation);
}
}
10. 继承的性能考量
10.1 方法调用的性能影响
继承层次过深可能影响性能:
- 方法调用需要查找方法表
- 虚方法调用比静态方法调用稍慢
- JIT优化可以缓解部分性能问题
10.2 内存占用分析
继承对内存的影响:
- 每个对象都包含其所有父类的字段
- 方法表指针占用固定空间
- 对象头开销与继承层次无关
实际项目中,继承带来的性能影响通常可以忽略,设计合理性比微小的性能差异更重要
11. 继承与设计模式
11.1 模板方法模式
模板方法模式是继承的经典应用:
java复制abstract class Game {
// 模板方法,定义算法骨架
public final void play() {
initialize();
startPlay();
endPlay();
}
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
}
class Cricket extends Game {
void initialize() {
System.out.println("Cricket Game Initialized");
}
void startPlay() {
System.out.println("Cricket Game Started");
}
void endPlay() {
System.out.println("Cricket Game Finished");
}
}
11.2 装饰器模式
装饰器模式使用继承扩展功能:
java复制// 组件接口
interface Coffee {
double getCost();
String getDescription();
}
// 具体组件
class SimpleCoffee implements Coffee {
public double getCost() {
return 1;
}
public String getDescription() {
return "Simple coffee";
}
}
// 装饰器基类
abstract class CoffeeDecorator implements Coffee {
protected final Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
public double getCost() {
return decoratedCoffee.getCost();
}
public String getDescription() {
return decoratedCoffee.getDescription();
}
}
// 具体装饰器
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
public double getCost() {
return super.getCost() + 0.5;
}
public String getDescription() {
return super.getDescription() + ", with milk";
}
}
12. 继承的替代方案
12.1 组合优于继承
很多情况下组合是更好的选择:
java复制// 使用继承
class LoggingArrayList<E> extends ArrayList<E> {
@Override
public boolean add(E e) {
System.out.println("Adding element: " + e);
return super.add(e);
}
}
// 使用组合
class LoggingList<E> implements List<E> {
private final List<E> list;
public LoggingList(List<E> list) {
this.list = list;
}
public boolean add(E e) {
System.out.println("Adding element: " + e);
return list.add(e);
}
// 委托其他方法给内部list
public int size() {
return list.size();
}
// ...其他方法实现
}
12.2 策略模式替代继承
使用策略模式避免继承层次过深:
java复制interface BillingStrategy {
double getActPrice(double rawPrice);
}
class NormalStrategy implements BillingStrategy {
public double getActPrice(double rawPrice) {
return rawPrice;
}
}
class HappyHourStrategy implements BillingStrategy {
public double getActPrice(double rawPrice) {
return rawPrice * 0.5;
}
}
class Customer {
private BillingStrategy strategy;
public Customer(BillingStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(BillingStrategy strategy) {
this.strategy = strategy;
}
public void printBill(List<Double> prices) {
double sum = 0;
for (Double price : prices) {
sum += strategy.getActPrice(price);
}
System.out.println("Total due: " + sum);
}
}
13. 继承的测试考量
13.1 测试继承层次
测试继承体系时的策略:
- 为父类编写全面的测试用例
- 子类测试应包含:
- 继承方法的测试
- 重写方法的测试
- 新增方法的测试
- 使用@Before方法初始化测试环境
java复制class StackTest {
protected Stack<Integer> stack;
@Before
public void setUp() {
stack = new Stack<>();
}
@Test
public void testEmpty() {
assertTrue(stack.isEmpty());
}
// 其他测试方法...
}
class SpecializedStackTest extends StackTest {
@Before
@Override
public void setUp() {
stack = new SpecializedStack<>();
}
@Test
public void testSpecialFeature() {
// 测试子类特有功能
}
}
13.2 模拟与继承
使用Mock框架测试继承代码:
java复制class Parent {
public String getName() {
return "Parent";
}
}
class Child extends Parent {
public String getFullName() {
return getName() + "'s Child";
}
}
@Test
public void testChildGetFullName() {
Child child = Mockito.spy(new Child());
Mockito.when(child.getName()).thenReturn("MockedParent");
assertEquals("MockedParent's Child", child.getFullName());
}
14. 实际项目中的继承经验
14.1 继承的适用场景
根据多年项目经验,以下场景适合使用继承:
- 框架设计中的扩展点
- 真正存在"is-a"关系的领域模型
- 需要多态行为的组件
- 少量、稳定的继承层次
14.2 继承的常见陷阱
实际项目中常见的继承问题:
- 脆弱的基类问题:父类修改可能破坏子类
- 过度继承:继承层次过深难以维护
- 错误的重写:违反里氏替换原则
- 构造方法问题:父类构造方法调用可重写方法
经验法则:当考虑使用继承时,先思考是否可以用组合+接口替代
15. Java继承的未来发展
15.1 记录类(Record)与继承
Java14引入的Record类对继承的影响:
- Record类隐式final,不能被继承
- 可以实现接口
- 简洁的数据载体定义方式
java复制record Point(int x, int y) implements Drawable {
// 自动生成构造方法、equals、hashCode等
public void draw() {
System.out.printf("Drawing point at (%d, %d)%n", x, y);
}
}
15.2 密封类(Sealed Class)
Java17引入的密封类提供了更精确的继承控制:
java复制public sealed class Shape
permits Circle, Square, Rectangle {
// 基类定义
}
public final class Circle extends Shape {
// 只能被Shape允许的类继承
}
public non-sealed class Square extends Shape {
// 可以被任何类继承
}
public sealed class Rectangle extends Shape
permits TransparentRectangle {
// 继续限制继承
}
16. 继承与模块化
16.1 模块系统中的访问控制
Java模块系统对继承的影响:
- 模块导出控制影响类的可继承性
- protected成员在不同模块中的可见性
- 反射访问限制
16.2 服务加载机制
模块化下的服务加载是继承的替代方案:
java复制module com.example.app {
requires com.example.service;
uses com.example.service.MyService;
}
module com.example.provider {
requires com.example.service;
provides com.example.service.MyService
with com.example.provider.MyServiceImpl;
}
17. 继承与注解处理
17.1 注解的继承规则
注解在继承中的行为:
- @Inherited元注解控制注解是否被继承
- 方法注解不会被继承
- 接口上的注解不会被实现类继承
java复制@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {}
@MyAnnotation
class Parent {}
class Child extends Parent {} // 会继承MyAnnotation
17.2 注解处理器中的继承处理
编写注解处理器时需要考虑继承:
java复制@SupportedAnnotationTypes("com.example.MyAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_11)
public class MyProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
for (TypeElement annotation : annotations) {
for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {
// 处理被注解的元素,包括继承得到的注解
}
}
return true;
}
}
18. 继承与泛型
18.1 泛型类的继承规则
泛型类继承的特殊考虑:
- 泛型参数不参与继承关系
- 通配符与继承的交互
- 类型擦除的影响
java复制class Box<T> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
class StringBox extends Box<String> {
@Override
public void set(String value) {
super.set(value.toLowerCase());
}
}
18.2 泛型方法的重写
泛型方法重写的特殊规则:
- 类型参数可以不同
- 擦除后签名必须兼容
- 桥方法的影响
java复制class Parent {
public <T> void process(T item) {
System.out.println("Processing in Parent: " + item);
}
}
class Child extends Parent {
@Override
public <T extends Number> void process(T item) {
System.out.println("Processing in Child: " + item);
}
}
19. 继承与反射
19.1 反射API中的继承支持
反射API提供了丰富的继承相关操作:
- Class.getSuperclass()
- Class.getInterfaces()
- Class.isAssignableFrom()
- Method.getDeclaringClass()
java复制void printInheritance(Class<?> clazz) {
System.out.println("Class: " + clazz.getName());
Class<?> superclass = clazz.getSuperclass();
if (superclass != null) {
System.out.println("Extends: " + superclass.getName());
}
Class<?>[] interfaces = clazz.getInterfaces();
if (interfaces.length > 0) {
System.out.println("Implements: " +
Arrays.stream(interfaces)
.map(Class::getName)
.collect(Collectors.joining(", ")));
}
}
19.2 反射访问控制
反射可以突破部分访问限制:
- setAccessible(true)可以访问private成员
- 模块系统增加了新的限制
- 反射创建对象不会调用构造方法
java复制class Secret {
private void hidden() {
System.out.println("Secret method");
}
}
// 通过反射调用private方法
Method hidden = Secret.class.getDeclaredMethod("hidden");
hidden.setAccessible(true);
hidden.invoke(new Secret());
20. 继承与Java新特性整合
20.1 继承与模式匹配
Java16引入的模式匹配简化了继承检查:
java复制// 传统方式
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.length());
}
// 模式匹配方式
if (obj instanceof String s) {
System.out.println(s.length());
}
20.2 继承与文本块
Java15引入的文本块在继承中的使用:
java复制abstract class Template {
public abstract String generate();
}
class HtmlTemplate extends Template {
@Override
public String generate() {
return """
<html>
<body>
<h1>Hello World</h1>
</body>
</html>
""";
}
}
21. 继承的性能优化技巧
21.1 减少继承层次
过深的继承层次影响性能:
- 方法查找需要遍历继承链
- 对象创建需要初始化所有父类
- 内联优化可能受限
21.2 使用final优化
适当使用final提升性能:
- final类:禁止继承,JVM可以做更多优化
- final方法:禁止重写,便于内联
- final字段:保证不可变性,线程安全
java复制public final class UtilityClass {
private UtilityClass() {} // 防止实例化
public static final int MAX_SIZE = 1024;
public static final void process() {
// 工具方法
}
}
22. 继承与多线程
22.1 线程安全与继承
继承对线程安全的影响:
- 父类的线程安全性影响子类
- 重写方法可能破坏线程安全
- 同步策略可能被改变
java复制class Counter {
protected int count;
public synchronized void increment() {
count++;
}
}
class BrokenCounter extends Counter {
@Override
public void increment() {
count++; // 破坏了同步
}
}
22.2 继承与并发工具
在并发环境下使用继承的注意事项:
- 避免在同步块中调用可重写方法
- 使用final或private方法保证原子性
- 考虑使用并发集合而非同步方法
java复制class SafeList<E> {
private final List<E> list = new CopyOnWriteArrayList<>();
public final void add(E element) {
list.add(element);
}
public final int size() {
return list.size();
}
}
23. 继承与函数式编程
23.1 Lambda与继承
函数式接口与继承的交互:
- 函数式接口可以继承其他接口
- 只能有一个抽象方法
- 默认方法不影响函数式特性
java复制@FunctionalInterface
interface Processor {
void process();
}
@FunctionalInterface
interface AdvancedProcessor extends Processor {
default void log() {
System.out.println("Processing...");
}
}
23.2 方法引用与继承
方法引用在继承中的行为:
- 可以引用父类方法
- 可以引用重写的方法
- 静态方法引用不受影响
java复制class Parent {
static void staticMethod() {}
void instanceMethod() {}
}
class Child extends Parent {
void test() {
Runnable r1 = Parent::staticMethod;
Runnable r2 = super::instanceMethod;
}
}
24. 继承的设计模式应用进阶
24.1 桥接模式中的继承
桥接模式使用继承分离抽象与实现:
java复制// 实现接口
interface DrawingAPI {
void drawCircle(double x, double y, double radius);
}
// 具体实现
class DrawingAPI1 implements DrawingAPI {
public void drawCircle(double x, double y, double radius) {
System.out.printf("API1.circle at %.1f:%.1f radius %.1f\n", x, y, radius);
}
}
// 抽象类
abstract class Shape {
protected DrawingAPI drawingAPI;
protected Shape(DrawingAPI drawingAPI) {
this.drawingAPI = drawingAPI;
}
public abstract void draw();
}
// 扩展抽象
class CircleShape extends Shape {
private double x, y, radius;
public CircleShape(double x, double y, double radius, DrawingAPI drawingAPI) {
super(drawingAPI);
this.x = x;
this.y = y;
this.radius = radius;
}
public void draw() {
drawingAPI.drawCircle(x, y, radius);
}
}
24.2 访问者模式中的继承
访问者模式利用双重分派处理继承:
java复制interface Visitor {
void visit(ElementA element);
void visit(ElementB element);
}
interface Element {
void accept(Visitor visitor);
}
class ElementA implements Element {
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
class ElementB implements Element {
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
class ConcreteVisitor implements Visitor {
public void visit(ElementA element) {
System.out.println("Processing ElementA");
}
public void visit(ElementB element) {
System.out.println("Processing ElementB");
}
}
25. 继承的调试技巧
25.1 调试继承问题的方法
调试继承相关问题的实用技巧:
- 使用IDE的继承层次视图
- 检查方法重写的@Override注解
- 使用断点观察super调用
- 检查构造方法执行顺序
25.2 常见继承问题诊断
典型继承问题及其诊断:
- NullPointerException:检查父类构造方法是否完整执行
- ClassCastException:检查instanceof使用是否正确
- 方法未按预期执行:检查是否意外重写了方法
- 字段值不正确:检查父类和子类字段命名冲突
java复制class DebugExample {
public static void main(String[] args) {
Child child = new Child();
child.method(); // 在此处设置断点调试
}
}
class Parent {
protected int value = 10;
public void method() {
System.out.println("Parent: " + value);
}
}
class Child extends Parent {
protected int value = 20; // 遮蔽了父类的value
@Override
public void method() {
System.out.println("Child: " + value);
System.out.println("Parent's value: " + super.value);
}
}
26. 继承与代码质量工具
26.1 静态分析工具对继承的检查
常用工具对继承的检查规则:
-
SonarQube:
- 检查继承深度
- 检查不必要的方法重写
- 检查脆弱的基类问题
-
Checkstyle:
- 检查@Override注解使用
- 检查设计扩展性
-
PMD:
- 检查空的finalize方法
- 检查clone方法的实现
26.2 继承相关的代码度量
评估继承设计的常用指标:
- 继承深度(DIT):类在继承树中的深度
- 子类数(NOC):直接子类的数量
- 方法重写比率:重写方法占可重写方法的比例
- 耦合度:父类与子类之间的耦合程度
健康指标参考:DIT≤5,NOC≤10,方法重写比率30%-70%
27. 继承在框架设计中的应用
27.1 Spring框架中的继承应用
Spring中继承的典型用法:
- 配置继承:@Configuration类可以继承
- Bean定义继承:parent属性
- AOP代理:基于继承的代理方式
- 事件处理:ApplicationEvent继承体系
java复制@Configuration
class ParentConfig {
@Bean
public DataSource dataSource() {
return new BasicDataSource();
}
}
@Configuration
class ChildConfig extends ParentConfig {
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
27.2 Hibernate中的继承策略
Hibernate提供的三种继承映射策略:
- 单表继承(SINGLE_TABLE):所有类共用一张表
- 连接表继承(JOINED):每个类对应自己的表
- 表每类继承(TABLE_PER_CLASS):每个具体类对应完整表
java复制@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Person {
@Id @GeneratedValue
private Long id;
private String name;
}
@Entity
public class Employee extends Person {
private String department;
}
28. 继承与Java新版本特性
28.1 记录类(Record)的继承限制
Record类的继承特性:
- 隐式final,不能被子类化
- 可以显式实现接口
- 可以定义静态方法和静态字段
- 可以定义实例方法
java复制record Point(int x, int y) implements Comparable<Point> {
// 添加自定义方法
public double distance() {
return Math.hypot(x, y);
}
// 实现接口
public int compareTo(Point other) {
return Double.compare(this.distance(), other.distance());
}
}
28.2 密封类(Sealed Class)的精细控制
密封类提供的继承控制:
- 明确指定哪些类可以继承
- 支持permits子句列出允许的子类
- 子类必须为final、sealed或non-sealed
java复制public sealed interface Expr
permits ConstantExpr, PlusExpr, TimesExpr, NegExpr {
// 接口定义
}
public final class ConstantExpr implements Expr {
// 实现
}
public final class PlusExpr implements Expr {
// 实现
}
public sealed class TimesExpr implements Expr
permits SpecialTimesExpr {
// 实现
}
public final class SpecialTimesExpr extends TimesExpr {
// 实现
}
29. 继承与Java模块系统
29.1 模块对继承的影响
模块系统引入的继承相关变化:
- 模块导出控制影响类的可继承性
- 反射访问限制影响子类化能力
- 服务加载机制提供替代方案
29.2 跨模块继承的最佳实践
跨模块使用继承的建议:
- 明确导出需要被继承的包
- 使用provides/uses机制替代部分继承场景
- 考虑使用接口而非具体类作为扩展点
- 文档清楚地说明继承约束
java复制module com.example.framework {
exports com.example.framework.api;
exports com.example.framework.spi;
}
module com.example.plugin {
requires com.example.framework;
provides com.example.framework.spi.MyService
with com.example.plugin.MyServiceImpl;
}
30. 继承的替代方案深入探讨
30.1 组合与委托模式
组合的典型实现方式:
java复制interface Engine {
void start();
void stop();
}
class Car {
private final Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public void start() {
engine.start();
}
public void stop() {
engine.stop();
}
}
30.2 策略模式替代继承
策略模式的灵活实现:
java复制interface DiscountStrategy {
double applyDiscount(double originalPrice);
}
class NoDiscount implements DiscountStrategy {
public double applyDiscount(double price) {
return price;
}
}
class PercentageDiscount implements DiscountStrategy {
private final double percentage;
public PercentageDiscount(double percentage) {
this.percentage = percentage;
}
public double applyDiscount(double price) {
return price * (1 - percentage);
}
}
class Order {
private DiscountStrategy strategy = new NoDiscount();
public void setDiscountStrategy(DiscountStrategy strategy) {
this.strategy = strategy;
}
public double calculateTotal(double subtotal) {
return strategy.applyDiscount(subtotal);
}
}
31. 继承与Java持久化
31.1 JPA继承策略
JPA提供的继承映射策略:
- SINGLE_TABLE:默认策略,所有类存储在一张表
- JOINED:每个实体类对应自己的表
- TABLE_PER_CLASS:每个具体类对应完整表
java复制@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Account {
@Id @GeneratedValue
private Long id;
private String owner;
}
@Entity
public class CheckingAccount extends Account {
private BigDecimal overdraftLimit;
}
@Entity
public class SavingsAccount extends Account {
private BigDecimal interestRate;
}
31.2 继承与DAO设计
DAO设计中的继承应用:
java复制public interface GenericDao<T, ID> {
T findById(ID id);
List<T> findAll();
T save(T entity);
void delete(T entity);
}
public abstract class GenericJpaDao<T, ID> implements GenericDao<T, ID> {
protected Class<T> entityClass;
protected EntityManager entityManager;
public Generic
