多态是面向对象编程(OOP)中的一个核心概念,指的是一个对象可以呈现出多种形式的能力。在 Java 中,多态允许我们通过同一个方法名执行多种不同的操作。
任何能够通过多个 IS-A 测试的 Java 对象都被认为是多态的。由于所有 Java 对象都继承自 Object 类,因此每个 Java 对象至少可以通过两个 IS-A 测试:它自身的类型和 Object 类型,所以所有 Java 对象本质上都是多态的。
Java 中多态的使用
在面向对象编程中,多态最常见的用法是:使用父类的引用变量来引用子类的对象。
需要记住以下几点:
- 访问对象的唯一方式是通过引用变量。
- 引用变量只能有一种类型,一旦声明后,其类型不能更改。
- 如果引用变量未被声明为
final,它可以被重新赋值为其他对象。 - 引用变量的类型决定了它可以调用哪些方法。
- 引用变量可以指向其声明类型或其任何子类型的对象。
- 引用变量可以声明为类类型或接口类型。
Java 多态示例
考虑以下代码:
public interface Vegetarian {}
public class Animal {}
public class Deer extends Animal implements Vegetarian {}
此时,Deer 类被认为是多态的,因为它满足多重“IS-A”关系:
DeerIS-AAnimalDeerIS-AVegetarianDeerIS-ADeerDeerIS-AObject
因此,以下声明都是合法的:
Deer d = new Deer();
Animal a = d;
Vegetarian v = d;
Object o = d;
上述所有引用变量 d、a、v、o 都指向堆内存中的同一个 Deer 对象。
示例代码
interface Vegetarian {}
class Animal {}
public class Deer extends Animal implements Vegetarian {
public static void main(String[] args) {
Deer d = new Deer();
Animal a = d;
Vegetarian v = d;
Object o = d;
System.out.println(d instanceof Deer); // true
System.out.println(a instanceof Deer); // true
System.out.println(v instanceof Deer); // true
System.out.println(o instanceof Deer); // true
}
}
输出:
true
true
true
true
Java 多态的类型
Java 中的多态分为两种:
- 编译时多态(Compile Time Polymorphism)
- 运行时多态(Run Time Polymorphism)
1. 编译时多态(静态多态)
也称为静态多态,通过**方法重载(Method Overloading)**实现。
示例:编译时多态
public class Main {
// 两个整数相加
public int addition(int x, int y) {
return x + y;
}
// 三个整数相加
public int addition(int x, int y, int z) {
return x + y + z;
}
// 两个 double 相加
public double addition(double x, double y) {
return x + y;
}
public static void main(String[] args) {
Main number = new Main();
int res1 = number.addition(444, 555);
System.out.println("两个整数相加: " + res1);
int res2 = number.addition(333, 444, 555);
System.out.println("三个整数相加: " + res2);
double res3 = number.addition(10.15, 20.22);
System.out.println("两个 double 相加: " + res3);
}
}
输出:
两个整数相加: 999
三个整数相加: 1332
两个 double 相加: 30.369999999999997
方法重载是在编译期根据参数列表决定调用哪个方法。
2. 运行时多态(动态多态)
也称为动态方法分派(Dynamic Method Dispatch),通过**方法重写(Method Overriding)**实现。
示例:运行时多态
class Vehicle {
public void displayInfo() {
System.out.println("Some vehicles are there.");
}
}
class Car extends Vehicle {
@Override
public void displayInfo() {
System.out.println("I have a Car.");
}
}
class Bike extends Vehicle {
@Override
public void displayInfo() {
System.out.println("I have a Bike.");
}
}
public class Main {
public static void main(String[] args) {
Vehicle v1 = new Car(); // 向上转型
Vehicle v2 = new Bike(); // 向上转型
v1.displayInfo(); // 调用 Car 的 displayInfo()
v2.displayInfo(); // 调用 Bike 的 displayInfo()
}
}
输出:
I have a Car.
I have a Bike.
尽管引用类型是
Vehicle,但 JVM 在运行时根据实际对象类型调用相应子类的方法。
虚方法与运行时多态
Java 中所有非 private、非 static、非 final 的实例方法本质上都是虚方法(Virtual Methods),支持运行时多态。
示例:虚方法调用
// Employee.java
class Employee {
private String name;
private String address;
private int number;
public Employee(String name, String address, int number) {
System.out.println("Constructing an Employee");
this.name = name;
this.address = address;
this.number = number;
}
public void mailCheck() {
System.out.println("Mailing a check to " + this.name + " " + this.address);
}
public String getName() { return name; }
public String getAddress() { return address; }
public int getNumber() { return number; }
}
// Salary.java
class Salary extends Employee {
private double salary;
public Salary(String name, String address, int number, double salary) {
super(name, address, number);
setSalary(salary);
}
@Override
public void mailCheck() {
System.out.println("Within mailCheck of Salary class");
System.out.println("Mailing check to " + getName() + " with salary " + salary);
}
public void setSalary(double newSalary) {
if (newSalary >= 0.0) salary = newSalary;
}
public double getSalary() { return salary; }
}
// VirtualDemo.java
public class VirtualDemo {
public static void main(String[] args) {
Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00);
System.out.println("使用 Salary 引用调用 mailCheck --");
s.mailCheck();
System.out.println("\n使用 Employee 引用调用 mailCheck --");
e.mailCheck();
}
}
输出:
Constructing an Employee
Constructing an Employee
使用 Salary 引用调用 mailCheck --
Within mailCheck of Salary class
Mailing check to Mohd Mohtashim with salary 3600.0
使用 Employee 引用调用 mailCheck --
Within mailCheck of Salary class
Mailing check to John Adams with salary 2400.0
即使
e是Employee类型的引用,JVM 在运行时仍会调用Salary类中重写的mailCheck()方法。这种机制称为虚方法调用(Virtual Method Invocation)。
总结
| 特性 | 编译时多态 | 运行时多态 |
|---|---|---|
| 别名 | 静态多态 | 动态多态 |
| 实现方式 | 方法重载(Overloading) | 方法重写(Overriding) |
| 决策时机 | 编译期 | 运行期 |
| 依据 | 参数列表(数量、类型、顺序) | 实际对象类型 |
| 示例 | add(int a, int b) vs add(double a, double b) |
Animal a = new Dog(); a.speak(); |
多态是 Java 实现灵活性和可扩展性的关键机制,广泛应用于框架设计、插件系统和接口编程中。