Java 面相对象继承(Inheritance)指南

82 阅读11分钟

🧬 Java 继承(Inheritance)完全指南

📖 继承的基本概念

什么是继承?

继承是面向对象编程的三大特性之一,允许一个类(子类)基于另一个类(父类)构建,复用父类的属性和方法。

继承的核心思想

  • 代码复用:子类继承父类的属性和方法
  • 扩展功能:子类可以添加新的属性和方法
  • 方法重写:子类可以重写父类的方法
  • 多态基础:为多态提供支持

🎯 继承的基本语法

extends 关键字

// 父类(超类、基类)
class Animal {
    String name;
    int age;
    
    void eat() {
        System.out.println(name + "正在吃东西");
    }
    
    void sleep() {
        System.out.println(name + "正在睡觉");
    }
}

// 子类(派生类)
class Dog extends Animal {  // 使用 extends 继承
    // Dog 自动拥有 Animal 的所有属性和方法
    
    // 子类可以添加新方法
    void bark() {
        System.out.println(name + "汪汪叫");
    }
}

// 另一个子类
class Cat extends Animal {
    void meow() {
        System.out.println(name + "喵喵叫");
    }
}

public class BasicInheritance {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.name = "旺财";
        dog.age = 3;
        
        dog.eat();    // 继承自 Animal
        dog.sleep();  // 继承自 Animal
        dog.bark();   // Dog 自己的方法
        
        System.out.println();
        
        Cat cat = new Cat();
        cat.name = "小花";
        cat.age = 2;
        
        cat.eat();    // 继承自 Animal
        cat.sleep();  // 继承自 Animal
        cat.meow();   // Cat 自己的方法
    }
}

🔄 继承的特点

1. 单继承性

Java 只支持单继承,一个类只能有一个直接父类。

// ✅ 正确的单继承
class A {
    void methodA() {
        System.out.println("A的方法");
    }
}

class B extends A {
    void methodB() {
        System.out.println("B的方法");
    }
}

class C extends B {
    void methodC() {
        System.out.println("C的方法");
    }
}

// ❌ 错误的多继承(Java不允许)
/*
class D extends A, B {  // 编译错误
}
*/

public class SingleInheritance {
    public static void main(String[] args) {
        C obj = new C();
        obj.methodA();  // 可以调用祖先类的方法
        obj.methodB();
        obj.methodC();
        
        // 继承链:Object ← A ← B ← C
        System.out.println("C的父类: " + obj.getClass().getSuperclass());
        System.out.println("B的父类: " + obj.getClass().getSuperclass().getSuperclass());
    }
}

2. 所有类的根类:Object

Java 中所有类都直接或间接继承自 Object 类。

class MyClass {
    // 即使没有显式继承,也默认继承 Object
}

public class ObjectClassDemo {
    public static void main(String[] args) {
        MyClass obj = new MyClass();
        
        // Object 类的方法都可以使用
        System.out.println("字符串表示: " + obj.toString());
        System.out.println("哈希码: " + obj.hashCode());
        System.out.println("类名: " + obj.getClass());
        
        // 常见的 Object 方法
        Object obj1 = new Object();
        Object obj2 = new Object();
        
        System.out.println("\nObject 方法演示:");
        System.out.println("equals: " + obj1.equals(obj2));
        System.out.println("toString: " + obj1.toString());
        System.out.println("getClass: " + obj1.getClass());
        
        // 常用的 final 方法
        System.out.println("wait, notify, notifyAll 用于线程同步");
    }
}

3. 继承的访问权限

class Parent {
    // 不同访问权限的成员
    public String publicField = "public";
    protected String protectedField = "protected";
    String defaultField = "default";  // 包访问权限
    private String privateField = "private";
    
    public void show() {
        // 父类中可以访问所有权限的成员
        System.out.println("父类中访问:");
        System.out.println("public: " + publicField);
        System.out.println("protected: " + protectedField);
        System.out.println("default: " + defaultField);
        System.out.println("private: " + privateField);
    }
}

class Child extends Parent {
    public void accessTest() {
        System.out.println("\n子类中访问:");
        
        // 子类可以访问 public
        System.out.println("public: " + publicField);
        
        // 子类可以访问 protected
        System.out.println("protected: " + protectedField);
        
        // 同包时可以访问 default
        System.out.println("default: " + defaultField);
        
        // 子类不能访问 private
        // System.out.println("private: " + privateField);  // ❌ 编译错误
    }
}

// 不同包中的子类
package other;
import Parent;

class DifferentPackageChild extends Parent {
    public void accessTest() {
        System.out.println("\n不同包子类中访问:");
        
        // 可以访问 public
        System.out.println("public: " + publicField);
        
        // 可以访问 protected(子类权限)
        System.out.println("protected: " + protectedField);
        
        // 不能访问 default(不同包)
        // System.out.println("default: " + defaultField);  // ❌ 编译错误
        
        // 不能访问 private
        // System.out.println("private: " + privateField);  // ❌ 编译错误
    }
}

public class InheritanceAccess {
    public static void main(String[] args) {
        Child child = new Child();
        child.show();
        child.accessTest();
    }
}

🔧 方法重写(Override)

1. 基本重写

class Vehicle {
    protected String brand = "未知品牌";
    
    // 父类方法
    public void start() {
        System.out.println("车辆启动");
    }
    
    public void stop() {
        System.out.println("车辆停止");
    }
    
    public String getInfo() {
        return "车辆品牌: " + brand;
    }
}

class Car extends Vehicle {
    private String model = "未知型号";
    
    // 重写父类方法
    @Override
    public void start() {
        System.out.println("汽车启动:插入钥匙,打火");
    }
    
    @Override
    public void stop() {
        System.out.println("汽车停止:踩刹车,熄火");
    }
    
    // 重写 getInfo 方法
    @Override
    public String getInfo() {
        // 调用父类的方法
        return super.getInfo() + ",型号: " + model;
    }
    
    // 新增方法
    public void playMusic() {
        System.out.println("播放音乐");
    }
}

class ElectricCar extends Car {
    private int batteryLevel = 100;
    
    // 重写父类的方法
    @Override
    public void start() {
        System.out.println("电动汽车启动:按下启动按钮");
    }
    
    @Override
    public String getInfo() {
        return super.getInfo() + ",电量: " + batteryLevel + "%";
    }
    
    // 新增方法
    public void charge() {
        System.out.println("正在充电...");
        batteryLevel = 100;
    }
}

public class MethodOverride {
    public static void main(String[] args) {
        System.out.println("=== 普通汽车 ===");
        Car car = new Car();
        car.brand = "丰田";
        car.start();
        car.stop();
        System.out.println(car.getInfo());
        car.playMusic();
        
        System.out.println("\n=== 电动汽车 ===");
        ElectricCar electricCar = new ElectricCar();
        electricCar.brand = "特斯拉";
        electricCar.start();
        electricCar.stop();
        System.out.println(electricCar.getInfo());
        electricCar.playMusic();  // 继承自 Car
        electricCar.charge();
    }
}

2. 重写规则

class Parent {
    // 可以被重写的方法
    public void publicMethod() {
        System.out.println("父类public方法");
    }
    
    protected void protectedMethod() {
        System.out.println("父类protected方法");
    }
    
    // 不能被重写的方法
    private void privateMethod() {
        System.out.println("父类private方法");
    }
    
    public final void finalMethod() {
        System.out.println("父类final方法");
    }
    
    public static void staticMethod() {
        System.out.println("父类static方法");
    }
}

class Child extends Parent {
    // ✅ 正确:重写public方法(访问权限可以相同或更宽松)
    @Override
    public void publicMethod() {
        System.out.println("子类重写的public方法");
    }
    
    // ✅ 正确:protected可以重写为public
    @Override
    public void protectedMethod() {
        System.out.println("子类重写的protected方法(改为public)");
    }
    
    // ❌ 错误:不能重写private方法
    // @Override
    // private void privateMethod() { }  // 编译错误
    
    // ❌ 错误:不能重写final方法
    // @Override
    // public final void finalMethod() { }  // 编译错误
    
    // ❌ 错误:不能重写static方法(这是隐藏,不是重写)
    public static void staticMethod() {
        System.out.println("子类static方法(隐藏父类)");
    }
    
    // ❌ 错误:访问权限不能更严格
    /*
    @Override
    void publicMethod() {  // 改为默认权限,编译错误
        System.out.println("错误的重写");
    }
    */
}

public class OverrideRules {
    public static void main(String[] args) {
        Child child = new Child();
        child.publicMethod();
        child.protectedMethod();
        
        // 静态方法调用(属于类,不是对象)
        Parent.staticMethod();
        Child.staticMethod();
        
        // 验证重写规则
        System.out.println("\n重写规则总结:");
        System.out.println("1. 方法名、参数列表必须相同");
        System.out.println("2. 返回值类型相同或是子类");
        System.out.println("3. 访问权限不能比父类更严格");
        System.out.println("4. 不能重写private、final、static方法");
        System.out.println("5. 抛出的异常不能比父类更多或更通用");
    }
}

3. @Override 注解

class Base {
    public void display() {
        System.out.println("Base display");
    }
    
    public void show(String message) {
        System.out.println("Base show: " + message);
    }
}

class Derived extends Base {
    // 使用 @Override 注解:明确表示这是重写
    @Override
    public void display() {
        System.out.println("Derived display");
    }
    
    // @Override 可以帮助发现错误
    /*
    @Override
    public void Display() {  // 方法名写错了,编译器会报错
        System.out.println("拼写错误");
    }
    */
    
    /*
    @Override
    public void show() {  // 参数列表不同,不是重写,编译器报错
        System.out.println("缺少参数");
    }
    */
    
    @Override
    public void show(String msg) {  // 正确重写
        System.out.println("Derived show: " + msg);
    }
    
    // 重载(Overload),不是重写
    public void show(String msg, int times) {
        for (int i = 0; i < times; i++) {
            System.out.println("Derived show: " + msg);
        }
    }
}

public class OverrideAnnotation {
    public static void main(String[] args) {
        Derived obj = new Derived();
        obj.display();
        obj.show("Hello");
        obj.show("Hello", 3);
        
        System.out.println("\n@Override 注解的好处:");
        System.out.println("1. 提高代码可读性");
        System.out.println("2. 编译器检查重写是否正确");
        System.out.println("3. 避免拼写错误或参数错误");
        System.out.println("4. 方便IDE进行重构和分析");
    }
}

🔄 super 关键字

1. 调用父类构造器

class Person {
    private String name;
    private int age;
    
    // 父类构造器
    public Person() {
        this.name = "未知";
        this.age = 0;
        System.out.println("Person无参构造器");
    }
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person有参构造器");
    }
    
    public String getInfo() {
        return "姓名: " + name + ", 年龄: " + age;
    }
}

class Student extends Person {
    private String studentId;
    private String school;
    
    // 子类构造器必须调用父类构造器
    public Student() {
        super();  // 调用父类无参构造器(可省略,编译器会自动添加)
        this.studentId = "未分配";
        this.school = "未知学校";
        System.out.println("Student无参构造器");
    }
    
    public Student(String name, int age, String studentId, String school) {
        super(name, age);  // 必须放在第一行
        this.studentId = studentId;
        this.school = school;
        System.out.println("Student有参构造器");
    }
    
    // 错误示例:super() 不是第一句
    /*
    public Student(String id) {
        System.out.println("初始化...");  // ❌ 编译错误
        super();  // super() 必须在第一行
        this.studentId = id;
    }
    */
    
    @Override
    public String getInfo() {
        // 调用父类的方法
        return super.getInfo() + ", 学号: " + studentId + ", 学校: " + school;
    }
    
    // 访问父类属性(如果权限允许)
    public void showParentInfo() {
        // System.out.println(name);  // ❌ 不能直接访问父类private属性
        System.out.println(getInfo());  // ✅ 通过方法访问
    }
}

public class SuperConstructor {
    public static void main(String[] args) {
        System.out.println("=== 创建学生1 ===");
        Student s1 = new Student();
        System.out.println(s1.getInfo());
        
        System.out.println("\n=== 创建学生2 ===");
        Student s2 = new Student("张三", 20, "2023001", "清华大学");
        System.out.println(s2.getInfo());
        
        s2.showParentInfo();
    }
}

2. 访问父类成员

class Animal {
    protected String name = "动物";
    
    public void eat() {
        System.out.println(name + "在吃东西");
    }
    
    public void sleep() {
        System.out.println(name + "在睡觉");
    }
}

class Bird extends Animal {
    private String name = "小鸟";  // 隐藏父类的name
    
    @Override
    public void eat() {
        System.out.println(name + "在吃虫子");
    }
    
    public void show() {
        System.out.println("\n在Bird类中:");
        
        // 访问子类的name
        System.out.println("this.name = " + this.name);
        
        // 访问父类的name
        System.out.println("super.name = " + super.name);
        
        // 调用子类的方法
        this.eat();
        
        // 调用父类的方法
        super.eat();
        super.sleep();
        
        // 访问父类被重写的方法
        System.out.println("\n通过super访问被重写的方法:");
        super.eat();  // 调用父类的eat
    }
    
    // 演示方法重写
    @Override
    public void sleep() {
        System.out.println(name + "在鸟巢睡觉");
        // 可以在重写方法中调用父类方法
        super.sleep();  // 调用父类的sleep
        System.out.println(name + "睡醒了");
    }
}

public class SuperMember {
    public static void main(String[] args) {
        Bird bird = new Bird();
        bird.show();
        
        System.out.println("\n=== 测试重写方法 ===");
        bird.sleep();
    }
}

🏗️ 构造器调用链

class Grandparent {
    public Grandparent() {
        System.out.println("1. Grandparent构造器");
    }
    
    public Grandparent(String message) {
        System.out.println("1. Grandparent构造器: " + message);
    }
}

class Parent extends Grandparent {
    public Parent() {
        // 这里会自动调用 super()
        System.out.println("2. Parent构造器");
    }
    
    public Parent(String message) {
        super("来自Parent的消息");  // 显式调用父类构造器
        System.out.println("2. Parent构造器: " + message);
    }
}

class Child extends Parent {
    public Child() {
        // 这里会自动调用 super()
        System.out.println("3. Child构造器");
    }
    
    public Child(String message) {
        super("来自Child的消息");  // 调用父类构造器
        System.out.println("3. Child构造器: " + message);
    }
    
    public Child(int number) {
        this("带参数的Child");  // 调用本类其他构造器
        System.out.println("3. Child构造器(数字): " + number);
    }
}

public class ConstructorChain {
    public static void main(String[] args) {
        System.out.println("=== 创建Child对象(无参)===");
        Child c1 = new Child();
        
        System.out.println("\n=== 创建Child对象(有参)===");
        Child c2 = new Child("测试");
        
        System.out.println("\n=== 创建Child对象(数字)===");
        Child c3 = new Child(100);
        
        /*
        输出结果:
        === 创建Child对象(无参)===
        1. Grandparent构造器
        2. Parent构造器
        3. Child构造器
        
        === 创建Child对象(有参)===
        1. Grandparent构造器: 来自Parent的消息
        2. Parent构造器: 来自Child的消息
        3. Child构造器: 测试
        
        === 创建Child对象(数字)===
        1. Grandparent构造器: 来自Parent的消息
        2. Parent构造器: 来自Child的消息
        3. Child构造器: 带参数的Child
        3. Child构造器(数字): 100
        */
        
        System.out.println("\n构造器调用规则:");
        System.out.println("1. 构造器第一行必须是 this() 或 super()");
        System.out.println("2. 如果没有显式调用,编译器会自动添加 super()");
        System.out.println("3. this() 和 super() 不能同时存在");
        System.out.println("4. 构造器调用形成一条链,最终会调用到 Object 的构造器");
    }
}

⚠️ 继承的注意事项

1. 谨慎使用继承

// ❌ 错误的继承使用:为了复用代码而滥用继承
class Engine {
    void start() {
        System.out.println("引擎启动");
    }
}

// Car 和 Engine 不是 "is-a" 关系,而是 "has-a" 关系
/*
class Car extends Engine {  // 错误:汽车不是一种引擎
    void drive() {
        System.out.println("汽车行驶");
    }
}
*/

// ✅ 正确的做法:使用组合
class Car {
    private Engine engine;  // 组合:汽车有一个引擎
    
    public Car() {
        this.engine = new Engine();
    }
    
    void drive() {
        engine.start();  // 调用引擎的方法
        System.out.println("汽车行驶");
    }
}

// ✅ 正确的继承:符合 "is-a" 关系
class Vehicle {
    void move() {
        System.out.println("交通工具移动");
    }
}

class Truck extends Vehicle {  // 正确:卡车是一种交通工具
    @Override
    void move() {
        System.out.println("卡车在路上行驶");
    }
}

class Boat extends Vehicle {   // 正确:船是一种交通工具
    @Override
    void move() {
        System.out.println("船在水上航行");
    }
}

public class InheritanceDesign {
    public static void main(String[] args) {
        System.out.println("继承设计原则:");
        System.out.println("1. 符合 is-a 关系才使用继承");
        System.out.println("2. 优先使用组合而不是继承");
        System.out.println("3. 避免过深的继承层次(通常不超过3层)");
        System.out.println("4. 考虑使用接口实现多继承的效果");
        
        Car car = new Car();
        car.drive();
    }
}

2. final 关键字与继承

// final 类:不能被继承
final class FinalClass {
    public void show() {
        System.out.println("FinalClass的方法");
    }
}

// ❌ 错误:不能继承final类
/*
class SubClass extends FinalClass {  // 编译错误
}
*/

class NormalClass {
    // final 方法:不能被子类重写
    public final void finalMethod() {
        System.out.println("这个方法不能重写");
    }
    
    // 普通方法:可以重写
    public void normalMethod() {
        System.out.println("这个方法可以重写");
    }
}

class SubNormalClass extends NormalClass {
    /*
    // ❌ 错误:不能重写final方法
    @Override
    public void finalMethod() {
        System.out.println("尝试重写");
    }
    */
    
    // ✅ 正确:可以重写普通方法
    @Override
    public void normalMethod() {
        System.out.println("重写后的方法");
    }
}

public class FinalInheritance {
    public static void main(String[] args) {
        System.out.println("final 关键字的作用:");
        System.out.println("1. final 类:不能被继承(如 String、Integer)");
        System.out.println("2. final 方法:不能被子类重写");
        System.out.println("3. final 变量:常量,不能修改");
        
        System.out.println("\n使用 final 的好处:");
        System.out.println("1. 提高性能(编译器优化)");
        System.out.println("2. 保证安全性(防止修改)");
        System.out.println("3. 设计意图明确(禁止扩展)");
        
        FinalClass obj = new FinalClass();
        obj.show();
    }
}

💡 继承的最佳实践

1. 抽象类和继承

// 抽象类:不能实例化,用于定义模板
abstract class Employee {
    private String name;
    private String id;
    
    public Employee(String name, String id) {
        this.name = name;
        this.id = id;
    }
    
    // 抽象方法:必须由子类实现
    public abstract double calculateSalary();
    
    // 具体方法:子类可以直接使用或重写
    public String getInfo() {
        return "员工: " + name + " (ID: " + id + ")";
    }
    
    // 模板方法模式
    public final void showSalary() {
        System.out.println(getInfo());
        System.out.println("月薪: " + calculateSalary());
    }
}

// 具体子类
class FullTimeEmployee extends Employee {
    private double monthlySalary;
    
    public FullTimeEmployee(String name, String id, double monthlySalary) {
        super(name, id);
        this.monthlySalary = monthlySalary;
    }
    
    @Override
    public double calculateSalary() {
        return monthlySalary;
    }
}

class PartTimeEmployee extends Employee {
    private double hourlyRate;
    private int hoursWorked;
    
    public PartTimeEmployee(String name, String id, double hourlyRate, int hours) {
        super(name, id);
        this.hourlyRate = hourlyRate;
        this.hoursWorked = hours;
    }
    
    @Override
    public double calculateSalary() {
        return hourlyRate * hoursWorked;
    }
}

public class AbstractClassDemo {
    public static void main(String[] args) {
        // ❌ 错误:不能实例化抽象类
        // Employee emp = new Employee("张三", "001");
        
        // ✅ 正确:创建具体子类的对象
        Employee emp1 = new FullTimeEmployee("张三", "FT001", 8000.0);
        Employee emp2 = new PartTimeEmployee("李四", "PT001", 50.0, 120);
        
        System.out.println("=== 员工薪资 ===");
        emp1.showSalary();
        emp2.showSalary();
        
        System.out.println("\n抽象类的优点:");
        System.out.println("1. 定义通用模板和行为规范");
        System.out.println("2. 强制子类实现特定方法");
        System.out.println("3. 代码复用和扩展性");
        System.out.println("4. 支持模板方法模式");
    }
}

📊 继承总结表

特性说明
语法class SubClass extends SuperClass
单继承Java 只支持单继承
所有类根所有类继承自 Object
访问权限子类可以访问父类的 publicprotected、同包default
构造器子类必须调用父类构造器(显式或隐式)
方法重写子类可以重写父类非private、非final、非static方法
super关键字访问父类成员、调用父类构造器
向上转型子类对象可以赋值给父类引用
动态绑定实例方法调用在运行时确定
final类不能被继承
final方法不能被子类重写
抽象类不能实例化,用于定义模板
继承层次不宜过深(通常≤3层)

🎓 继承最佳实践总结

  1. 符合 is-a 关系:子类应该是父类的特殊类型
  2. 优先使用组合:除非需要多态,否则优先考虑组合
  3. 避免深层继承:继承层次不宜过深(≤3层)
  4. 合理使用 final:明确设计意图,防止不必要的扩展
  5. 使用抽象类:为相关类提供通用模板
  6. 使用接口:实现多重继承,定义行为契约
  7. 方法访问权限:从严格到宽松,避免过度暴露
  8. 构造函数设计:合理设计构造器链,确保对象正确初始化

记住:继承是强耦合关系,要谨慎使用。在面向对象设计中,组合优于继承(Composition over Inheritance)是一个重要原则!