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

🧬 Java 继承(Inheritance)完全指南

📖 继承的基本概念

什么是继承?

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

继承的核心思想

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

🎯 继承的基本语法

extends 关键字

java 复制代码
// 父类(超类、基类)
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 只支持单继承,一个类只能有一个直接父类。

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 类。

java 复制代码
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. 继承的访问权限

java 复制代码
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. 基本重写

java 复制代码
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. 重写规则

java 复制代码
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 注解

java 复制代码
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. 调用父类构造器

java 复制代码
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. 访问父类成员

java 复制代码
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();
    }
}

🏗️ 构造器调用链

java 复制代码
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. 谨慎使用继承

java 复制代码
// ❌ 错误的继承使用:为了复用代码而滥用继承
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 关键字与继承

java 复制代码
// 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. 抽象类和继承

java 复制代码
// 抽象类:不能实例化,用于定义模板
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)是一个重要原则!

相关推荐
该用户已不存在1 小时前
一句话让一个AI为我花了(划掉)生成一个APP,Google Antigravity 实操
后端·ai编程·gemini
苏禾1 小时前
Spring 事务全面详解
后端
t***p9351 小时前
springboot项目读取 resources 目录下的文件的9种方式
java·spring boot·后端
哈哈哈笑什么1 小时前
微服务间进行调用进行失败后重试
后端
小周在成长2 小时前
Java 静态变量(Static Variables)指南
后端
加瓦点灯2 小时前
我用 AI,一天就把一个桌面提醒插件撸完了
后端
红色石头本尊2 小时前
27-集成swagger接口文档
后端
嘻哈baby2 小时前
Linux系统性能排查实战指南:从定位到解决
后端
开心就好20252 小时前
使用开心上架上架 App Store,一次跨平台团队的完整发布流程调度记录
后端