目录
一、什么是多态
多态的实现前提
二、多态调用成员的特点
1. 调用成员变量的特点:编译看左边,运行也看左边
2. 调用成员方法的特点:编译看左边,运行看右边
关键对比总结
三、多态的优势和弊端
1. 多态的优势(为什么必须掌握多态)
(1)提高代码的扩展性
(2)提高代码的复用性
(3)简化代码逻辑
2. 多态的弊端(为什么需要类型强转)
四、多态弊端的解决方法:类型强转
1. 类型强转的语法
2. 强转的风险:ClassCastException
3. 安全强转:先判断后转换(instanceof 关键字)
instanceof 的语法
instanceof 的核心作用
五、多态的典型应用场景
六、多态高频面试题整理
七、总结
一、什么是多态
多态是 Java 面向对象三大特性(封装、继承、多态)的核心之一,指同一行为(方法调用)在不同对象上表现出不同的实现效果。简单来说,就是 “一个接口,多种实现”。
多态的实现前提
存在继承关系:多态发生在父类与子类之间(包括直接继承和间接继承);子类重写父类方法:子类对父类的非静态方法进行重写(Override),这是多态的核心载体;父类引用指向子类对象:通过父类类型 变量名 = new 子类类型()的形式,建立多态引用。
示例:多态的基本实现
// 父类:定义统一行为标准
class Animal {
public void move() {
System.out.println("动物在移动");
}
}
// 子类1:重写父类方法,实现特有行为
class Dog extends Animal {
@Override
public void move() {
System.out.println("狗在奔跑");
}
}
// 子类2:重写父类方法,实现另一特有行为
class Bird extends Animal {
@Override
public void move() {
System.out.println("鸟在飞翔");
}
}
public class PolymorphismDemo {
public static void main(String[] args) {
// 父类引用指向子类对象(多态引用)
Animal animal1 = new Dog();
Animal animal2 = new Bird();
// 同一方法调用,表现出不同行为(多态效果)
animal1.move(); // 输出:狗在奔跑
animal2.move(); // 输出:鸟在飞翔
}
}
二、多态调用成员的特点
多态中,成员变量和成员方法的调用遵循截然不同的规则,核心区别在于 “成员方法支持多态,成员变量不支持多态”。
1. 调用成员变量的特点:编译看左边,运行也看左边
编译看左边:编译时检查 “父类类型” 是否包含该成员变量,若没有则编译报错;运行看左边:运行时实际访问的是 “父类类型” 中定义的成员变量,与子类无关。
原因:成员变量属于 “对象的静态属性”,不参与多态,其访问权限在编译时就已确定。
示例:
class Father {
int num = 10; // 父类成员变量
}
class Son extends Father {
int num = 20; // 子类成员变量(隐藏父类变量)
}
public class PolymVarDemo {
public static void main(String[] args) {
// 多态引用:父类引用指向子类对象
Father father = new Son();
// 访问成员变量:编译看左边(Father有num),运行也看左边(Father的num=10)
System.out.println(father.num); // 输出:10(而非子类的20)
}
}
2. 调用成员方法的特点:编译看左边,运行看右边
编译看左边:编译时检查 “父类类型” 是否包含该方法(或其重载形式),若没有则编译报错;运行看右边:运行时实际执行的是 “子类对象” 中重写后的方法,而非父类的原方法。
原因:成员方法属于 “对象的动态行为”,多态的核心就是通过重写实现方法行为的动态绑定(运行时确定具体执行哪个类的方法)。
示例:
class Father {
public void show() { // 父类方法
System.out.println("父类的show方法");
}
}
class Son extends Father {
@Override
public void show() { // 子类重写父类方法
System.out.println("子类的show方法");
}
}
public class PolymMethodDemo {
public static void main(String[] args) {
// 多态引用:父类引用指向子类对象
Father father = new Son();
// 调用成员方法:编译看左边(Father有show()),运行看右边(Son重写的show())
father.show(); // 输出:子类的show方法
}
}
关键对比总结
成员类型编译时检查对象运行时执行对象核心原因成员变量父类(左边)父类(左边)不支持多态,静态绑定成员方法父类(左边)子类(右边)支持多态,动态绑定(重写)
三、多态的优势和弊端
多态是 Java 代码设计的核心技巧,但其特性也带来了明显的优势和局限性。
1. 多态的优势(为什么必须掌握多态)
(1)提高代码的扩展性
通过父类引用接收所有子类对象,新增子类时无需修改原有代码(符合 “开闭原则”:对扩展开放,对修改关闭)。
示例:扩展新子类无需修改调用逻辑
// 原有代码:接收Animal类型的方法
public class AnimalTool {
public static void letItMove(Animal animal) {
animal.move(); // 多态调用,无需关心具体子类
}
}
// 新增子类:无需修改AnimalTool
class Fish extends Animal {
@Override
public void move() {
System.out.println("鱼在游泳");
}
}
// 测试:直接使用新子类
public class ExtendDemo {
public static void main(String[] args) {
AnimalTool.letItMove(new Fish()); // 输出:鱼在游泳(原有代码未修改)
}
}
(2)提高代码的复用性
通过统一的父类接口编写通用逻辑,可复用在所有子类对象上(如工具类、框架代码)。
(3)简化代码逻辑
调用者只需关注父类定义的接口,无需了解每个子类的具体实现,降低代码耦合度。
2. 多态的弊端(为什么需要类型强转)
多态的核心限制是:父类引用只能访问父类中定义的成员,无法直接调用子类特有的方法。
示例:多态引用无法调用子类特有方法
class Animal {
public void move() { ... }
}
class Dog extends Animal {
@Override
public void move() { ... }
// 子类特有方法:父类中没有定义
public void bark() {
System.out.println("狗在叫:汪汪汪");
}
}
public class PolymDisadvantage {
public static void main(String[] args) {
Animal animal = new Dog(); // 多态引用
animal.move(); // 正常:父类有move()方法
// animal.bark(); // 编译报错:Animal类中没有bark()方法
// 原因:编译时检查父类,父类无bark(),即使子类有也无法直接调用
}
}
四、多态弊端的解决方法:类型强转
当需要调用子类特有方法时,需将 “父类引用” 强制转换为 “子类类型”,即类型强转。
1. 类型强转的语法
// 格式:子类类型 变量名 = (子类类型) 父类引用;
Dog dog = (Dog) animal; // 将Animal类型的animal强转为Dog类型
示例:通过强转调用子类特有方法
public class CastDemo {
public static void main(String[] args) {
Animal animal = new Dog(); // 多态引用
// 强转:将父类引用转为子类类型
Dog dog = (Dog) animal;
dog.bark(); // 调用子类特有方法,输出:狗在叫:汪汪汪
}
}
2. 强转的风险:ClassCastException
若父类引用指向的实际对象类型与强转的目标类型不匹配,会抛出ClassCastException(类型转换异常)。
示例:错误强转导致异常
public class CastErrorDemo {
public static void main(String[] args) {
Animal animal = new Bird(); // 实际是Bird对象
// 错误强转:将Bird对象转为Dog类型
Dog dog = (Dog) animal; // 运行时抛出ClassCastException
}
}
3. 安全强转:先判断后转换(instanceof 关键字)
为避免ClassCastException,强转前需用instanceof关键字判断 “父类引用指向的实际对象类型” 是否与目标类型匹配。
instanceof 的语法
// 格式:父类引用 instanceof 目标类型 → 返回boolean
if (animal instanceof Dog) {
// 若为true,说明实际是Dog对象,可安全强转
}
示例:安全的类型强转
public class SafeCastDemo {
public static void main(String[] args) {
Animal animal = new Dog(); // 实际是Dog对象
// 先判断类型,再强转
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark(); // 安全调用,输出:狗在叫:汪汪汪
} else if (animal instanceof Bird) {
Bird bird = (Bird) animal;
// 调用Bird的特有方法
}
}
}
instanceof 的核心作用
检测 “父类引用指向的实际对象” 是否是 “目标类型” 或其子孙类的实例;是多态场景下安全转换类型的 “前置校验工具”,几乎所有强转都需要配合instanceof使用。
五、多态的典型应用场景
参数统一化:方法参数定义为父类类型,可接收所有子类对象(如public void feed(Animal animal));返回值统一化:方法返回父类类型,可返回任意子类对象(如public Animal createAnimal()返回Dog或Bird);集合存储多类型对象:用父类作为集合元素类型,存储不同子类对象(如List
六、多态高频面试题整理
什么是多态?实现多态的三个前提是什么? 答:多态是同一行为在不同对象上的不同表现;前提是:存在继承关系、子类重写父类方法、父类引用指向子类对象。
多态中,成员变量和成员方法的访问规则有何不同? 答:成员变量 “编译看左边,运行看左边”(不支持多态);成员方法 “编译看左边,运行看右边”(支持多态,依赖重写)。
为什么多态引用不能直接调用子类特有方法?如何解决? 答:因为编译时检查父类是否有该方法,父类无定义则编译报错;解决方法是通过类型强转((子类类型)父类引用),强转前需用instanceof判断类型。
instanceof关键字的作用是什么?为什么必须在强转前使用? 答:instanceof用于判断父类引用指向的实际对象是否为目标类型的实例;避免强转时出现ClassCastException,确保类型转换安全。
多态的优势和弊端分别是什么? 答:优势是提高代码扩展性、复用性,简化逻辑;弊端是父类引用无法直接调用子类特有方法,需通过强转解决。
重载和重写的区别?它们与多态的关系? 答:重载是同一类中同名不同参数的方法(编译时多态);重写是子类覆盖父类的方法(运行时多态)。重写是多态的核心实现,重载不直接参与多态但可丰富接口。
以下代码的运行结果是什么?为什么?
class A {
public void show() { System.out.println("A"); }
}
class B extends A {
@Override
public void show() { System.out.println("B"); }
}
public class Test {
public static void main(String[] args) {
A a = new B();
a.show(); // 输出B:多态调用子类重写的方法
System.out.println(a instanceof B); // 输出true:a实际是B对象
}
}
七、总结
多态是 Java 面向对象编程的灵魂,其核心是 “父类引用指向子类对象,方法调用动态绑定到子类实现”。掌握多态的成员访问规则(变量看左边,方法看右边)、优势(扩展性、复用性)、弊端(无法直接调用子类特有方法)及解决方案(instanceof+ 强转),是理解 Java 框架设计、写出灵活可扩展代码的基础,也是面试中考察 Java 基础能力的核心考点。
如果我的内容对你有帮助,请点赞,评论,收藏。接下来我将继续更新相关内容!