子类与继承
基本概念
- 继承:面向对象编程的重要特性,允许一个类(子类)继承另一个类(父类)的属性和方法。
- 子类 (Subclass/Derived class):通过
extends关键字继承父类的类。 - 父类 (Superclass/Base class):被子类继承的类。
子类的定义
- 使用
extends关键字:public class 子类名 extends 父类名 { // 子类特有的属性和方法 } - Java 中单继承:一个子类只能有一个直接父类,但可以通过链式继承形成继承层次。
- 所有类默认继承自 Object类(如果未显式指定父类)。
继承的特性
- 子类继承父类的所有非私有成员(属性、方法),但受访问控制修饰符影响。
- 不能继承的成员:
private修饰的属性和方法(但可以通过getter/setter间接访问)。- 构造方法(不能继承,但子类可以调用父类构造方法)。
- 静态方法(不属于实例,但可以通过子类名调用)。
- 子类可以添加新的属性和方法,也可以重写(Override) 父类的方法。
方法重写 (Override)
- 子类重新定义父类中已有的方法,实现不同的行为。
- 重写规则:
- 方法名、参数列表必须相同。
- 返回类型需相同或为父类返回类型的子类型(协变返回类型)。
- 访问权限不能更严格(例如,父类
protected,子类不能改为private)。 - 不能抛出更宽泛的异常(可以抛出更具体的异常或不抛出)。
- 使用
@Override注解显式标明重写,帮助编译器检查错误。
示例:
class Animal {
public void sound() {
System.out.println("Animal makes sound");
}
}
class Cat extends Animal {
@Override
public void sound() {
System.out.println("Meow");
}
}super 关键字
super:引用父类的对象。- 常见用途:
- 访问父类的属性:
super.属性名 - 调用父类的方法:
super.方法名() - 调用父类的构造方法:
super(参数列表)(必须在子类构造方法的第一行)
- 访问父类的属性:
- 如果子类构造方法中未显式调用父类构造方法,编译器会自动插入
super()调用父类的无参构造方法。如果父类没有无参构造方法,子类构造方法必须显式调用父类的有参构造方法。 - 拓展:不使用
super调用父类方法,方法链:创建无名对象直接调用new 类名.方法名
构造方法在继承中的行为
- 子类构造方法会隐式或显式调用父类构造方法。
- 执行顺序:
- 调用父类构造方法(初始化父类部分)。
- 初始化子类的实例变量。
- 执行子类构造方法中的代码。
抽象类与抽象方法
抽象类用于提炼父类模板,抽象方法把不确定行为留给子类实现。详细见:抽象类。
接口与实现
- 接口(
interface)是一种更抽象的“继承”形式(通过implements实现)。 - 一个类可以实现多个接口(弥补 Java 单继承的限制)。
- 接口中的方法默认是
public abstract,属性默认是public static final。 详细见:接口与实现。
多态 (Polymorphism)
- 向上转型:子类对象赋值给父类引用(例如
Animal a = new Cat();)。 - 通过父类引用调用方法时,实际执行的是子类重写的方法(动态绑定)。
- 编译时类型决定可调用的成员,运行时类型决定实际行为。
详细见:多态。
最佳实践
- 优先使用组合而非继承:组合(HAS-A)比继承(IS-A)更灵活,降低耦合。
- 继承用于 “is-a” 关系,如
Dog is an Animal。 - 避免过度继承层级过深,建议不超过 3-4 层。
- 如果父类方法不确定子类是否需要重写,可考虑使用
abstract方法或设为默认空实现。
instanceof运算符
用来检查对象是否是特定类或其父类的实例。语法为 对象 instanceof 类名,返回布尔值。在继承中常用于多态场景的类型判断,例如:
Animal animal = new Cat();
if (animal instanceof Cat) {
System.out.println("这是一只猫");
} 注意:若对象为null,instanceof会直接返回false。
final关键字
final 可以限制类被继承、方法被重写、变量被重新赋值。详细见:final关键字。
上转型变量
- 上转型变量:子类对象赋值给父类引用。
- 使用
System.out.println(上转型变量)时,实际调用的是对象的toString()方法,体现动态绑定(多态)。 - 具体输出取决于对象实际类型所属类是否重写了
toString():- 若已重写:输出子类自定义的字符串。
- 若未重写:默认输出
类名@哈希码(继承自 Object类)。
- 本质:编译时引用类型决定可调用的方法,运行时对象类型决定实际执行的方法。
- 可访问部分
- 唯有父类变量、方法以及子类重写的方法
abstract类和abstract()方法
抽象类的理解:抽象出重要的行为标准,减少在细节上浪费的时间,从而设计出易维护、易拓展的程序。详细见:抽象类。
开-闭原则
定义:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。 核心思想**:通过抽象和继承实现功能扩展,而不用修改原有代码。 典型实现方式:
- 使用抽象类/接口定义稳定部分
- 通过子类/实现类进行扩展