Java 继承(Inheritance)
继承是面向对象编程的核心特性之一,它允许子类继承父类的属性和方法,实现代码复用和建立类之间的层次关系。理解继承是掌握面向对象编程的关键。本章将详细介绍 Java 中的继承机制。
单继承与继承链
什么是继承
**继承(Inheritance)**是子类继承父类的属性和方法,实现代码复用。
语法:class 子类 extends 父类
// 父类(基类、超类)
public class Animal {
protected String name;
protected int age;
public void eat() {
System.out.println(name + " 正在吃东西");
}
public void sleep() {
System.out.println(name + " 正在睡觉");
}
}
// 子类(派生类)
public class Dog extends Animal {
public void bark() {
System.out.println(name + " 在叫");
}
}
// 使用
Dog dog = new Dog();
dog.name = "旺财";
dog.eat(); // 继承自父类
dog.bark(); // 子类特有方法
Java 单继承
Java 只支持单继承,一个类只能继承一个父类。
// ✅ 正确:单继承
public class Dog extends Animal { }
// ❌ 错误:不能多继承
// public class Dog extends Animal, Mammal { }
为什么单继承:
- 避免多重继承的复杂性
- 简化类层次结构
- 减少命名冲突
继承链
继承可以形成链式结构:
// 第一层:动物
public class Animal {
protected String name;
public void eat() {
System.out.println(name + " 吃东西");
}
}
// 第二层:哺乳动物
public class Mammal extends Animal {
public void giveBirth() {
System.out.println(name + " 是胎生");
}
}
// 第三层:狗
public class Dog extends Mammal {
public void bark() {
System.out.println(name + " 汪汪叫");
}
}
// 继承链:Animal -> Mammal -> Dog
Dog dog = new Dog();
dog.name = "旺财";
dog.eat(); // 来自 Animal
dog.giveBirth(); // 来自 Mammal
dog.bark(); // 来自 Dog
Object 类
所有类都继承自 Object 类(如果没有显式继承)。
// 等价于:public class Student extends Object
public class Student {
// ...
}
// Object 类的方法
Student student = new Student();
student.toString(); // 继承自 Object
student.equals(student); // 继承自 Object
student.hashCode(); // 继承自 Object
super 关键字
super 的作用
super 用于引用父类的成员(属性、方法、构造方法)。
访问父类属性
public class Animal {
protected String name = "动物";
}
public class Dog extends Animal {
private String name = "狗";
public void display() {
System.out.println(super.name); // 父类的 name:"动物"
System.out.println(this.name); // 子类的 name:"狗"
}
}
调用父类方法
public class Animal {
public void eat() {
System.out.println("动物吃东西");
}
}
public class Dog extends Animal {
@Override
public void eat() {
super.eat(); // 调用父类方法
System.out.println("狗吃骨头");
}
}
// 使用
Dog dog = new Dog();
dog.eat();
// 输出:
// 动物吃东西
// 狗吃骨头
调用父类构造方法
子类构造方法必须调用父类构造方法。
public class Animal {
protected String name;
protected int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Dog extends Animal {
private String breed;
public Dog(String name, int age, String breed) {
super(name, age); // 必须调用父类构造方法
this.breed = breed;
}
}
规则:
super()必须是子类构造方法的第一条语句- 如果没有显式调用,编译器会自动调用父类的无参构造方法
- 如果父类没有无参构造方法,必须显式调用有参构造方法
public class Animal {
private String name;
// 只有有参构造方法
public Animal(String name) {
this.name = name;
}
}
public class Dog extends Animal {
public Dog(String name) {
super(name); // 必须显式调用
}
// ❌ 错误:如果没有 super(name),会尝试调用 super(),但父类没有无参构造
}
方法重写(Override)
什么是方法重写
**方法重写(Override)**是子类重新定义父类的方法,提供自己的实现。
public class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("汪汪汪");
}
}
// 使用
Animal animal = new Dog();
animal.makeSound(); // 汪汪汪(调用子类重写的方法)
重写的规则
- 方法签名相同:方法名、参数列表必须相同
- 返回类型兼容:返回类型相同或是子类
- 访问修饰符不能更严格:可以更宽松,不能更严格
- 不能重写 private 方法:private 方法不能被重写
- 不能重写 final 方法:final 方法不能被重写
- 不能重写 static 方法:static 方法不能被重写(可以隐藏)
public class Animal {
public void method1() { }
protected void method2() { }
private void method3() { }
public final void method4() { }
public static void method5() { }
}
public class Dog extends Animal {
@Override
public void method1() { } // ✅ 可以重写
@Override
public void method2() { } // ✅ 可以重写(访问修饰符更宽松)
// @Override
// private void method3() { } // ❌ 不能重写 private 方法
// @Override
// public void method4() { } // ❌ 不能重写 final 方法
// @Override
// public static void method5() { } // ❌ 不能重写 static 方法(可以隐藏)
}
@Override 注解
@Override 注解用于标识重写的方法,编译器会检查是否正确重写。
public class Animal {
public void eat() { }
}
public class Dog extends Animal {
@Override
public void eat() { } // ✅ 正确重写
// @Override
// public void eat(String food) { } // ❌ 编译错误:不是重写,是重载
}
好处:
- 编译器检查重写是否正确
- 代码更清晰
- 如果父类方法签名改变,会提示错误
重写 vs 重载
| 特性 | 重写(Override) | 重载(Overload) |
|---|---|---|
| 方法名 | 相同 | 相同 |
| 参数列表 | 相同 | 不同 |
| 返回类型 | 相同或子类 | 可以不同 |
| 访问修饰符 | 不能更严格 | 可以不同 |
| 发生位置 | 继承关系中 | 同一类中 |
public class Animal {
public void eat() { }
public void eat(String food) { } // 重载
}
public class Dog extends Animal {
@Override
public void eat() { } // 重写父类的 eat()
// 这是重载,不是重写
public void eat(String food, int amount) { }
}
继承的访问控制
继承中的访问修饰符
public class Animal {
public String publicField;
protected String protectedField;
String defaultField;
private String privateField;
public void publicMethod() { }
protected void protectedMethod() { }
void defaultMethod() { }
private void privateMethod() { }
}
public class Dog extends Animal {
public void test() {
publicField = "public"; // ✅ 可以访问
protectedField = "protected"; // ✅ 可以访问(子类)
defaultField = "default"; // ✅ 可以访问(同一包)
// privateField = "private"; // ❌ 不能访问
publicMethod(); // ✅ 可以访问
protectedMethod(); // ✅ 可以访问(子类)
defaultMethod(); // ✅ 可以访问(同一包)
// privateMethod(); // ❌ 不能访问
}
}
实际示例
示例 1:完整的继承示例
// 父类:动物
public class Animal {
protected String name;
protected int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println(name + " 正在吃东西");
}
public void sleep() {
System.out.println(name + " 正在睡觉");
}
public void makeSound() {
System.out.println(name + " 发出声音");
}
public void displayInfo() {
System.out.println("姓名:" + name);
System.out.println("年龄:" + age);
}
}
// 子类:狗
public class Dog extends Animal {
private String breed;
public Dog(String name, int age, String breed) {
super(name, age); // 调用父类构造方法
this.breed = breed;
}
@Override
public void makeSound() {
System.out.println(name + " 汪汪叫");
}
public void bark() {
System.out.println(name + " 在叫");
}
@Override
public void displayInfo() {
super.displayInfo(); // 调用父类方法
System.out.println("品种:" + breed);
}
}
// 使用
Dog dog = new Dog("旺财", 3, "金毛");
dog.eat(); // 继承自父类
dog.makeSound(); // 重写的方法
dog.bark(); // 子类特有方法
dog.displayInfo(); // 重写的方法