Skip to content

Chapter 09 · 继承

"我妈是我妈" — 子类天然拥有父类的一切,还能加自己的东西。


🎯 本章目标

  • 理解继承的本质:复用 + 扩展
  • 掌握 extends 关键字
  • 学会 super 的两种用法
  • 区分 方法重写(Override)vs 重载(Overload)
  • 知道 final 关键字三大用法
  • 理解 Object 类是所有类的祖先

1. 为什么需要继承?

假设你要写"学生"和"老师"两个类:

java
class Student {
    String name;
    int age;
    void eat() { ... }
    void sleep() { ... }
    void study() { ... }
}

class Teacher {
    String name;          // 重复
    int age;              // 重复
    void eat() { ... }    // 重复
    void sleep() { ... }  // 重复
    void teach() { ... }
}

90% 重复!再来"工程师"、"医生"……要疯。

抽出公共部分到父类 Person,让 StudentTeacher 继承 它:

java
class Person {                       // 父类(基类、超类)
    String name;
    int age;
    void eat() { ... }
    void sleep() { ... }
}

class Student extends Person {       // 继承
    void study() { ... }
}

class Teacher extends Person {       // 继承
    void teach() { ... }
}

子类自动拥有父类的字段和方法,还能加自己的

继承结构


2. extends 关键字

java
class 子类 extends 父类 { ... }

Java 继承的特点

  1. 单继承:一个类只能有一个直接父类(不像 C++ 多继承)
  2. 多层继承:可以 A → B → C → ... 一直延伸
  3. 不能跨级:孙子继承爷爷必须经过爸爸
  4. 构造器不被继承,但子类构造器会先调用父类构造器
java
class Animal { }
class Dog extends Animal { }
class Husky extends Dog { }       // ✅ 多层继承
class Cat extends Animal, Dog { } // ❌ 不支持多继承

3. super 关键字

super = "调用父类的东西",两大用法:

3.1 调用父类的方法 / 字段

java
class Person {
    String name;
    void greet() { System.out.println("Hi, I'm " + name); }
}

class Student extends Person {
    String school;
    @Override
    void greet() {
        super.greet();    // 先调父类的 greet
        System.out.println("我在 " + school + " 读书");
    }
}

3.2 调用父类的构造器

java
class Person {
    String name;
    Person(String name) { this.name = name; }
}

class Student extends Person {
    String school;
    Student(String name, String school) {
        super(name);                  // ① 必须是第一行
        this.school = school;          // ②
    }
}

⚠️ 隐式 super():如果子类构造器里没写 super(...),编译器会自动加 super();(调用父类无参构造器)。如果父类没有无参构造器 → 编译错误

super 调用顺序


4. 方法重写(Override)

子类对父类方法不满意?重写它!

java
class Animal {
    void sound() { System.out.println("某种声音"); }
}

class Dog extends Animal {
    @Override
    void sound() { System.out.println("汪汪!"); }
}

class Cat extends Animal {
    @Override
    void sound() { System.out.println("喵喵!"); }
}

Animal a = new Dog();
a.sound();    // 输出: 汪汪!(不是"某种声音"!)

重写规则(必背)

规则说明
方法签名相同名字 + 参数列表必须一致
返回类型相同或父类返回类型的子类(协变返回)
访问权限不能比父类更严格(父 public,子不能 private)
抛出异常不能抛比父类更宽的检查异常
@Override强烈建议加(编译期检查)

重写 vs 重载(高频面试)

对比重写 Override重载 Overload
位置父子类同一个类
方法名相同相同
参数列表必须相同必须不同
返回类型相同/协变无要求
绑定运行时(动态分派)编译时(静态分派)
关键字@Override

5. final 关键字三大用法

final = "终极",封死,不准改

修饰对象含义
final 变量常量,赋值后不能改
final 方法不允许子类重写
final不允许被继承(如 StringInteger
java
final class Constants { }                  // 不能被继承

class Calculator {
    final void calculate() { }              // 子类不能重写
}

final int MAX = 100;                        // 常量
final List<String> list = new ArrayList<>(); // 引用不能换,但内容能改
list.add("hi");                             // ✅
list = new ArrayList<>();                   // ❌

6. Object 类:所有类的祖宗

所有类都隐式继承 Object(不写 extends 默认就是)。

Object 提供了几个所有类都有的方法:

方法默认行为是否常重写
toString()返回 类名@hashCode✅ 常重写
equals(Object)比较地址(== 等价)✅ 必重写(要比内容时)
hashCode()返回内存地址 hash✅ 必和 equals 一起重写
getClass()返回 Class 对象反射用
clone()浅拷贝偶尔重写

toString 经典重写

java
class Person {
    String name; int age;

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

System.out.println(new Person()); // 自动调 toString,不用 .toString()

equals + hashCode 必须一起重写

🔑 黄金规则:两个对象 equals() 为 true,hashCode 必须相等

java
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof Person)) return false;
    Person p = (Person) o;
    return age == p.age && Objects.equals(name, p.name);
}

@Override
public int hashCode() {
    return Objects.hash(name, age);
}

💡 IDE 一键生成(IDEA: Alt + Insert → equals and hashCode)。


7. 实战练习

文件内容
Person.java父类:人
Student.java子类:学生
Teacher.java子类:老师
InheritanceDemo.java综合演示入口
ConstructorChain.javasuper 构造器链
EqualsHashCodeDemo.javaequals/hashCode 必看

8. 浏览器演示

打开 demo.html

  • 继承关系树图
  • 父类构造器调用顺序动画
  • equals/hashCode 翻车与修复

9. 面试可能会问什么?

Q1: Java 为什么不支持多继承?

:避免菱形继承问题(A 继承 B 和 C,而 B 和 C 都继承 D,A 调用 D 的方法时不知道走哪条路)。 Java 通过单继承 + 多接口 实现"伪多继承"。

Q2: 子类构造器一定会调父类构造器吗?

一定。如果你不写 super(...),编译器会自动加 super();。如果父类没有无参构造器,子类必须显式写 super(参数),否则编译失败。

Q3: 重写要满足什么规则?(即"两同两小一大")

  • 两同:方法名相同、参数列表相同
  • 两小:返回类型 ≤ 父类(协变),异常 ≤ 父类
  • 一大:访问权限 ≥ 父类(不能更严格)

Q4: equals 和 == 的区别?

  • ==:基本类型比值,引用类型比地址
  • equals():默认与 == 等价;String/Integer 等重写后比内容

Q5: 为什么 equals 和 hashCode 必须一起重写?

:HashMap、HashSet 等基于 hash 的容器:

  1. hashCode 算桶位置
  2. equals 在桶内比对

如果两个对象 equals 为 true 但 hashCode 不等,HashSet 就会重复存储 → bug。

Q6: final 类有哪些?

StringIntegerLongBooleanMath... 为什么?安全(防止子类覆盖关键行为)+ 性能(JIT 可以做更激进的优化)。

Q7: super.method()this.method() 区别?

  • this.method():调用当前对象的方法(如果重写了,调子类的)
  • super.method()强制调父类的版本,绕过子类重写

🎁 本章小结

✅ 继承 = 复用 + 扩展,extends 关键字
✅ super 调父类方法/构造器
✅ 重写 @Override:方法签名相同,访问权限不缩小
✅ final 三用:常量、方法不可重写、类不可继承
✅ Object 是所有类的爹,toString/equals/hashCode 三剑客

🔗 导航