面向对象的三大特性---多态
多态(Polymorphism)是面向对象编程的核心特性之一,简单来说就是父类的引用指向子类的对象。在有继承关系的对象中,同一个对象在不同时候可以展示出不同的状态和行为。这种特性让我们的程序更加灵活和可扩展。
多态可以分为两种时期:
- 运行期多态: 程序在运行时根据对象的实际类型来决定调用哪个方法
- 编译器多态: 在编译时就能确定调用哪个方法, 比如方法重载
我们平时说的方法重载其实是编译期多态的一种体现,编译器在编译时会根据参数类型和数量自动匹配对应的方法。
多态的分类
参数多态(泛型多态)
参数多态在Java中最典型的应用就是泛型。当我们使用List<String>
、Map<String, Integer>
这样的集合时,就是在使用参数多态。
java
// 参数多态的例子
List<String> stringList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
public <T> void processData(List<T> data) {
// 同一个方法可以处理不同类型的List
}
参数多态允许我们在声明函数、类或变量时不指定具体类型,而是把类型当作参数来使用。这样一个定义就可以适用于多种具体类型,大大提高了代码的复用性。
子类型多态
这是我们在面向对象编程中最常见的多态形式。当程序运行时,相同的方法调用可能会发送给不同类型的对象,系统会根据对象的实际类型来决定调用哪个具体的方法实现。
java
// 子类型多态示例
Animal animal1 = new Dog();
Animal animal2 = new Cat();
animal1.makeSound(); // 调用Dog的makeSound方法
animal2.makeSound(); // 调用Cat的makeSound方法
Java中的多态实现
在Java中, 多态的核心思想是"同一操作作用于不同的对象, 可以有不同的解释, 产生不同的执行结果"
多态的三个必要条件
要在Java中实现运行期多态, 需要满足三个条件:
- 有继承或接口实现关系: 存在父子类的继承关系, 或者接口的实现关系
- 子类重写父类的方法: 子类必须重写父类方法, 提供自己的具体实现
- 父类引用指向子类的对象: 使用父类类型的引用变量来指向子类的实例对象
代码示例
java
// 定义抽象父类
abstract class Shape {
abstract void draw();
abstract double calculateArea();
}
// 具体子类实现
class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
void draw() {
System.out.println("绘制圆形");
}
@Override
double calculateArea() {
return Math.PI * radius * radius;
}
}
class Rectangle extends Shape {
private double width, height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
void draw() {
System.out.println("绘制矩形");
}
@Override
double calculateArea() {
return width * height;
}
}
// 多态的应用
public class PolymorphismDemo {
public static void main(String[] args) {
// 父类引用指向不同的子类对象
Shape[] shapes = {
new Circle(5.0),
new Rectangle(4.0, 6.0),
new Circle(3.0)
};
// 同样的方法调用,产生不同的行为
for (Shape shape : shapes) {
shape.draw();
System.out.println("面积: " + shape.calculateArea());
}
}
}
静态多态与动态多态
静态多态(编译时多态)
静态多态在编译时就能确定具体调用哪个方法,主要包括:
方法重载(Overloading)
java
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
public int add(int a, int b, int c) {
return a + b + c;
}
}
编译器会根据参数的类型和数量来决定调用哪个重载方法,这个过程在编译时就完成了。
动态多态(运行时多态)
动态多态是在程序运行时才确定具体调用哪个方法,这就是我们前面提到的方法重写(Override)。
java
class PaymentProcessor {
public void processPayment(Payment payment) {
payment.pay(); // 运行时才知道调用哪个具体的pay方法
}
}
interface Payment {
void pay();
}
class CreditCardPayment implements Payment {
@Override
public void pay() {
System.out.println("使用信用卡支付");
}
}
class AlipayPayment implements Payment {
@Override
public void pay() {
System.out.println("使用支付宝支付");
}
}
多态的优势
- 提高代码的可扩展性:新增子类时,无需修改使用父类引用的代码
- 降低代码耦合度:客户端代码只依赖于抽象接口,不依赖具体实现
- 增强代码的灵活性:同一段代码可以处理多种不同类型的对象
- 符合开闭原则:对扩展开放,对修改关闭
实际应用场景
多态在实际开发中非常常见,比如:
- 数据库操作:同一个DAO接口可以有MySQL、Oracle等不同的实现
- 消息处理:不同类型的消息可以有不同的处理方式
- UI组件:不同的UI控件可以响应相同的事件,但有不同的行为
- 策略模式:同一个策略接口可以有多种不同的算法实现
多态让我们的程序更加模块化和可维护,是写出高质量Java代码的重要技能。掌握好多态,你就掌握了面向对象编程的精髓。