Java 继承

继承(Inheritance)是面向对象第二大特性——子类继承父类,自动获得父类的字段和方法,避免重复代码。Java 是单继承:一个类只能有一个直接父类,但通过"接口"可以实现"多继承"的效果。本章带你彻底搞懂 Java 继承。

1. extends 关键字

子类用 extends 关键字继承父类,获得父类所有非 private 成员:

// 父类(基类、超类)
class Animal {
    String name;

    public Animal(String name) {
        this.name = name;
    }

    public void eat() {
        System.out.println(name + " 在吃东西");
    }

    public void sleep() {
        System.out.println(name + " 在睡觉");
    }
}

// 子类用 extends 继承父类
// 子类自动获得父类的所有非 private 成员
class Dog extends Animal {
    public Dog(String name) {
        super(name);          // 调用父类构造方法,必须放第一行
    }

    // 子类可以新增自己的方法
    public void bark() {
        System.out.println(name + " 汪汪叫!");
    }
}

public class InheritDemo {
    public static void main(String[] args) {
        Dog d = new Dog("旺财");
        d.eat();          // 旺财 在吃东西(继承自父类)
        d.sleep();        // 旺财 在睡觉(继承自父类)
        d.bark();         // 旺财 汪汪叫!(子类特有)
    }
}

几个核心规则:

2. 方法重写(@Override)

子类可以重新实现父类的方法,这叫"重写"(Override)。运行时根据实际对象类型调用对应版本——这就是多态的基础。

class Shape {
    public double area() {
        return 0;
    }

    // 父类方法,子类可以重写
    public String describe() {
        return "形状,面积 = " + area();
    }
}

class Circle extends Shape {
    private double r;

    public Circle(double r) { this.r = r; }

    // @Override 注解:告诉编译器"我这是重写父类方法"
    // 写错了(方法名拼错、参数对不上)编译器会报错
    @Override
    public double area() {
        return Math.PI * r * r;
    }
}

class Rectangle extends Shape {
    private double w, h;

    public Rectangle(double w, double h) {
        this.w = w;
        this.h = h;
    }

    @Override
    public double area() {
        return w * h;
    }
}

public class OverrideDemo {
    public static void main(String[] args) {
        Shape s1 = new Circle(2);
        Shape s2 = new Rectangle(3, 4);
        System.out.println(s1.describe());   // 形状,面积 = 12.566...
        System.out.println(s2.describe());   // 形状,面积 = 12.0
    }
}

重写的规则("两同两小一大"):

@Override 注解不是必须的,但强烈推荐写——它让编译器帮你检查"是不是真的重写了",新手救星。

3. super 关键字

super 是"父类的引用",有三种用法:

class Person {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person 构造被调用");
    }

    public void greet() {
        System.out.println("我是 Person: " + name);
    }
}

class Student extends Person {
    String school;

    public Student(String name, int age, String school) {
        super(name, age);       // 调用父类构造(必须第一行)
        this.school = school;
        System.out.println("Student 构造被调用");
    }

    @Override
    public void greet() {
        super.greet();          // 调用父类版本
        System.out.println("我是 Student,在 " + school + " 上学");
    }
}

public class SuperDemo {
    public static void main(String[] args) {
        Student s = new Student("小明", 20, "清华");
        // 输出:
        // Person 构造被调用
        // Student 构造被调用
        // (创建子类对象时,父类构造先执行)

        s.greet();
        // 输出:
        // 我是 Person: 小明
        // 我是 Student,在 清华 上学
    }
}

构造方法链:创建子类对象时,先执行父类构造(一直追溯到 Object),再执行子类构造。这就是为什么 super(...) 必须放第一行——父类必须先初始化。

4. Object 类:所有类的根

Java 所有类都继承自 java.lang.Object。Object 提供了几个每个类都该重写的方法:

// 所有类都隐式继承 Object,即使没写 extends Object
class User {
    String name;
    int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 重写 Object 的 equals:自定义"相等"逻辑
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;             // 同一对象
        if (o == null || getClass() != o.getClass()) return false;
        User u = (User) o;
        return age == u.age && name.equals(u.name);
    }

    // 重写 hashCode:equals 相等的对象 hashCode 必须相等
    @Override
    public int hashCode() {
        return java.util.Objects.hash(name, age);
    }

    // 重写 toString:打印对象时更友好
    @Override
    public String toString() {
        return "User{name=" + name + ", age=" + age + "}";
    }
}

public class ObjectDemo {
    public static void main(String[] args) {
        User u1 = new User("小明", 20);
        User u2 = new User("小明", 20);
        System.out.println(u1);              // User{name=小明, age=20}(默认调 toString)
        System.out.println(u1.equals(u2));   // true(自定义相等)
        System.out.println(u1 instanceof User);    // true
    }
}

IDEA 一键生成:右键 Generate → equals() and hashCode() → toString()。这是新手必学操作。

5. final 关键字:禁止继承/重写

final 用在三个地方,含义不同:

// final 类:不能被继承
final class MathUtil {
    public static int square(int n) { return n * n; }
}
// class SubMath extends MathUtil { }   // ❌ 编译报错:final 类不能被继承

class Parent {
    // final 方法:不能被子类重写
    public final void rules() {
        System.out.println("这是规矩,不许改");
    }

    public void optional() {
        System.out.println("可以重写");
    }
}

class Child extends Parent {
    // @Override
    // public void rules() { }       // ❌ 编译报错:final 方法不能重写

    @Override
    public void optional() {
        System.out.println("子类重写了");
    }
}

6. 继承 vs 组合

继承虽好,但不要滥用。《Effective Java》建议:优先使用组合而非继承

小结

这一章你掌握了 Java 继承的全部要点:extends、super、方法重写、Object 根类、final。下一篇我们看继承的延伸——多态,看父类引用怎么调用子类方法,以及它为什么是设计模式、框架扩展的基石。

← 上一篇 Java 类与对象

下一篇 Java 多态

✈️💬