讲解

继承(inheritance)让一个新类基于已有类来定义:class Dog extends Animal,Dog 自动获得 Animal 的全部非私有成员,只需补充自己特有的部分。被继承的叫父类(超类),继承的叫子类。Java 只支持单继承——一个类只能 extends 一个父类,但继承可以成链(Dog extends Animal,Animal extends LivingThing),所有类的最终祖先是 Object。

子类构造对象时,会先执行父类的构造方法再执行自己的:父类部分先就绪,子类部分才能搭建。用 super(参数) 显式调用父类构造方法,且必须是子类构造方法的第一行;不写的话编译器自动插入无参 super()——如果父类没有无参构造方法,这一步就会编译报错。super 还能用来调用被子类覆盖前的父类方法:super.speak()。

子类可以重新定义父类已有的方法,这叫重写(override):方法名、参数列表完全相同,方法体换成子类自己的实现,习惯上加 @Override 注解让编译器帮忙校验(名字拼错、签名不符会立刻报错,不加也能工作,但强烈建议加)。重写和重载别混淆:重载是同一个类里参数不同的同名方法,重写是子类对父类方法的重新实现。父类里被 private 修饰的成员子类继承不到(存在但不可直接访问),被 final 修饰的方法不允许重写。

示例

Dog 继承 Animal:继承字段、调用父类构造、重写方法:

class Animal {
    protected String name;

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

    public void speak() {
        System.out.println(name + " 发出声音");
    }
}

class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }

    @Override
    public void speak() {
        System.out.println(name + " 汪汪叫");
    }

    public void fetch() {
        System.out.println(name + " 去捡球");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog("旺财");
        dog.speak();
        dog.fetch();

        Animal generic = new Animal("某动物");
        generic.speak();
    }
}

用 super 调用父类版本,在重写基础上扩展而不是完全替换:

class Employee {
    protected String name;
    protected double baseSalary;

    public Employee(String name, double baseSalary) {
        this.name = name;
        this.baseSalary = baseSalary;
    }

    public double getPay() {
        return baseSalary;
    }
}

class Manager extends Employee {
    private final double bonus;

    public Manager(String name, double baseSalary, double bonus) {
        super(name, baseSalary);
        this.bonus = bonus;
    }

    @Override
    public double getPay() {
        return super.getPay() + bonus;
    }
}

public class Main {
    public static void main(String[] args) {
        Employee e = new Employee("张三", 8000);
        Manager m = new Manager("李四", 12000, 3000);
        System.out.println(e.name + " 的薪资:" + e.getPay());
        System.out.println(m.name + " 的薪资:" + m.getPay());
    }
}

常见坑

  • super(...) 不在第一行:调用父类构造方法必须是子类构造方法的第一条语句,放后面直接编译错误。
  • 父类没有无参构造导致子类编译失败:父类一旦只提供带参构造,子类就必须显式写 super(参数)。给父类加构造方法时想想子类的处境。
  • 重写时签名不一致变成「重载」:参数列表差一个类型,@Override 立刻帮你发现;不加注解的话程序能编译,但调用的永远是父类版本,bug 极难排查。重写必加 @Override。
  • 把继承当万能复用:继承表达的是「是一种」(is-a)关系,Dog 是一种 Animal 才合理;为了复用几个方法就继承(比如「汽车继承方向盘」)会让类层次变成灾难。复用优先考虑「有一个」(组合)。

小结

extends 单继承,super 调父类构造与方法,@Override 校验重写;继承用于 is-a 关系,复用优先组合。下一节看继承真正的威力:多态。