🎁个人主页:User_芊芊君子
🎉欢迎大家点赞👍评论📝收藏⭐文章
🔍系列专栏:【Java】内容概括
【前言】
在Java面向对象编程的世界中,多(Polymorphism) 是一个核心概念,它赋予了程序强大的灵活性和扩展性。无论是编写大型企业级应用,还是开发小型工具,多态的合理运用都能让代码更加简洁、优雅且易于维护。本文将深入探讨Java多态的概念、实现方式,并通过丰富的代码示例、图片和表格,带您全面掌握这一重要特性。
文章目录:
一、什么是多态?
多态
字面意思就是多种状态,在Java中,多态
就是指不同类型的对象去完成某种行为时的不同状态
在这里举一个例子:我的家里同时养了一只猫和一只狗(
两个对象
),吃饭(同一行为
)时,猫吃的是一条鱼,狗吃的是一根骨头(两种状态
)
1.多态实现条件
- 必须在
继承
体系下- 子类要对父类方法进行
重写
- 通过父类的引用
调用
父类重写的方法
2.代码示例
还是举上面那个例子:猫和狗吃饭,一个吃的是鱼,一个吃的是骨头
父类
public class Animal {
public void eat(){
System.out.println("吃饭");
}
}
Dog子类
public class Dog extends Animal{
@Override
public void eat(){
System.out.println("啃骨头");
}
}
Cat子类
public class Cat extends Animal {
@Override
public void eat(){
System.out.println("吃一条鱼");
}
}
public class Test {
public static void main(String[] args) {
Animal animal = new Dog();
Animal animal1 = new Cat();
animal.eat();
animal1.eat();
}
}
执行结果:
在这个程序中,
Animal
是父类,Dog
和Cat
是两个子类,这就确定了它的继承
关系,然后子类也对父类的eat方法进行了重写
,在运行时,通过父类的引用调用
了重写的方法,而执行不同的状态,这就是多态
。
二、重写
1.什么是重写
重写(override)
:也称为覆盖。重写是⼦类对⽗类⾮静态
、⾮private修饰
,⾮final修饰
,⾮构造⽅法
等的实现过程进⾏重新编写,返回值和形参都不能改变。即外壳不变,核⼼重写!重写的好处在于⼦类可以根据需要,定义特定于⾃⼰的⾏为。 也就是说⼦类能够根据需要实现⽗类的⽅法。
【重写的规则】
- 子类与父类的返回值类型,方法名(参数列表)要一致;
- 被重写的方法返回值类型可以不同,但是必须具有父子关系;
- 父类被static,private 修饰的方法,构造方法都不能被重写;
- 重写的方法可以用@Override注解来显示指定,它可以帮我们进行校验(如果你的方法名写错,就会报错)
2.重写与重载的区别
⽅法重载
是⼀个类的多态性表现⽅法重写
是⼦类与⽗类的⼀种多态性表现。
区别 | 重写 | 重载 |
---|---|---|
参数列表 | 不能修改 | 必须修改 |
返回类型 | 不能修改(除非是父子关系) | 可以修改 |
访问限定符 | 不能做更严格的限制 | 可以修改 |
静态绑定
:也称为前期绑定(早绑定),即在编译时,根据⽤⼾所传递实参类型就确定了具体调⽤那个⽅法。典型代表函数重载。动态绑定
:也称为后期绑定(晚绑定),即在编译时,不能确定⽅法的⾏为,需要等到程序运⾏时,才能 够确定具体调⽤那个类的⽅法。
三、向上转型和向下转型
向上转型
和向下转型
是面向对象编程中关于对象类型转换的两个重要概念,主要应用于继承关系中。它们分别描述了将对象从子类类型转换为父类类型,以及从父类类型转换回子类类型的过程。
1.向上转型
向上转型
是指将子类对象赋值给父类引用变量的过程。这种转换是隐式的,不需要显式地进行类型转换,因为子类继承了父类的所有属性和方法,因此子类对象可以被视为父类对象。
语法格式:父类类型 对象名=new 子类类型()
Animal animal = new Dog("旺财",10);
我们还是举上面那个例子
public class Test {
public static void main(String[] args) {
Animal animal = new Cat();//子类对象赋值给父类对象
animal.eat();
Animal animal1 = new Dog();//子类对象赋值给父类对象
animal1.eat();
}
}
我们将子类Dog和Cat类赋值给父类Animal中的引用变量animal,这就是
向上转型
。通过animal引用变量可以调用eat()方法,而在运行时,实际执行的是子类Dog和Cat中重写的eat()方法
2.向下转型
向下转型
是指将父类对象转换为子类类型的操作。这种转换是显式的,需要使用类型转换操作符。向下转型是不安全的,因为父类对象可能并不是子类的实例,因此在向下转型时需要进行类型检查,以避免 ClassCastException 异常。
代码示例:
public class Test {
public static void main1(String[] args) {
Animal animal = new Cat();//子类对象赋值给父类对象
animal.eat();
Animal animal1 = new Dog();//子类对象赋值给父类对象
animal1.eat();
if(animal instanceof Cat){//animal是否引用了Cat类型的对象
Cat cat = (Cat) animal;//向下转型
cat.eat();
}else {
System.out.println("animal not instanceof Cat");
}
}
}
animal 对象被向下转型为Cat类,因为animal实际指向的Cat,所以将他还原为Cat类是可以的,即向下转型。如果强制还原为Dog,就会出现下面的
报错
,因为animal1指向的Dog
所以说向下转型是不安全的,万⼀转换失败,运⾏时就会抛异常。Java中为了提⾼向下转型的安全性,引⼊了
instanceof
,如果该表达式为true,则可以安全转换
四、 多态的优缺点
1.优点
提高代码的可扩展性
多态允许在不修改现有代码的情况下扩展系统功能。通过继承和接口实现,可以轻松添加新的子类或实现类,而无需修改父类或接口的代码。例如,在一个图形绘制程序中,可以通过添加新的子类(如圆形、三角形)来扩展支持的图形类型,而无需修改现有的绘制逻辑。
增强代码的可维护性
多态将具体实现与接口分离,使得代码结构更加清晰。通过统一的接口调用不同的实现,减少了代码的重复性,降低了维护成本。例如,在支付系统中,可以通过统一的支付接口调用不同的支付方式(如支付宝、微信支付),而无需在业务逻辑中硬编码每种支付方式的具体实现。
提高代码的灵活性
多态使得程序可以在运行时动态决定调用哪个方法,从而适应不同的场景需求。例如,在游戏开发中,可以根据玩家的选择动态加载不同的角色类,而无需在代码中预先定义所有可能的角色类型。
支持代码复用
通过继承和多态,子类可以复用父类的代码,同时可以根据需要重写或扩展父类的方法。例如,在动物类中定义通用的行为(如移动、进食),子类(如猫、狗)可以复用这些行为,并根据自身特点进行扩展。
2.缺点
增加代码的复杂性
多态引入了继承和接口等概念,可能使代码结构变得更加复杂,尤其是当继承层次较深或接口实现较多时。例如,在一个复杂的系统中,可能需要跟踪多个类的继承关系,增加了理解和调试的难度。
性能开销
多态通常需要在运行时进行动态绑定,这可能导致一定的性能开销。例如,在Java中,虚方法调用需要通过虚方法表(vtable)进行查找,比直接调用静态方法稍慢。
可能导致设计过度抽象
过度使用多态可能导致设计过于抽象,增加了不必要的复杂性。例如,在一个简单的应用中,如果过度使用接口和抽象类,可能会使代码变得难以理解和维护。
继承的脆弱性
多态依赖于继承,而继承可能导致父类的修改影响所有子类。例如,如果父类的方法签名或行为发生改变,所有子类可能需要相应调整,增加了维护的难度。
难以调试
由于多态允许在运行时动态绑定方法,调试时可能难以确定实际调用的方法。例如,在一个多态调用链中,可能需要跟踪多个类的实现,增加了调试的复杂性。
五、总结
- 多态是Java面向对象编程的灵魂,它让代码具备更高的抽象层次和灵活性。通过继承和接口,我们可以将不同的实现细节封装在子类中,使用父类或接口统一管理,从而降低代码复杂度。在实际开发中,合理运用多态能够显著提升系统的可维护性和扩展性。
- 希望本文的讲解和示例能帮助您深入理解Java多态的原理与应用!如果有任何疑问,欢迎在评论区交流讨论~