继承的基本概念与语法
在Java中,继承是面向对象编程(OOP)的核心特性之一,它允许一个类(子类)获取另一个类(父类或超类)的属性和方法,通过继承,子类可以复用父类的代码,减少重复开发,同时扩展或修改父类的功能,形成类的层次结构,Java仅支持单继承(即一个子类只能有一个直接父类),但通过接口可以实现多重继承的效果。
1 继承的语法
使用extends关键字实现继承,基本语法如下:
class 子类 extends 父类 {
// 子类特有的属性和方法
}
定义一个Animal父类和Dog子类:
class Animal {
String name;
void eat() {
System.out.println("动物进食");
}
}
class Dog extends Animal {
void bark() {
System.out.println("狗叫");
}
}
在上述代码中,Dog类继承了Animal类,因此可以直接使用name属性和eat()方法,同时新增了bark()方法。
2 继承的访问权限
继承时需注意访问修饰符的影响:
public和protected成员:子类可直接访问。- 默认(包私有)成员:子类与父类在同一包下时可访问。
private成员:子类无法直接访问,需通过父类的公共方法间接调用。
继承的核心特性
1 方法重写(Override)
子类可以重写父类的实例方法,即提供与父类方法相同的方法名、参数列表和返回类型,但实现逻辑不同,重写时需遵循“里氏替换原则”(LSP),即子类方法不能比父类方法更严格地限制访问权限,且返回类型必须是父类返回类型的子类型。
重写Animal类的eat()方法:
class Dog extends Animal {
@Override // 注解:检查是否正确重写
void eat() {
System.out.println("狗吃骨头");
}
}
调用时,实际执行的是子类的方法:
Dog dog = new Dog(); dog.eat(); // 输出:狗吃骨头
2 super关键字
super用于引用父类的成员(方法、属性或构造器),常见场景包括:
- 调用父类的被重写方法:
super.eat()。 - 访问父类的隐藏属性:
super.name。 - 调用父类的构造器:必须在子类构造器的第一行使用
super()或this()。
通过super调用父类构造器:
class Dog extends Animal {
Dog() {
super(); // 调用父类无参构造器(默认存在)
System.out.println("Dog构造器");
}
}
3 继承中的构造器执行顺序
创建子类对象时,构造器的执行顺序遵循“先父后子”原则:
- 父类构造器(默认调用
super(),可显式指定)。 - 子类构造器。
class Animal {
Animal() {
System.out.println("Animal构造器");
}
}
class Dog extends Animal {
Dog() {
System.out.println("Dog构造器");
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
// 输出:
// Animal构造器
// Dog构造器
}
}
继承的类型与限制
1 继承的类型
- 单继承:一个子类只能有一个直接父类,如
class B extends A。 - 多层继承:子类可继承父类,父类再继承祖父类,形成继承链,如
class C extends B extends A(Java语法中需连续声明extends)。 - 接口继承:接口通过
extends关键字继承其他接口,支持多重继承,如interface C extends A, B。
2 继承的限制
- 构造器不可继承:子类不会继承父类的构造器,但可通过
super()调用。 - 静态成员与私有成员:静态方法(
static)和私有成员(private)不能被继承,但可通过父类类名或公共方法间接访问。 - final修饰的类或方法:被
final修饰的类不能被继承,被final修饰的方法不能被重写。
继承的设计原则与最佳实践
1 里氏替换原则(LSP)
子类必须能够替换父类而不影响程序的正确性,这意味着子类不能修改父类的预期行为,只能扩展或优化,父类方法返回List时,子类重写方法不应返回更具体的类型(如ArrayList),除非明确允许。
2 组合优于继承
过度继承会导致类层次结构复杂,耦合度增高,在设计中,优先考虑组合(has-a关系)而非继承(is-a关系)。Car类包含Engine类(组合)比让Car继承Engine(继承)更灵活。
3 合理使用抽象类与接口
- 抽象类:当多个子类需要共享代码且存在“is-a”关系时,使用抽象类(
abstract class)定义通用属性和方法。 - 接口:当需要定义规范(行为)且类可能不属于同一继承体系时,使用接口(
interface),如Runnable接口。
继承的实际应用场景
继承常用于以下场景:
- 代码复用:将公共属性和方法提取到父类,子类直接继承,减少冗余代码,定义
Shape父类,Circle和Rectangle子类继承并实现各自的面积计算方法。 - 多态实现:通过继承和方法重写,结合父类引用指向子类对象,实现运行时动态绑定。
Animal animal = new Dog(); // 父类引用指向子类对象 animal.eat(); // 调用子类的重写方法
- 框架与库设计:Java集合框架(如
ArrayList继承AbstractList)、Spring框架等都大量使用继承,提供可扩展的基类或接口。
继承是Java实现代码复用和扩展的重要机制,通过extends关键字建立父子类关系,支持方法重写、super调用等特性,合理使用继承可以简化代码设计,但需避免过度继承导致的耦合问题,在实际开发中,应结合“里氏替换原则”和“组合优于继承”的思想,选择合适的设计模式,以构建灵活、可维护的代码结构。















