Java继承:代码复用的"家族树"

32 阅读10分钟

一、什么是继承?

继承是面向对象编程的核心特性之一,它允许一个类(子类)继承另一个类(父类)的属性和方法。就像生物学中的遗传:

  • 父类(基类)→ 通用特性
  • 子类(派生类)→ 父类的特性 + 自己的特性

现实世界的例子:

交通工具(父类)
├── 汽车(子类)
├── 摩托车(子类)
└── 自行车(子类)

二、继承的基本语法

2.1 最简单的继承示例

// 父类
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的name、age属性和eat()、sleep()方法
    
    // Dog特有的方法
    void bark() {
        System.out.println(name + "在汪汪叫");
    }
}

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

public class InheritanceBasic {
    public static void main(String[] args) {
        System.out.println("=== 继承基本示例 ===");
        
        // 创建Dog对象
        Dog dog = new Dog();
        dog.name = "旺财";
        dog.age = 3;
        
        // 调用继承自父类的方法
        dog.eat();
        dog.sleep();
        
        // 调用子类特有的方法
        dog.bark();
        
        System.out.println("------------------");
        
        // 创建Cat对象
        Cat cat = new Cat();
        cat.name = "咪咪";
        cat.age = 2;
        
        cat.eat();
        cat.sleep();
        cat.meow();
    }
}

运行结果:

=== 继承基本示例 ===
旺财正在吃东西
旺财正在睡觉
旺财在汪汪叫
------------------
咪咪正在吃东西
咪咪正在睡觉
咪咪在喵喵叫

2.2 继承的层次结构

// 更复杂的继承层次
class Vehicle {
    String brand;
    String color;
    int maxSpeed;
    
    void start() {
        System.out.println(color + "的" + brand + "启动了");
    }
    
    void stop() {
        System.out.println(color + "的" + brand + "停下了");
    }
}

// 第一层子类
class Car extends Vehicle {
    int doors;  // 车门数量
    
    void honk() {
        System.out.println(brand + "汽车在鸣笛:嘀嘀!");
    }
}

class Motorcycle extends Vehicle {
    boolean hasSidecar;  // 是否有边斗
    
    void revEngine() {
        System.out.println(brand + "摩托车在轰油门:嗡嗡!");
    }
}

// 第二层子类
class ElectricCar extends Car {
    int batteryCapacity;  // 电池容量
    
    void charge() {
        System.out.println(brand + "电动车正在充电");
    }
}

class SportsCar extends Car {
    boolean isConvertible;  // 是否敞篷
    
    void turboBoost() {
        System.out.println(brand + "跑车启动涡轮增压!");
    }
}

public class InheritanceHierarchy {
    public static void main(String[] args) {
        System.out.println("=== 继承层次结构 ===");
        
        // 创建Vehicle对象
        Vehicle vehicle = new Vehicle();
        vehicle.brand = "通用";
        vehicle.color = "白色";
        vehicle.maxSpeed = 120;
        vehicle.start();
        vehicle.stop();
        
        System.out.println("------------------");
        
        // 创建Car对象
        Car car = new Car();
        car.brand = "丰田";
        car.color = "黑色";
        car.doors = 4;
        car.start();
        car.honk();
        car.stop();
        
        System.out.println("------------------");
        
        // 创建ElectricCar对象
        ElectricCar electricCar = new ElectricCar();
        electricCar.brand = "特斯拉";
        electricCar.color = "红色";
        electricCar.doors = 4;
        electricCar.batteryCapacity = 100;
        electricCar.start();
        electricCar.charge();
        electricCar.honk();
        electricCar.stop();
        
        System.out.println("------------------");
        
        // 创建SportsCar对象
        SportsCar sportsCar = new SportsCar();
        sportsCar.brand = "保时捷";
        sportsCar.color = "黄色";
        sportsCar.isConvertible = true;
        sportsCar.start();
        sportsCar.turboBoost();
        sportsCar.stop();
    }
}

三、方法重写(Override):子类的新特性

3.1 什么是方法重写?

方法重写是子类重新定义父类中已有的方法,以满足子类的特殊需求。

class Bird {
    String name;
    
    void fly() {
        System.out.println(name + "在天空飞翔");
    }
    
    void makeSound() {
        System.out.println(name + "发出声音");
    }
}

class Penguin extends Bird {
    // 企鹅不会飞,所以重写fly方法
    @Override  // 注解,表示重写父类方法(可选但推荐)
    void fly() {
        System.out.println(name + "不会飞,但游泳很棒!");
    }
    
    // 重写makeSound方法
    @Override
    void makeSound() {
        System.out.println(name + "在叫:嘎嘎!");
    }
}

class Eagle extends Bird {
    // 鹰飞得更高更快
    @Override
    void fly() {
        System.out.println(name + "在高空翱翔,速度很快!");
    }
    
    @Override
    void makeSound() {
        System.out.println(name + "在叫:啸!");
    }
    
    // 鹰特有的方法
    void hunt() {
        System.out.println(name + "正在捕猎");
    }
}

public class MethodOverride {
    public static void main(String[] args) {
        System.out.println("=== 方法重写示例 ===");
        
        Bird bird1 = new Bird();
        bird1.name = "普通鸟";
        bird1.fly();
        bird1.makeSound();
        
        System.out.println("------------------");
        
        Penguin penguin = new Penguin();
        penguin.name = "企鹅";
        penguin.fly();      // 调用重写后的方法
        penguin.makeSound();
        
        System.out.println("------------------");
        
        Eagle eagle = new Eagle();
        eagle.name = "老鹰";
        eagle.fly();        // 调用重写后的方法
        eagle.makeSound();
        eagle.hunt();       // 调用特有方法
    }
}

3.2 super关键字:访问父类成员

class Smartphone {
    String brand;
    double screenSize;
    
    Smartphone(String brand, double screenSize) {
        this.brand = brand;
        this.screenSize = screenSize;
    }
    
    void makeCall(String number) {
        System.out.println("用" + brand + "手机打电话给" + number);
    }
    
    void showInfo() {
        System.out.println("品牌:" + brand + ",屏幕尺寸:" + screenSize + "英寸");
    }
}

class SmartphonePro extends Smartphone {
    boolean has5G;
    int cameraCount;
    
    // 子类构造方法必须调用父类构造方法
    SmartphonePro(String brand, double screenSize, boolean has5G, int cameraCount) {
        super(brand, screenSize);  // 调用父类构造方法
        this.has5G = has5G;
        this.cameraCount = cameraCount;
    }
    
    // 重写父类方法,并扩展功能
    @Override
    void showInfo() {
        super.showInfo();  // 调用父类的方法
        System.out.println("5G支持:" + (has5G ? "是" : "否"));
        System.out.println("摄像头数量:" + cameraCount + "个");
    }
    
    // 子类特有的方法
    void takePhoto() {
        System.out.println("用" + cameraCount + "个摄像头拍照");
    }
}

public class SuperKeyword {
    public static void main(String[] args) {
        System.out.println("=== super关键字示例 ===");
        
        Smartphone phone1 = new Smartphone("小米", 6.5);
        phone1.makeCall("10086");
        phone1.showInfo();
        
        System.out.println("------------------");
        
        SmartphonePro phone2 = new SmartphonePro("华为", 6.8, true, 4);
        phone2.makeCall("10010");  // 继承自父类
        phone2.showInfo();         // 调用重写后的方法
        phone2.takePhoto();        // 子类特有方法
    }
}

四、访问修饰符与继承

4.1 不同访问修饰符的继承特性

class Parent {
    public String publicField = "公共字段";       // 任何地方都可访问
    protected String protectedField = "受保护字段"; // 同包或子类可访问
    String defaultField = "默认字段";             // 同包可访问
    private String privateField = "私有字段";     // 只有本类可访问
    
    public void publicMethod() {
        System.out.println("公共方法");
    }
    
    protected void protectedMethod() {
        System.out.println("受保护方法");
    }
    
    void defaultMethod() {
        System.out.println("默认方法");
    }
    
    private void privateMethod() {
        System.out.println("私有方法");
    }
    
    // 测试私有字段的访问
    public void testPrivateAccess() {
        System.out.println("父类内部可以访问:" + privateField);
        privateMethod();
    }
}

// 同包子类
class ChildSamePackage extends Parent {
    public void testAccess() {
        System.out.println("\n同包子类访问测试:");
        System.out.println(publicField);      // ✓ 可以
        System.out.println(protectedField);   // ✓ 可以(子类)
        System.out.println(defaultField);     // ✓ 可以(同包)
        // System.out.println(privateField);  // ✗ 错误:私有
        
        publicMethod();      // ✓ 可以
        protectedMethod();   // ✓ 可以(子类)
        defaultMethod();     // ✓ 可以(同包)
        // privateMethod();  // ✗ 错误:私有
    }
}

// 不同包子类
class ChildDifferentPackage extends Parent {
    public void testAccess() {
        System.out.println("\n不同包子类访问测试:");
        System.out.println(publicField);      // ✓ 可以
        System.out.println(protectedField);   // ✓ 可以(子类)
        // System.out.println(defaultField);  // ✗ 错误:不同包
        // System.out.println(privateField);  // ✗ 错误:私有
        
        publicMethod();      // ✓ 可以
        protectedMethod();   // ✓ 可以(子类)
        // defaultMethod();  // ✗ 错误:不同包
        // privateMethod();  // ✗ 错误:私有
    }
}

public class InheritanceAccess {
    public static void main(String[] args) {
        System.out.println("=== 访问修饰符与继承 ===");
        
        Parent parent = new Parent();
        System.out.println(parent.publicField);
        parent.publicMethod();
        parent.testPrivateAccess();
        
        ChildSamePackage child1 = new ChildSamePackage();
        child1.testAccess();
    }
}

五、构造方法的继承

class Employee {
    String name;
    int id;
    double salary;
    
    // 父类无参构造方法
    Employee() {
        System.out.println("Employee无参构造方法被调用");
        this.name = "未知";
        this.id = 0;
        this.salary = 0.0;
    }
    
    // 父类有参构造方法
    Employee(String name, int id, double salary) {
        System.out.println("Employee有参构造方法被调用");
        this.name = name;
        this.id = id;
        this.salary = salary;
    }
    
    void work() {
        System.out.println(name + "正在工作");
    }
    
    void showInfo() {
        System.out.println("员工:" + name + ",工号:" + id + ",薪资:" + salary);
    }
}

class Manager extends Employee {
    String department;
    int teamSize;
    
    // 子类构造方法1:无参
    Manager() {
        super();  // 调用父类无参构造方法(可省略,编译器会自动添加)
        System.out.println("Manager无参构造方法被调用");
        this.department = "未分配";
        this.teamSize = 0;
    }
    
    // 子类构造方法2:有参,调用父类有参构造方法
    Manager(String name, int id, double salary, String department, int teamSize) {
        super(name, id, salary);  // 必须放在第一行
        System.out.println("Manager有参构造方法被调用");
        this.department = department;
        this.teamSize = teamSize;
    }
    
    // 子类构造方法3:部分参数
    Manager(String name, String department) {
        super(name, 1000, 8000.0);  // 调用父类构造方法
        this.department = department;
        this.teamSize = 5;
    }
    
    // 重写父类方法
    @Override
    void work() {
        super.work();  // 调用父类方法
        System.out.println(name + "作为" + department + "部门经理在管理工作");
    }
    
    // 重写showInfo方法
    @Override
    void showInfo() {
        super.showInfo();
        System.out.println("部门:" + department + ",团队人数:" + teamSize);
    }
    
    // 经理特有的方法
    void holdMeeting() {
        System.out.println(name + "正在召开部门会议");
    }
}

public class ConstructorInheritance {
    public static void main(String[] args) {
        System.out.println("=== 构造方法的继承 ===");
        
        System.out.println("\n1. 创建普通员工:");
        Employee emp = new Employee("张三", 1001, 5000.0);
        emp.work();
        emp.showInfo();
        
        System.out.println("\n2. 创建经理(无参构造):");
        Manager mgr1 = new Manager();
        mgr1.showInfo();
        
        System.out.println("\n3. 创建经理(有参构造):");
        Manager mgr2 = new Manager("李四", 1002, 15000.0, "技术部", 10);
        mgr2.work();
        mgr2.showInfo();
        mgr2.holdMeeting();
        
        System.out.println("\n4. 创建经理(部分参数):");
        Manager mgr3 = new Manager("王五", "市场部");
        mgr3.showInfo();
    }
}

六、final关键字与继承

6.1 final类:不能被继承

// final类:不能被继承
final class Circle {
    double radius;
    
    Circle(double radius) {
        this.radius = radius;
    }
    
    double getArea() {
        return Math.PI * radius * radius;
    }
}

// ❌ 错误:不能继承final类
// class SpecialCircle extends Circle { }

public class FinalClass {
    public static void main(String[] args) {
        Circle circle = new Circle(5.0);
        System.out.println("圆面积:" + circle.getArea());
    }
}

6.2 final方法:不能被子类重写

class Shape {
    // final方法:不能被子类重写
    final void displayType() {
        System.out.println("这是一个形状");
    }
    
    // 普通方法:可以被子类重写
    void draw() {
        System.out.println("绘制形状");
    }
}

class Square extends Shape {
    // ❌ 错误:不能重写final方法
    // @Override
    // void displayType() { }
    
    // ✓ 正确:可以重写非final方法
    @Override
    void draw() {
        System.out.println("绘制正方形");
    }
}

6.3 final变量:常量

class Constants {
    // final变量:常量,必须初始化且不能修改
    final double PI = 3.1415926535;
    final int MAX_SIZE;
    
    Constants() {
        MAX_SIZE = 100;  // 可以在构造方法中初始化
    }
    
    void test() {
        // PI = 3.14;  // ❌ 错误:不能修改final变量
        // MAX_SIZE = 200;  // ❌ 错误:不能修改final变量
    }
}

七、综合示例:完整的员工管理系统

import java.util.ArrayList;

// 基类:员工
class Employee {
    private String name;
    private int id;
    private double baseSalary;
    
    public Employee(String name, int id, double baseSalary) {
        this.name = name;
        this.id = id;
        this.baseSalary = baseSalary;
    }
    
    // 计算工资(基类中定义,子类可以重写)
    public double calculateSalary() {
        return baseSalary;
    }
    
    public void displayInfo() {
        System.out.println("员工:" + name + ",工号:" + id);
        System.out.println("基本工资:" + baseSalary + ",实发工资:" + calculateSalary());
    }
    
    // Getter方法
    public String getName() { return name; }
    public int getId() { return id; }
    public double getBaseSalary() { return baseSalary; }
}

// 子类1:普通员工
class RegularEmployee extends Employee {
    private int overtimeHours;  // 加班小时数
    private double overtimeRate = 50.0;  // 加班费率
    
    public RegularEmployee(String name, int id, double baseSalary, int overtimeHours) {
        super(name, id, baseSalary);
        this.overtimeHours = overtimeHours;
    }
    
    @Override
    public double calculateSalary() {
        // 工资 = 基本工资 + 加班费
        return super.getBaseSalary() + (overtimeHours * overtimeRate);
    }
    
    @Override
    public void displayInfo() {
        super.displayInfo();
        System.out.println("职位:普通员工,加班小时:" + overtimeHours + ",加班费:" + (overtimeHours * overtimeRate));
    }
}

// 子类2:经理
class Manager extends Employee {
    private double bonus;  // 奖金
    private String department;
    
    public Manager(String name, int id, double baseSalary, double bonus, String department) {
        super(name, id, baseSalary);
        this.bonus = bonus;
        this.department = department;
    }
    
    @Override
    public double calculateSalary() {
        // 工资 = 基本工资 + 奖金
        return super.getBaseSalary() + bonus;
    }
    
    @Override
    public void displayInfo() {
        super.displayInfo();
        System.out.println("职位:经理,部门:" + department + ",奖金:" + bonus);
    }
    
    // 经理特有方法
    public void manageTeam() {
        System.out.println(getName() + "正在管理" + department + "部门");
    }
}

// 子类3:销售员
class Salesperson extends Employee {
    private double salesAmount;  // 销售额
    private double commissionRate = 0.1;  // 提成比例
    
    public Salesperson(String name, int id, double baseSalary, double salesAmount) {
        super(name, id, baseSalary);
        this.salesAmount = salesAmount;
    }
    
    @Override
    public double calculateSalary() {
        // 工资 = 基本工资 + 销售提成
        return super.getBaseSalary() + (salesAmount * commissionRate);
    }
    
    @Override
    public void displayInfo() {
        super.displayInfo();
        System.out.println("职位:销售员,销售额:" + salesAmount + ",提成:" + (salesAmount * commissionRate));
    }
    
    // 销售员特有方法
    public void makeSale(double amount) {
        salesAmount += amount;
        System.out.println(getName() + "完成一笔销售,金额:" + amount);
    }
}

// 公司类:管理所有员工
class Company {
    private String companyName;
    private ArrayList<Employee> employees;
    
    public Company(String companyName) {
        this.companyName = companyName;
        this.employees = new ArrayList<>();
    }
    
    // 添加员工
    public void addEmployee(Employee employee) {
        employees.add(employee);
        System.out.println("添加员工:" + employee.getName());
    }
    
    // 显示所有员工信息
    public void displayAllEmployees() {
        System.out.println("\n=== " + companyName + " 员工列表 ===");
        for (Employee emp : employees) {
            emp.displayInfo();
            System.out.println("--------------------");
        }
    }
    
    // 计算总工资支出
    public double calculateTotalSalary() {
        double total = 0.0;
        for (Employee emp : employees) {
            total += emp.calculateSalary();
        }
        return total;
    }
    
    // 根据类型统计员工
    public void countEmployeesByType() {
        int regularCount = 0;
        int managerCount = 0;
        int salesCount = 0;
        
        for (Employee emp : employees) {
            if (emp instanceof RegularEmployee) {
                regularCount++;
            } else if (emp instanceof Manager) {
                managerCount++;
            } else if (emp instanceof Salesperson) {
                salesCount++;
            }
        }
        
        System.out.println("\n=== 员工类型统计 ===");
        System.out.println("普通员工:" + regularCount + "人");
        System.out.println("经理:" + managerCount + "人");
        System.out.println("销售员:" + salesCount + "人");
        System.out.println("总计:" + employees.size() + "人");
    }
}

public class EmployeeManagementSystem {
    public static void main(String[] args) {
        System.out.println("=== 员工管理系统 ===");
        
        // 创建公司
        Company company = new Company("科技公司");
        
        // 创建各种员工
        Employee emp1 = new RegularEmployee("张三", 1001, 5000.0, 10);
        Employee emp2 = new Manager("李四", 1002, 10000.0, 5000.0, "技术部");
        Employee emp3 = new Salesperson("王五", 1003, 4000.0, 50000.0);
        Employee emp4 = new RegularEmployee("赵六", 1004, 4500.0, 5);
        Employee emp5 = new Manager("钱七", 1005, 12000.0, 8000.0, "市场部");
        
        // 添加员工到公司
        company.addEmployee(emp1);
        company.addEmployee(emp2);
        company.addEmployee(emp3);
        company.addEmployee(emp4);
        company.addEmployee(emp5);
        
        // 显示所有员工信息
        company.displayAllEmployees();
        
        // 统计员工类型
        company.countEmployeesByType();
        
        // 计算总工资支出
        double totalSalary = company.calculateTotalSalary();
        System.out.println("\n=== 工资支出 ===");
        System.out.println("总工资支出:" + totalSalary + "元");
        
        // 测试多态
        System.out.println("\n=== 多态测试 ===");
        Employee emp = emp2;  // Manager对象赋给Employee引用
        emp.displayInfo();    // 调用的是Manager的displayInfo方法
        
        // 检查类型并调用特有方法
        if (emp instanceof Manager) {
            Manager mgr = (Manager) emp;  // 向下转型
            mgr.manageTeam();
        }
        
        // 销售员完成一笔销售
        if (emp3 instanceof Salesperson) {
            Salesperson sales = (Salesperson) emp3;
            sales.makeSale(10000.0);
            System.out.println("销售后工资:" + sales.calculateSalary());
        }
    }
}

八、总结

8.1 继承的优点

  1. 代码复用:子类可以重用父类的代码
  2. 易于维护:修改父类,所有子类自动受影响
  3. 多态基础:为多态提供支持
  4. 层次清晰:表达类之间的"是一种"关系

8.2 继承的局限性

  1. 单一继承:Java只支持单继承(一个类只能有一个父类)
  2. 紧耦合:子类与父类紧密关联,父类修改可能影响子类
  3. 不适当的继承:滥用继承会导致设计复杂

8.3 何时使用继承?

  1. 是一种(is-a)关系:狗是一种动物,汽车是一种交通工具
  2. 需要代码复用:多个类有共同的属性和方法
  3. 需要多态:需要通过父类引用操作不同子类对象

8.4 重要概念总结

  1. extends关键字:用于继承
  2. 方法重写:子类重新定义父类方法
  3. super关键字:访问父类成员
  4. final关键字:限制继承
  5. 访问修饰符:控制继承的可见性

8.5 快速记忆口诀

  1. 父类通用,子类特殊
  2. 继承用extends,重写加@Override
  3. super访问父类,this访问自己
  4. final防继承,protected护子类