深度解析Java的多态特性

  • 引言:为什么需要多态?
  • 什么是多态?
  • 多态的实现条件
    • [1. 继承关系(或接口实现)](#1. 继承关系(或接口实现))
    • [2. 方法重写(Override)](#2. 方法重写(Override))
    • [3. 父类引用指向子类对象](#3. 父类引用指向子类对象)
  • 多态的实现原理
    • 方法调用的过程
    • [虚方法表(Virtual Method Table)](#虚方法表(Virtual Method Table))
  • 多态的两种形式(编译时vs运行时)
    • [1. 编译时多态(静态多态)------方法重载](#1. 编译时多态(静态多态)——方法重载)
    • [2. 运行时多态(动态多态)------方法重写](#2. 运行时多态(动态多态)——方法重写)
  • 多态的优势
    • [1. 提高代码的可扩展性和灵活性](#1. 提高代码的可扩展性和灵活性)
    • [2. 降低代码耦合度(面向接口编程)](#2. 降低代码耦合度(面向接口编程))
    • [3. 提高代码的可维护性](#3. 提高代码的可维护性)
    • [4. 实现代码复用](#4. 实现代码复用)
  • 多态的注意事项
    • [1. 向上转型和向下转型详解](#1. 向上转型和向下转型详解)
    • [2. 多态与成员变量](#2. 多态与成员变量)
    • [3. 静态方法不支持多态](#3. 静态方法不支持多态)
    • [4. 私有方法、final方法不能被重写](#4. 私有方法、final方法不能被重写)
    • [5. 构造方法不能被重写](#5. 构造方法不能被重写)
  • 多态与设计原则
    • [1. 开闭原则(Open-Closed Principle)](#1. 开闭原则(Open-Closed Principle))
    • [2. 里氏替换原则](#2. 里氏替换原则)
    • [3. 依赖倒置原则](#3. 依赖倒置原则)
  • 多态的核心要点
  • 结语

引言:为什么需要多态?

在深入了解多态之前,让我们先思考一个实际问题:

假设你正在开发一个绘图应用,需要绘制不同的图形(圆形、矩形、三角形等)。如果没有多态,你可能需要这样写代码:

java 复制代码
public void drawShape(String shapeType) {
    if (shapeType.equals("circle")) {
        // 绘制圆形的代码
    } else if (shapeType.equals("rectangle")) {
        // 绘制矩形的代码
    } else if (shapeType.equals("triangle")) {
        // 绘制三角形的代码
    }
    // 每次添加新图形,都要修改这个方法
}

这种方式导致的问题显而易见:

  • 代码臃肿,充满if-else判断
  • 每次新增图形类型都要修改原有代码
  • 难以维护,容易出错
  • 违反了开闭原则

而有了多态,代码会变得优雅简洁:

java 复制代码
public void drawShape(Shape shape) {
    shape.draw();  // 就这么简单!
}

这就是多态的魅力所在。

什么是多态?

基本概念

多态(Polymorphism)源自希腊语,poly意为"多",morph意为"形态"。在Java中,多态是指同一个行为具有多个不同表现形式或形态的能力

生活中的多态

在深入代码之前,让我们先跳出编程的世界,看看身边的例子,其实"多态"在我们的日常生活中随处可见。比如"按钮"的多态:电视遥控器上的"开关"按钮用于控制电视的开关,电灯的开关按钮用于控制灯的开关,而汽车的启动按钮则用于启动汽车。虽然都是"按钮按下"这个相同的动作,但因为对象不同,产生的效果却完全不同。

编程中的多态本质

从技术角度看,多态的本质是:

  • 编译时:编译器只知道引用类型(父类型),确保调用的方法在父类中存在
  • 运行时:JVM根据实际对象类型(子类型)决定执行哪个具体的方法实现

这个过程称为动态绑定后期绑定

多态的实现条件

1. 继承关系(或接口实现)

java 复制代码
// 通过继承实现
class Animal {
    public void makeSound() {
        System.out.println("动物发出声音");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("汪汪汪!");
    }
}


// 通过接口实现
interface Flyable {
    void fly();
}

class Bird implements Flyable {
    @Override
    public void fly() {
        System.out.println("鸟在飞翔");
    }
}

class Airplane implements Flyable {
    @Override
    public void fly() {
        System.out.println("飞机在飞行");
    }
}

为什么需要继承关系?

继承的存在是为了建立一种"is-a"的关系,使子类对象能够被当作父类对象使用,同时它还为方法提供了基础定义,让子类在此基础上进行重写,从而实现更灵活的功能扩展。

2. 方法重写(Override)

方法重写必须满足以下规则:

java 复制代码
class Parent {
    // 返回类型、方法名、参数列表
    public Number calculate(int a, int b) {
        return a + b;
    }
    
    protected void display() {
        System.out.println("父类显示");
    }
}

class Child extends Parent {
    // 正确:返回类型可以是父类方法返回类型的子类(协变返回类型)
    @Override
    public Integer calculate(int a, int b) {
        return a * b;
    }
    
    // 正确:访问修饰符可以更宽松(protected → public)
    @Override
    public void display() {
        System.out.println("子类显示");
    }
    
    // 错误示例:
    // @Override
    // private void display() { }  // 访问权限不能更严格
    
    // @Override
    // public String calculate(int a, int b) { }  // 返回类型不兼容
}

方法重写的关键点

  • 方法签名必须完全相同(方法名 + 参数列表)
  • 返回类型必须相同或是子类型(协变返回类型)
  • 访问修饰符不能更严格(可以更宽松)
  • 不能抛出新的或更广泛的检查异常

3. 父类引用指向子类对象

java 复制代码
// 这是多态的核心语法
Parent reference = new Child();

// 解析:
// Parent:引用变量的类型(编译时类型)
// reference:引用变量名
// new Child():实际创建的对象(运行时类型)

多态的实现原理

方法调用的过程

java 复制代码
class Animal {
    public void eat() {
        System.out.println("动物在吃东西");
    }
}

class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗在吃骨头");
    }
}

public class Test {
    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.eat();  // 这行代码发生了什么?
    }
}

执行步骤

  1. 编译阶段

    • 编译器检查Animal类中是否有eat()方法
    • 编译器检查方法的访问权限
    • 编译通过
  2. 运行阶段

    • JVM查看animal引用实际指向的对象类型(Dog)
    • 在Dog类的方法表中查找eat()方法
    • 找到了Dog重写的eat()方法
    • 执行Dog的eat()方法 → 输出"狗在吃骨头"

虚方法表(Virtual Method Table)

每个类都有一个虚方法表,存储着该类所有可以被调用的方法:

当调用animal.eat()时,JVM会:

  1. 获取animal实际指向的对象(Dog对象)
  2. 查找Dog的虚方法表
  3. 找到eat()对应的方法地址
  4. 执行该方法

多态的两种形式(编译时vs运行时)

1. 编译时多态(静态多态)------方法重载

java 复制代码
class MathOperations {
    // 方法重载:方法名相同,参数不同
    
    // 计算两个整数的和
    public int add(int a, int b) {
        System.out.println("调用 add(int, int)");
        return a + b;
    }
    
    // 计算三个整数的和
    public int add(int a, int b, int c) {
        System.out.println("调用 add(int, int, int)");
        return a + b + c;
    }
    
    // 计算两个浮点数的和
    public double add(double a, double b) {
        System.out.println("调用 add(double, double)");
        return a + b;
    }
    
    // 字符串拼接
    public String add(String a, String b) {
        System.out.println("调用 add(String, String)");
        return a + b;
    }
}

public class OverloadingDemo {
    public static void main(String[] args) {
        MathOperations math = new MathOperations();
        
        // 编译器根据参数类型和数量决定调用哪个方法
        System.out.println(math.add(5, 3));              // 调用第一个
        System.out.println(math.add(5, 3, 2));           // 调用第二个
        System.out.println(math.add(5.5, 3.2));          // 调用第三个
        System.out.println(math.add("Hello", "World"));  // 调用第四个
    }
}

输出结果

方法重载的规则

  • 必须在同一个类中(或父类子类之间)
  • 方法名必须相同
  • 参数列表必须不同(类型、个数或顺序)
  • 返回类型可以相同也可以不同
  • 访问修饰符可以不同

方法重载与多态的关系

  • 方法重载是编译时确定的,不是真正的多态
  • 但它体现了"同一个方法名,不同行为"的多态思想
  • 通常称为"静态多态"或"编译时多态"

2. 运行时多态(动态多态)------方法重写

java 复制代码
// 抽象的图形类
abstract class Shape {
    protected String color;
    
    public Shape(String color) {
        this.color = color;
    }
    
    // 抽象方法:计算面积
    public abstract double calculateArea();
    
    // 抽象方法:绘制图形
    public abstract void draw();
    
    // 具体方法
    public void displayColor() {
        System.out.println("颜色:" + color);
    }
}

// 圆形
class Circle extends Shape {
    private double radius;
    
    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }
    
    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
    
    @Override
    public void draw() {
        System.out.println("绘制一个半径为 " + radius + " 的圆形");
    }
}

// 矩形
class Rectangle extends Shape {
    private double width;
    private double height;
    
    public Rectangle(String color, double width, double height) {
        super(color);
        this.width = width;
        this.height = height;
    }
    
    @Override
    public double calculateArea() {
        return width * height;
    }
    
    @Override
    public void draw() {
        System.out.println("绘制一个 " + width + "x" + height + " 的矩形");
    }
}

// 三角形
class Triangle extends Shape {
    private double base;
    private double height;
    
    public Triangle(String color, double base, double height) {
        super(color);
        this.base = base;
        this.height = height;
    }
    
    @Override
    public double calculateArea() {
        return 0.5 * base * height;
    }
    
    @Override
    public void draw() {
        System.out.println("绘制一个底边为 " + base + ",高为 " + height + " 的三角形");
    }
}

// 测试运行时多态
public class RuntimePolymorphismDemo {
    public static void main(String[] args) {
        // 创建图形数组,体现多态
        Shape[] shapes = {
            new Circle("红色", 5.0),
            new Rectangle("蓝色", 4.0, 6.0),
            new Triangle("绿色", 3.0, 4.0),
            new Circle("黄色", 3.0)
        };
        
        System.out.println("--- 绘制所有图形 ---");
        for (Shape shape : shapes) {
            shape.displayColor();           // 调用父类方法
            shape.draw();                   // 多态:调用各自重写的方法
            System.out.println("面积:" + 
                String.format("%.2f", shape.calculateArea()));  // 多态
            System.out.println("-------------------");
        }
        
        // 计算总面积
        double totalArea = calculateTotalArea(shapes);
        System.out.println("所有图形的总面积:" + String.format("%.2f", totalArea));
    }
    
    // 这个方法不需要知道具体的图形类型,展现了多态的威力
    public static double calculateTotalArea(Shape[] shapes) {
        double total = 0;
        for (Shape shape : shapes) {
            total += shape.calculateArea();  // 多态调用
        }
        return total;
    }
}

输出结果

多态的优势

1. 提高代码的可扩展性和灵活性

java 复制代码
// 没有多态的代码(反面示例)
class AnimalManagerBad {
    public void feedDog(Dog dog) {
        System.out.println("喂狗");
        dog.eat();
    }
    
    public void feedCat(Cat cat) {
        System.out.println("喂猫");
        cat.eat();
    }
    
    public void feedBird(Bird bird) {
        System.out.println("喂鸟");
        bird.eat();
    }
    
    // 每增加一种动物,就要添加一个新方法
}

// 使用多态的代码(正面示例)
class AnimalManagerGood {
    // 一个方法处理所有动物
    public void feedAnimal(Animal animal) {
        System.out.println("喂养动物");
        animal.eat();  // 多态调用
    }
    
    // 批量喂养
    public void feedAll(List<Animal> animals) {
        for (Animal animal : animals) {
            feedAnimal(animal);
        }
    }
}

// 测试
public class ExtensibilityDemo {
    public static void main(String[] args) {
        AnimalManagerGood manager = new AnimalManagerGood();
        
        List<Animal> animals = Arrays.asList(
            new Dog(),
            new Cat(),
            new Bird()
        );
        
        manager.feedAll(animals);
        
        // 就算将来新增一个 Rabbit 类,也无需修改 AnimalManagerGood 的任何代码
        // animals.add(new Rabbit());
    }
}

2. 降低代码耦合度(面向接口编程)

java 复制代码
// 定义支付接口
interface PaymentProcessor {
    boolean processPayment(double amount);
    String getPaymentMethod();
}

// 信用卡支付
class CreditCardPayment implements PaymentProcessor {
    private String cardNumber;
    
    public CreditCardPayment(String cardNumber) {
        this.cardNumber = cardNumber;
    }
    
    @Override
    public boolean processPayment(double amount) {
        System.out.println("通过信用卡 " + maskCardNumber() + " 支付 $" + amount);
        // 实际的支付逻辑...
        return true;
    }
    
    @Override
    public String getPaymentMethod() {
        return "Credit Card";
    }
    
    private String maskCardNumber() {
        return "**** **** **** " + cardNumber.substring(cardNumber.length() - 4);
    }
}

// PayPal支付
class PayPalPayment implements PaymentProcessor {
    private String email;
    
    public PayPalPayment(String email) {
        this.email = email;
    }
    
    @Override
    public boolean processPayment(double amount) {
        System.out.println("通过PayPal账户 " + email + " 支付 $" + amount);
        // 实际的支付逻辑...
        return true;
    }
    
    @Override
    public String getPaymentMethod() {
        return "PayPal";
    }
}

// 加密货币支付
class CryptoPayment implements PaymentProcessor {
    private String walletAddress;
    private String cryptoType;
    
    public CryptoPayment(String walletAddress, String cryptoType) {
        this.walletAddress = walletAddress;
        this.cryptoType = cryptoType;
    }
    
    @Override
    public boolean processPayment(double amount) {
        System.out.println("通过" + cryptoType + "钱包支付 $" + amount);
        System.out.println("钱包地址:" + walletAddress.substring(0, 10) + "...");
        // 实际的支付逻辑...
        return true;
    }
    
    @Override
    public String getPaymentMethod() {
        return cryptoType;
    }
}

// 订单类 - 不依赖具体的支付方式
class Order {
    private String orderId;
    private double amount;
    private List<String> items;
    
    public Order(String orderId, double amount, List<String> items) {
        this.orderId = orderId;
        this.amount = amount;
        this.items = items;
    }
    
    // 关键方法:接受任何支付方式
    public void checkout(PaymentProcessor paymentProcessor) {
        System.out.println("\n==== 订单详情 ====");
        System.out.println("订单号:" + orderId);
        System.out.println("商品:" + items);
        System.out.println("金额:$" + amount);
        System.out.println("支付方式:" + paymentProcessor.getPaymentMethod());
        
        if (paymentProcessor.processPayment(amount)) {
            System.out.println("支付成功!");
        } else {
            System.out.println("支付失败!");
        }
    }
}

// 测试
public class CouplingDemo {
    public static void main(String[] args) {
        Order order1 = new Order("ORD001", 199.99, 
            Arrays.asList("笔记本电脑", "鼠标"));
        Order order2 = new Order("ORD002", 59.99, 
            Arrays.asList("键盘"));
        Order order3 = new Order("ORD003", 299.99, 
            Arrays.asList("显示器"));
        
        // 使用不同的支付方式,Order类完全不需要修改
        order1.checkout(new CreditCardPayment("1234567890123456"));
        order2.checkout(new PayPalPayment("user@example.com"));
        order3.checkout(new CryptoPayment("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "Bitcoin"));
        
        // 将来新增Apple Pay、Google Pay等,Order类依然不需要修改
    }
}

3. 提高代码的可维护性

java 复制代码
// 日志系统示例
interface Logger {
    void log(String message);
    void logError(String message);
}

class ConsoleLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("[INFO] " + getCurrentTime() + ": " + message);
    }
    
    @Override
    public void logError(String message) {
        System.err.println("[ERROR] " + getCurrentTime() + ": " + message);
    }
    
    private String getCurrentTime() {
        return java.time.LocalDateTime.now().format(
            java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
        );
    }
}

class FileLogger implements Logger {
    private String filePath;
    
    public FileLogger(String filePath) {
        this.filePath = filePath;
    }
    
    @Override
    public void log(String message) {
        writeToFile("[INFO] " + message);
    }
    
    @Override
    public void logError(String message) {
        writeToFile("[ERROR] " + message);
    }
    
    private void writeToFile(String message) {
        System.out.println("写入文件 " + filePath + ": " + message);
        // 实际的文件写入逻辑...
    }
}

// 应用程序类
class Application {
    private Logger logger;  // 依赖接口,不依赖具体实现
    
    public Application(Logger logger) {
        this.logger = logger;
    }
    
    public void run() {
        logger.log("应用程序启动");
        
        try {
            processData();
            logger.log("数据处理完成");
        } catch (Exception e) {
            logger.logError("处理数据时出错:" + e.getMessage());
        }
    }
    
    private void processData() {
        // 数据处理逻辑...
        logger.log("正在处理数据...");
    }
}

// 测试
public class MaintenanceDemo {
    public static void main(String[] args) {
        // 开发环境使用控制台日志
        Application devApp = new Application(new ConsoleLogger());
        devApp.run();
        
        System.out.println("\n---切换到生产环境---\n");
        
        // 生产环境切换到文件日志,无需修改Application类
        Application prodApp = new Application(new FileLogger("/var/log/app.log"));
        prodApp.run();
    }
}

4. 实现代码复用

java 复制代码
// 集合操作工具类
class CollectionProcessor {
    // 通用的过滤方法 - 可以处理任何类型的集合
    public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
        List<T> result = new ArrayList<>();
        for (T item : list) {
            if (predicate.test(item)) {
                result.add(item);
            }
        }
        return result;
    }
    
    // 通用的打印方法
    public static <T> void printAll(List<T> list) {
        for (T item : list) {
            System.out.println(item);  // 多态调用toString()
        }
    }
}

// 使用示例
interface Predicate<T> {
    boolean test(T t);
}

public class ReuseDemo {
    public static void main(String[] args) {
        // 处理整数列表
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        List<Integer> evenNumbers = CollectionProcessor.filter(numbers, 
            n -> n % 2 == 0);
        System.out.println("偶数:");
        CollectionProcessor.printAll(evenNumbers);
        
        // 处理字符串列表(同样的代码,不同的类型)
        List<String> words = Arrays.asList("apple", "banana", "cherry", "date");
        List<String> longWords = CollectionProcessor.filter(words, 
            w -> w.length() > 5);
        System.out.println("\n长单词:");
        CollectionProcessor.printAll(longWords);
    }
}

多态的注意事项

1. 向上转型和向下转型详解

java 复制代码
class Animal {
    public void eat() {
        System.out.println("动物在吃东西");
    }
    
    public void sleep() {
        System.out.println("动物在睡觉");
    }
}

class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗在吃骨头");
    }
    
    // Dog特有的方法
    public void bark() {
        System.out.println("汪汪汪!");
    }
    
    public void wagTail() {
        System.out.println("狗摇尾巴");
    }
}

class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫在吃鱼");
    }
    
    public void meow() {
        System.out.println("喵喵喵!");
    }
}

public class CastingDemo {
    public static void main(String[] args) {
        // ===== 向上转型(Upcasting) =====
        // 自动完成,总是安全的
        Animal animal1 = new Dog();  // Dog → Animal(向上转型)
        animal1.eat();     // 可以调用,输出:狗在吃骨头(多态)
        animal1.sleep();   // 可以调用
        // animal1.bark();  // 编译错误!父类引用看不到子类特有方法
        
        System.out.println("-------------------");
        
        // ===== 向下转型(Downcasting) =====
        // 需要强制转换,可能不安全
        
        // 示例1:正确的向下转型
        Animal animal2 = new Dog();  // 实际是Dog对象
        
        // 使用instanceof检查类型(推荐做法)
        if (animal2 instanceof Dog) {
            Dog dog = (Dog) animal2;  // 向下转型
            dog.bark();   // 现在可以调用子类特有方法了
            dog.wagTail(); 
        }
        
        System.out.println("-------------------");
        
        // 示例2:错误的向下转型
        Animal animal3 = new Cat();  // 实际是Cat对象
        
        // 不检查就强转会导致ClassCastException
        try {
            Dog dog2 = (Dog) animal3;  // 运行时错误!
            dog2.bark();
        } catch (ClassCastException e) {
            System.out.println("捕获异常:" + e.getMessage());
            System.out.println("不能将Cat转换为Dog!");
        }
        
        System.out.println("-------------------");
        
        // 示例3:使用instanceof的完整示例
        Animal[] animals = {new Dog(), new Cat(), new Dog(), new Cat()};
        
        for (Animal animal : animals) {
            animal.eat();  // 多态调用
            
            // 根据实际类型调用特有方法
            if (animal instanceof Dog) {
                ((Dog) animal).bark();
            } else if (animal instanceof Cat) {
                ((Cat) animal).meow();
            }
            System.out.println("---");
        }
    }
}

输出结果

2. 多态与成员变量

java 复制代码
class Parent {
    int value = 100;
    
    public void showValue() {
        System.out.println("方法中的value: " + value);
    }
}

class Child extends Parent {
    int value = 200;  // 隐藏父类的value(不是重写!)
    
    @Override
    public void showValue() {
        System.out.println("方法中的value: " + value);
    }
    
    public void showBothValues() {
        System.out.println("子类的value: " + value);
        System.out.println("父类的value: " + super.value);
    }
}

public class FieldPolymorphismDemo {
    public static void main(String[] args) {
        Parent obj = new Child();
        
        // 成员变量不支持多态!
        System.out.println("obj.value = " + obj.value);  // 输出100(父类的值)
        
        // 方法支持多态
        obj.showValue();  // 输出200(调用子类的方法)
        
        System.out.println("-------------------");
        
        // 验证
        Child child = new Child();
        child.showBothValues();
        
        System.out.println("-------------------");
        
        // 解释
        System.out.println("=== 为什么会这样?===");
        System.out.println("成员变量的访问:在编译时就确定了,看引用类型");
        System.out.println("方法的调用:在运行时才确定,看实际对象类型");
        System.out.println("\n结论:");
        System.out.println("方法支持多态(动态绑定)");
        System.out.println("成员变量不支持多态(静态绑定)");
    }
}

输出结果

3. 静态方法不支持多态

java 复制代码
class Animal {
    // 静态方法
    public static void staticMethod() {
        System.out.println("Animal的静态方法");
    }
    
    // 实例方法
    public void instanceMethod() {
        System.out.println("Animal的实例方法");
    }
}

class Dog extends Animal {
    // 这不是重写,而是隐藏父类的静态方法
    public static void staticMethod() {
        System.out.println("Dog的静态方法");
    }
    
    // 这是真正的重写
    @Override
    public void instanceMethod() {
        System.out.println("Dog的实例方法");
    }
}

public class StaticMethodDemo {
    public static void main(String[] args) {
        Animal animal1 = new Dog();
        Animal animal2 = new Animal();
        Dog dog = new Dog();
        
        System.out.println("=== 静态方法调用(不支持多态)===");
        animal1.staticMethod();  // 输出:Animal的静态方法(看引用类型)
        animal2.staticMethod();  // 输出:Animal的静态方法
        dog.staticMethod();      // 输出:Dog的静态方法
        
        // 推荐的静态方法调用方式
        Animal.staticMethod();   // 输出:Animal的静态方法
        Dog.staticMethod();      // 输出:Dog的静态方法
        
        System.out.println("\n=== 实例方法调用(支持多态)===");
        animal1.instanceMethod();  // 输出:Dog的实例方法(看对象类型)
        animal2.instanceMethod();  // 输出:Animal的实例方法
        dog.instanceMethod();      // 输出:Dog的实例方法
    }
}

输出结果

4. 私有方法、final方法不能被重写

java 复制代码
class Base {
    // private方法不能被重写
    private void privateMethod() {
        System.out.println("Base的私有方法");
    }
    
    // final方法不能被重写
    public final void finalMethod() {
        System.out.println("Base的final方法");
    }
    
    // 普通方法可以被重写
    public void normalMethod() {
        System.out.println("Base的普通方法");
    }
    
    public void callPrivateMethod() {
        privateMethod();  // 在类内部可以调用
    }
}

class Derived extends Base {
    // 这不是重写,只是定义了一个新方法(因为父类的privateMethod对子类不可见)
    private void privateMethod() {
        System.out.println("Derived的私有方法");
    }
    
    // 编译错误:不能重写final方法
    // public void finalMethod() {
    //     System.out.println("尝试重写final方法");
    // }
    
    // 正确:可以重写普通方法
    @Override
    public void normalMethod() {
        System.out.println("Derived的普通方法");
    }
}

public class FinalPrivateDemo {
    public static void main(String[] args) {
        Base base = new Derived();
        
        base.callPrivateMethod();  // 调用的是Base的私有方法
        base.finalMethod();        // 调用的是Base的final方法
        base.normalMethod();       // 多态调用Derived的方法
    }
}

输出结果

5. 构造方法不能被重写

java 复制代码
class Parent {
    private String name;
    
    // 构造方法
    public Parent() {
        System.out.println("Parent无参构造方法");
    }
    
    public Parent(String name) {
        this.name = name;
        System.out.println("Parent有参构造方法: " + name);
    }
}

class Child extends Parent {
    private int age;
    
    // 这不是重写,而是定义子类自己的构造方法
    public Child() {
        super();  // 调用父类构造方法
        System.out.println("Child无参构造方法");
    }
    
    public Child(String name, int age) {
        super(name);  // 调用父类构造方法
        this.age = age;
        System.out.println("Child有参构造方法: age=" + age);
    }
}

public class ConstructorDemo {
    public static void main(String[] args) {
        System.out.println("创建Child对象:");
        Child child = new Child("张三", 20);
        
        System.out.println("\n说明:");
        System.out.println("- 构造方法不能被重写");
        System.out.println("- 子类构造方法会自动调用父类构造方法");
        System.out.println("- 如果父类没有无参构造方法,子类必须显式调用父类的有参构造方法");
    }
}

输出结果

多态与设计原则

1. 开闭原则(Open-Closed Principle)

定义:软件实体应该对扩展开放,对修改关闭。

java 复制代码
// 违反开闭原则的设计
class BadDiscountCalculator {
    public double calculateDiscount(String customerType, double amount) {
        if (customerType.equals("Regular")) {
            return amount * 0.95;  // 95折
        } else if (customerType.equals("VIP")) {
            return amount * 0.90;  // 9折
        } else if (customerType.equals("SVIP")) {
            return amount * 0.85;  // 85折
        }
        // 添加新类型需要修改这个方法
        return amount;
    }
}

// 遵循开闭原则的设计
interface DiscountStrategy {
    double applyDiscount(double amount);
    String getCustomerType();
}

class RegularCustomerDiscount implements DiscountStrategy {
    @Override
    public double applyDiscount(double amount) {
        return amount * 0.95;
    }
    
    @Override
    public String getCustomerType() {
        return "普通客户";
    }
}

class VIPCustomerDiscount implements DiscountStrategy {
    @Override
    public double applyDiscount(double amount) {
        return amount * 0.90;
    }
    
    @Override
    public String getCustomerType() {
        return "VIP客户";
    }
}

class SVIPCustomerDiscount implements DiscountStrategy {
    @Override
    public double applyDiscount(double amount) {
        return amount * 0.85;
    }
    
    @Override
    public String getCustomerType() {
        return "SVIP客户";
    }
}

// 添加新类型不需要修改原有代码
class PlatinumCustomerDiscount implements DiscountStrategy {
    @Override
    public double applyDiscount(double amount) {
        return amount * 0.80;
    }
    
    @Override
    public String getCustomerType() {
        return "白金客户";
    }
}

class GoodDiscountCalculator {
    private DiscountStrategy strategy;
    
    public GoodDiscountCalculator(DiscountStrategy strategy) {
        this.strategy = strategy;
    }
    
    public double calculateFinalPrice(double amount) {
        return strategy.applyDiscount(amount);
    }
}

2. 里氏替换原则

定义:子类对象应该能够替换父类对象,而不影响程序的正确性。

java 复制代码
// 遵循里氏替换原则
class Rectangle {
    protected double width;
    protected double height;
    
    public void setWidth(double width) {
        this.width = width;
    }
    
    public void setHeight(double height) {
        this.height = height;
    }
    
    public double getArea() {
        return width * height;
    }
}

// 正方形不应该继承矩形(违反里氏替换原则)
// class Square extends Rectangle { ... }  

// 正确的做法:使用接口
interface Shape {
    double getArea();
    double getPerimeter();
}

class GoodRectangle implements Shape {
    private double width;
    private double height;
    
    public GoodRectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    @Override
    public double getArea() {
        return width * height;
    }
    
    @Override
    public double getPerimeter() {
        return 2 * (width + height);
    }
}

class GoodSquare implements Shape {
    private double side;
    
    public GoodSquare(double side) {
        this.side = side;
    }
    
    @Override
    public double getArea() {
        return side * side;
    }
    
    @Override
    public double getPerimeter() {
        return 4 * side;
    }
}

3. 依赖倒置原则

定义:高层模块不应该依赖低层模块,两者都应该依赖抽象。

java 复制代码
// 违反依赖倒置原则
class BadMySQLDatabase {
    public void connect() {
        System.out.println("连接到MySQL数据库");
    }
    
    public void query(String sql) {
        System.out.println("执行MySQL查询:" + sql);
    }
}

class BadUserService {
    private BadMySQLDatabase database = new BadMySQLDatabase();  // 依赖具体类
    
    public void getUser(int id) {
        database.connect();
        database.query("SELECT * FROM users WHERE id = " + id);
    }
}

// 遵循依赖倒置原则
interface Database {
    void connect();
    void query(String sql);
    void disconnect();
}

class MySQLDatabase implements Database {
    @Override
    public void connect() {
        System.out.println("连接到MySQL数据库");
    }
    
    @Override
    public void query(String sql) {
        System.out.println("执行MySQL查询:" + sql);
    }
    
    @Override
    public void disconnect() {
        System.out.println("断开MySQL连接");
    }
}

class PostgreSQLDatabase implements Database {
    @Override
    public void connect() {
        System.out.println("连接到PostgreSQL数据库");
    }
    
    @Override
    public void query(String sql) {
        System.out.println("执行PostgreSQL查询:" + sql);
    }
    
    @Override
    public void disconnect() {
        System.out.println("断开PostgreSQL连接");
    }
}

class GoodUserService {
    private Database database;  // 依赖抽象
    
    public GoodUserService(Database database) {
        this.database = database;
    }
    
    public void getUser(int id) {
        database.connect();
        database.query("SELECT * FROM users WHERE id = " + id);
        database.disconnect();
    }
}

// 测试
public class DIPDemo {
    public static void main(String[] args) {
        // 可以轻松切换数据库实现
        GoodUserService service1 = new GoodUserService(new MySQLDatabase());
        service1.getUser(1);
        
        System.out.println("\n切换到PostgreSQL:\n");
        
        GoodUserService service2 = new GoodUserService(new PostgreSQLDatabase());
        service2.getUser(1);
    }
}

多态的核心要点

  1. 三个必要条件

    • 继承关系(或接口实现)
    • 方法重写
    • 父类引用指向子类对象
  2. 两种形式

    • 编译时多态(方法重载)
    • 运行时多态(方法重写)
  3. 关键限制

    • 成员变量不支持多态
    • 静态方法不支持多态
    • private和final方法不能被重写
    • 构造方法不能被重写
  4. 主要优势

    • 提高代码的可扩展性
    • 降低代码耦合度
    • 提高代码的可维护性
    • 实现代码复用

结语

多态是面向对象编程的精髓,掌握多态不仅能写出更优雅的代码,还能让你更好地理解和使用Java的各种框架和设计模式。希望这篇文章能帮助你全面理解Java多态特性!

相关推荐
csbysj20202 小时前
DTD 元素:XML 与 SGML 文档结构解析指南
开发语言
傻童:CPU3 小时前
C语言练习题
c语言·开发语言
华仔啊3 小时前
JVM参数到底配在哪?7大场景全解,新手不再迷茫!
java·jvm
极地星光3 小时前
协程:实战与系统集成(高级篇)
开发语言
0和1的舞者3 小时前
《Git:从入门到精通(八)——企业级git开发相关内容》
大数据·开发语言·git·搜索引擎·全文检索·软件工程·初学者
liulilittle3 小时前
LwIP协议栈MPA多进程架构
服务器·开发语言·网络·c++·架构·lwip·通信
水淹萌龙4 小时前
玩转 Go 表达式引擎:expr 实战指南
开发语言·后端·golang
艾莉丝努力练剑4 小时前
【C++:继承】面向对象编程精要:C++继承机制深度解析与最佳实践
开发语言·c++·人工智能·继承·c++进阶
penguin_bark4 小时前
C++ 异步编程(future、promise、packaged_task、async)
java·开发语言·c++