一、多态的核心定义与必要条件
1. 核心定义
多态(Polymorphism)指的是:同一行为(方法),作用在不同对象上,会表现出不同的结果。
比如"发出声音"这个行为,猫叫"喵",狗叫"汪",本质都是"发声",但具体表现不同。
2. 多态的3个必要条件(缺一不可)
-
继承/实现:子类继承父类,或实现接口;
-
方法重写:子类重写父类的方法;
-
父类引用指向子类对象 :
父类类型 变量名 = new 子类类型();(这是多态的语法核心)。
二、多态的优缺点(对应第一张图)
1. 优点:提高代码扩展性与可维护性
-
解耦:调用者只依赖父类/接口,不依赖具体子类,代码更灵活;
-
可扩展 :新增子类(如新增
Bird)时,无需修改原有调用逻辑,直接兼容; -
代码复用:统一的调用方式,减少重复代码。
2. 缺点:不能直接调用子类的特有方法
当我们用父类引用指向子类对象时(如Animal a = new Dog()),编译时类型是Animal,所以只能调用父类中定义的方法,无法直接调用子类特有的方法 (如Dog的watchHome()看家方法)。
这也是我们需要学习"多态转型"的原因------解决这个缺点。
三、多态的转型机制(对应第一张图)
为了解决"多态对象不能调用子类特有方法"的问题,Java提供了类型转换机制,分为两种:
1. 自动类型转换(向上转型)
-
定义:把子类类型的数据赋值给父类类型的变量;
-
语法 :
Animal a = new Dog();(自动完成,无需强转); -
特点:安全、自动,但会"丢失"子类特有的方法(只能调用父类方法)。
2. 强制类型转换(向下转型)
-
定义:把父类类型的数据赋值给子类类型的变量;
-
语法 :
Dog dog = (Dog) a;(需要手动强转); -
作用:恢复子类的特有方法,解决多态的缺点;
-
风险 :如果对象的实际类型和目标类型不匹配,会抛出
ClassCastException(类型转换异常)。
注意:对象的本质是什么,就只能转成什么类型。比如
Animal a = new Cat(),就不能强转成Dog,否则会报错。
四、instanceof关键字(对应第一张图)
为了避免强制类型转换时出现ClassCastException,Java提供了instanceof关键字:
1. 作用
判断一个对象是否属于某种引用数据类型,返回boolean值(true/false)。
2. 语法
java
对象名 instanceof 数据类型
3. 典型用法(安全转型)
java
Animal a = new Dog();
if (a instanceof Dog) { // 先判断对象实际类型
Dog dog = (Dog) a; // 再强转,避免异常
dog.watchHome(); // 调用子类特有方法
}
五、多态的成员访问特点(对应第二张图)
这是多态最容易混淆的点,也是面试高频考点:
1. 核心结论(多态的"编译看左,运行看右")
-
成员变量 :编译看左边(父类),运行看左边(父类)→ 属性没有多态;
-
成员方法 :编译看左边(父类),运行看右边(子类)→ 方法有多态。
2. 对比继承的成员访问(就近原则)
在继承关系中,成员访问遵循"就近原则":子类没有就找父类,父类没有就报错;
但在多态中,成员变量和方法的访问规则完全不同,这是因为:
-
方法可以被重写,所以运行时会执行子类的逻辑;
-
变量不能被重写,所以始终访问的是编译时类型(父类)的变量。
六、代码示例 + 优化过程(核心)
下面通过4个版本的代码,清晰展示多态的优势、转型的必要性,以及成员访问的特点。
版本1:没有多态,代码冗余(反面教材)
java
class Cat {
public void makeSound() {
System.out.println("猫:喵~喵~");
}
public void catchMouse() { // 子类特有方法
System.out.println("猫抓老鼠");
}
}
class Dog {
public void makeSound() {
System.out.println("狗:汪~汪~");
}
public void watchHome() { // 子类特有方法
System.out.println("狗看家");
}
}
public class Test1 {
// 每个动物写一个方法,代码冗余,扩展性差
public static void letCatSpeak(Cat cat) {
cat.makeSound();
}
public static void letDogSpeak(Dog dog) {
dog.makeSound();
}
public static void main(String[] args) {
Cat cat = new Cat();
Dog dog = new Dog();
letCatSpeak(cat); // 输出:猫:喵~喵~
letDogSpeak(dog); // 输出:狗:汪~汪~
// 新增Bird类,还要写letBirdSpeak(),代码越来越臃肿
}
}
版本2:用多态,统一调用(优化1:解耦+扩展)
java
// 父类Animal
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
// 子类Cat
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("猫:喵~喵~");
}
public void catchMouse() {
System.out.println("猫抓老鼠");
}
}
// 子类Dog
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("狗:汪~汪~");
}
public void watchHome() {
System.out.println("狗看家");
}
}
public class Test2 {
// 统一方法,接收父类类型,可兼容所有子类
public static void letAnimalSpeak(Animal animal) {
animal.makeSound(); // 多态:运行时执行子类方法
// animal.catchMouse(); // 编译报错:父类没有这个方法(多态的缺点)
}
public static void main(String[] args) {
Animal a1 = new Cat();
Animal a2 = new Dog();
letAnimalSpeak(a1); // 输出:猫:喵~喵~
letAnimalSpeak(a2); // 输出:狗:汪~汪~
// 新增Bird类,无需修改letAnimalSpeak(),直接兼容(扩展性好)
}
}
版本3:用转型 + instanceof,解决子类特有方法(优化2:安全调用特有方法)
java
public class Test3 {
public static void letAnimalSpeak(Animal animal) {//方法的参数如果是一个类,那么调用此方法需传入此类的对象或此类的子类对象。
animal.makeSound();
// 用instanceof判断类型,再强转,安全调用特有方法
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
} else if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHome();
}
}
public static void main(String[] args) {
Animal a1 = new Cat();
Animal a2 = new Dog();
letAnimalSpeak(a1); // 输出:猫:喵~喵~;猫抓老鼠 也可以写成letAnimalSpeak(new Cat());//此类对象也叫匿名对象
letAnimalSpeak(a2); // 输出:狗:汪~汪~;狗看家
}
}
版本4:演示成员访问特点(对应第二张图)
java
class Person {
public String name = "人类";
public void sayHello() {
System.out.println("大家好,我是人类");
}
}
class Student extends Person {
public String name = "学生"; // 子类同名变量
@Override
public void sayHello() {
System.out.println("大家好,我是学生");
}
}
public class Test4 {
public static void main(String[] args) {
Person p = new Student(); // 父类引用指向子类对象
System.out.println(p.name); // 输出:人类(编译看左,运行看左 → 属性无多态)
p.sayHello(); // 输出:大家好,我是学生(编译看左,运行看右 → 方法有多态)
}
}
七、总结
-
多态的本质 :通过"父类引用指向子类对象",实现"同一行为,不同表现",核心是动态绑定(运行时确定执行哪个子类方法);
-
转型的本质 :向上转型是"安全的自动转换",向下转型是"恢复子类能力",必须配合
instanceof使用,避免异常; -
成员访问的本质:方法可以被重写,所以有多态;变量不能被重写,所以没有多态;
-
使用场景:当需要统一调用不同子类的行为,或需要提高代码扩展性时,优先使用多态。
(注:文档部分内容由 AI 生成)