1. 多态的概念
1.1 什么是多态?
多态 是指同一操作可以作用于不同的对象上,并表现出不同的行为。在 Java 中,多态意味着父类的引用可以指向子类的对象,并且调用方法时,执行的是子类的具体实现,而不是父类的方法。
1.2 多态的类型
多态分为两种:
- 编译时多态(静态多态):也称为方法重载(Method Overloading),同一方法名可以有不同的参数签名,编译时确定调用哪个方法。
- 运行时多态(动态多态):也称为方法重写(Method Overriding),父类的引用可以指向子类的对象,运行时根据实际对象类型调用子类的重写方法。
2. 多态的实现条件
在 Java 中,要实现多态,需要满足以下三个条件:
2.1 继承(Inheritance)
多态的前提是类之间存在继承关系。子类继承父类的属性和方法,然后可以对某些方法进行重写(Override
),以提供子类自己的实现。
示例:
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("狗在叫");
}
}
解释:
Dog
类继承自Animal
类,并重写了makeSound()
方法。这为多态的实现提供了基础。
2.2 方法重写(Overriding)
子类必须重写父类的方法,即子类提供了对父类方法的自己的实现。重写的方法签名(方法名、参数列表和返回类型)必须与父类一致,且访问修饰符不能比父类的更严格。
示例:
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("狗在叫");
}
}
解释:
Dog
类重写了makeSound()
方法,提供了不同于父类的实现。重写是多态的关键,只有通过重写,子类才能表现出不同的行为。
2.3 父类引用指向子类对象
实现多态的第三个条件是父类的引用指向子类的对象。即使引用类型是父类,但实际对象是子类,方法的执行将根据对象的实际类型来决定。这就是运行时多态的精髓。
示例:
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog(); // 父类引用指向子类对象
myDog.makeSound(); // 调用的是Dog类的makeSound方法
}
}
解释:
- 这里
myDog
是Animal
类型的引用,但它指向了Dog
对象。调用makeSound()
时,实际执行的是Dog
类的makeSound()
方法。
3. 方法重载与方法重写的对比学习
3.1. 概念对比
实现机制的比较
特性 | 方法重写(Override) | 方法重载(Overload) |
---|---|---|
调用时机 | 运行时决定调用哪个方法(动态绑定) | 编译时决定调用哪个方法(静态绑定) |
参数列表要求 | 必须与父类方法一致 | 参数列表必须不同 |
方法签名一致性 | 要求方法名、参数、返回类型一致 | 只要求方法名相同,参数不同 |
继承与否 | 必须在继承体系中,涉及父类与子类 | 只需在同一个类中 |
实现目的 | 实现父类方法的不同版本,以提供多态性 | 提供相同方法的不同版本,以应对不同的输入 |
异常处理 | 子类方法的异常不能比父类更多 | 没有异常处理的限制 |
3.2. 详细讲解与示例
3.2.1 方法重写(Override)
方法重写是指子类对父类方法进行重新实现,以定制或扩展父类的功能。子类重写的目的是为了在继承关系中提供不同的实现。方法重写支持多态,可以通过父类的引用在运行时执行子类的具体方法。
规则:
- 方法名和参数列表必须和父类中的方法完全一致。
- 返回类型必须与父类相同或是其子类(Java 5+ 引入的协变返回类型)。
- 子类方法的访问权限不能比父类更严格,但可以更宽松。
- 子类不能抛出比父类更多的受检异常。
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
class Dog extends Animal {
@Override
public void makeSound() { // 重写父类方法
System.out.println("狗在叫");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog(); // 父类引用指向子类对象
animal.makeSound(); // 调用的是 Dog 类的 makeSound 方法
}
}
输出:
狗在叫
解释:
- 在这个例子中,
Dog
类重写了Animal
类的makeSound()
方法。即使通过Animal
类型的引用调用makeSound()
,运行时多态保证了实际执行的是Dog
类的版本。 - 重写的主要目的是为了修改或扩展父类的行为,常用于继承关系和多态的场景。
2.2 方法重载(Overload)
方法重载是在同一个类中,定义多个方法名相同、但参数不同的方法。方法重载的目的是为了提供灵活的参数处理,让同一方法名能够处理不同的参数组合。方法重载在编译时确定调用哪个方法,因此它是静态绑定(编译时多态)。
规则:
- 方法名必须相同,但参数列表(参数类型、个数、顺序)必须不同。
- 重载方法可以有不同的返回类型,但返回类型不能用于区分重载方法。
- 访问修饰符和抛出的异常可以不同。
示例:
class Printer {
public void print(int num) {
System.out.println("打印整数: " + num);
}
public void print(String text) {
System.out.println("打印字符串: " + text);
}
public void print(int num, String text) {
System.out.println("打印整数和字符串: " + num + " " + text);
}
}
public class Main {
public static void main(String[] args) {
Printer printer = new Printer();
printer.print(123); // 调用 print(int)
printer.print("Hello World"); // 调用 print(String)
printer.print(456, "Java"); // 调用 print(int, String)
}
}
打印整数: 123
打印字符串: Hello World
打印整数和字符串: 456 Java