- 引言:为什么需要多态?
- 什么是多态?
- 多态的实现条件
-
- [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(); // 这行代码发生了什么?
}
}
执行步骤:
-
编译阶段:
- 编译器检查Animal类中是否有eat()方法
- 编译器检查方法的访问权限
- 编译通过
-
运行阶段:
- JVM查看animal引用实际指向的对象类型(Dog)
- 在Dog类的方法表中查找eat()方法
- 找到了Dog重写的eat()方法
- 执行Dog的eat()方法 → 输出"狗在吃骨头"
虚方法表(Virtual Method Table)
每个类都有一个虚方法表,存储着该类所有可以被调用的方法:

当调用animal.eat()时,JVM会:
- 获取animal实际指向的对象(Dog对象)
- 查找Dog的虚方法表
- 找到eat()对应的方法地址
- 执行该方法
多态的两种形式(编译时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);
}
}
多态的核心要点
-
三个必要条件:
- 继承关系(或接口实现)
- 方法重写
- 父类引用指向子类对象
-
两种形式:
- 编译时多态(方法重载)
- 运行时多态(方法重写)
-
关键限制:
- 成员变量不支持多态
- 静态方法不支持多态
- private和final方法不能被重写
- 构造方法不能被重写
-
主要优势:
- 提高代码的可扩展性
- 降低代码耦合度
- 提高代码的可维护性
- 实现代码复用
结语
多态是面向对象编程的精髓,掌握多态不仅能写出更优雅的代码,还能让你更好地理解和使用Java的各种框架和设计模式。希望这篇文章能帮助你全面理解Java多态特性!