什么是继承?
继承(Inheritance)是面向对象编程的三大特性之一,它是代码复用的一种重要手段。通过继承,子类可以自动拥有父类的属性和方法,同时还可以扩展自己的特有功能。
生活比喻
继承就像生物学的分类体系。"动物"是一个父类,它有"吃"、"睡"等通用行为。"狗"继承了"动物",除了有"吃"、"睡"外,还有"汪汪叫"这个狗特有的行为。"猫"也继承了"动物",有"喵喵叫"这个猫特有的行为。狗和猫 reuse(复用)了动物的基本特性。
继承的类层次结构:
┌─────────┐
│ Object │ ← 所有类的最终父类
└────┬────┘
│
┌────┴────┐
│ Animal │ ← 父类(共有属性和方法)
│ name │
│ age │
│ eat() │
│ sleep() │
└────┬────┘
│
┌─────────────┴─────────────┐
│ │
┌────┴────┐ ┌────┴────┐
│ Dog │ │ Cat │
│ breed │ │ indoor │
│ bark() │ │ meow() │
└────────┘ └────────┘
子类扩展父类 子类扩展父类
继承的基本语法
Java中使用extends关键字实现继承:
// 父类
class Animal {
protected String name;
protected int age;
public void eat() {
System.out.println(name + "正在吃东西");
}
public void sleep() {
System.out.println(name + "正在睡觉");
}
}
// 子类 - 使用extends继承父类
class Dog extends Animal {
private String breed;
// 子类自动拥有name、age属性和eat()、sleep()方法
public void bark() {
System.out.println(name + "汪汪叫"); // 可以访问父类的protected属性
}
}
// 使用
Dog dog = new Dog();
dog.name = "旺财"; // 继承自父类
dog.eat(); // 继承自父类
dog.bark(); // 子类自己的方法
super关键字详解
super关键字用于引用父类,它有三种用法:
| 用法 | 说明 | 示例 |
|---|---|---|
| super(...) | 调用父类构造方法 | super(name, age); |
| super.方法名() | 调用父类的方法 | super.eat(); |
| super.属性名 | 访问父类的属性 | super.name; |
class Dog extends Animal {
private String breed;
public Dog(String name, int age, String breed) {
// 调用父类构造方法初始化从父类继承的属性
super(name, age);
// 自己特有的属性自己初始化
this.breed = breed;
}
@Override
public void eat() {
// 先调用父类的eat方法
super.eat();
// 再扩展子类的特有行为
System.out.println(name + "正在吃狗粮");
}
public void showInfo() {
// 调用父类的方法
super.showInfo();
// 扩展子类的方法
System.out.println("品种: " + breed);
}
}
方法重写(Override)
子类可以重写(覆盖)父类的方法,提供自己的实现:
理解重写
方法重写就像"覆写菜单"。餐厅(父类)有一个标准菜品"红烧肉"。分店(子类)继承了这个菜品,但分店的厨师有自己独特的做法,于是"重写"了这个菜品的配方。虽然都叫"红烧肉",但吃起来味道不同。
class Animal {
public void eat() {
System.out.println("动物在吃东西");
}
}
class Cat extends Animal {
@Override // 重写注解,编译器会检查重写是否正确
public void eat() {
System.out.println("猫在吃猫粮");
}
}
// 重写规则:
// 1. 方法名、参数列表必须相同
// 2. 返回类型必须相同或是父类返回类型的子类型
// 3. 不能降低访问权限(public > protected > private)
类型转换详解
向上转型(Upcasting)
子类转换为父类类型,自动进行,绝对安全:
Dog dog = new Dog("旺财", 3, "金毛");
// 向上转型 - 自动转换,不需要强制转换
Animal animal = dog; // 等价于: Animal animal = (Animal) dog;
// 向上转型后:
animal.eat(); // 调用的是Dog的eat()方法!多态的体现
// animal.bark(); // 错误!Animal类型看不到Dog特有的方法
向下转型(Downcasting)
父类转换为子类类型,需要强制转换,有风险:
Animal animal = new Dog("旺财", 3, "金毛");
// 向下转型 - 需要强制转换,可能ClassCastException
Dog dog = (Dog) animal; // 安全的,因为animal确实是Dog
dog.bark(); // 现在可以调用Dog特有的方法了
// 不安全的向下转型
Animal cat = new Cat("咪咪", 2, true);
Dog dog2 = (Dog) cat; // 运行时ClassCastException!
安全向下转型:使用instanceof检查
public void handleAnimal(Animal animal) {
// 安全处理不同类型的动物
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark();
} else if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.meow();
} else {
animal.eat(); // 其他动物只调用通用方法
}
}
// Java 16+ 模式匹配(更简洁)
// if (animal instanceof Dog dog) {
// dog.bark();
// }
继承的注意事项
单继承:Java只支持单继承(一个类只能有一个直接父类),但支持多层继承
继承内容:子类继承父类的所有非private成员(private成员子类也无法直接访问)
构造方法:构造方法不能被继承,子类需要在自己的构造方法中调用父类的构造方法
final类:使用final关键字可以防止类被继承
初始化顺序:创建子类对象时,先执行父类的构造方法,再执行子类的构造方法
Object类 - 所有类的父类
在Java中,所有类都直接或间接继承自java.lang.Object类。
| Object类的方法 | 说明 |
|---|---|
| toString() | 返回对象的字符串表示 |
| equals(Object obj) | 判断两个对象是否相等 |
| hashCode() | 返回对象的哈希码 |
| getClass() | 返回对象的运行时类 |
| clone() | 克隆对象 |
| finalize() | 垃圾回收前调用(已过时) |
最佳实践
如果自定义类需要比较相等性或用作HashMap的key,应重写equals()和hashCode()方法,确保相等的对象有相同的哈希码。
小结
- 继承使用
extends关键字,实现代码复用和类层次结构 - super用于调用父类的构造方法、方法和属性
- 方法重写让子类提供父类方法的特定实现
- 向上转型是安全的,自动进行,体现多态
- 向下转型需要强制转换,可能抛ClassCastException,需用instanceof检查
- Java只支持单继承,但支持多层继承
- 所有类的最终父类是Object