通过向上向下转型对多态进行理解
2022-09-30-14:25:49
多态的基础语法
⭐学习多态基础语法之前,两个概念:
📧第一个:向上转型(通用语)
子 --->父(自动类型转换)
📧第二个:向下转型(通用语)
父 --->子(强制类型转换,需要加强制类型转换符)
注意:
java中允许向上转型,也允许向下转型
* * * * *无论是向上还是向下,两种类型之间必须有继承关系,
* * * * *没有继承关系,编译器报错。
==================================================//(举例)
public class Animal{ //移动的方法 public void move(){ System.out.println("动物在移动!!!"); } } //猫类,子类 public class Cat extends Animal{ //对move方法进行重写 public void move(){ System.out.println("猫在走猫步!!!"); }
//这个行为是子类型对象特有的方法
public void catchMouse(){
System.out.println("猫正在抓老鼠!!")
}
} //鸟类,子类 public class Bird extends Animal{ //对move方法进行重写 public void move(){ System.out.println("鸟儿在飞翔!!!"); } }
Animal a1=new Animal();
a1.move();
Cat c1=new Cat();
c1.move();
Bird b1=new Bird();
b1.move();
🍓向上转型【子 --->父】
Animal a2=new Cat();
Animal a3=new Bird();
a2.move();
a3.move();
代码可以这样写吗?可以
/*【解释说明🥥】
1、Animal和Cat之间有继承关系吗?有的
2、Animal是父类。Cat是子类
3、Cat is a Animal,这句话能不能说通?能
4、经过测试得知:父类型的引用允许指向子类型的对象
Animal a2=new Cat();
a2就是父类型的引用。
new Cat()是一个子类型的对象。
允许a2这个父类型的引用指向子类型的对象
*/
🍓分析:a2.move();
Java程序分为编译阶段和运行阶段。
📕先来分析编译阶段:
对于编译器来说,编译器只知道a2的类型是Animal,所以编译器在检查语法时,会去Animal.class字节码文件中找move()方法,找到了,绑定上move()方法,编译通过,静态绑定成功(编译阶段属于静态绑定。)。
📕再来分析运行阶段:
运行阶段的时候,实际上在堆内存中创建的java对象时cat对象,所以move的时候,真正参与move的对象是一只猫,所以运行阶段会动态执行cat对象的move()方法。这个过程属于运行阶段绑定(运行阶段绑定属于动态绑定。)。
⭐多态:
📧父类型引用指向子类型的对象。
📧包括编译阶段和运行阶段。
编译阶段:绑定父类的方法。
运行阶段:动态绑定子类型对象的方法。
📧多种形态,多种状态。
❤首要条件❤
无论向上还是向下转型,两种类型之间必须要有继承关系。
没有继承关系,就没有向上还是向下转型。
🐖🐖🐖🐖🐖
Animal a5=new Cat();//底层对象是一只猫
//分析这个程序能否编译和运行?
//分析程序一定要分析编译阶段的静态绑定和运行阶段的动态绑定
//只有编译通过的代码才能运行。没有编译,根本轮不到运行。
a5.catchMouse();//编译错误,在Animal.class文件中找不到catchMouse()方法
********************************************************************************************
a5.catchMouse();//编译错误,在Animal.class文件中找不到catchMouse()方法
要想编译通过,需要使用向下转型
🍓向下转型【父 --->子】 👇
什么时候必须使用“向下转型”?
不要随便做强制类型转换。当你需要访问的是子类对象“特有的”方法,此时必须进行向下转型。
Cat x=(Cat)a5;
x.catchMouse();//猫正在抓老鼠
//编译通过:因为a5是Animal类型,转成Cat,Animal和Cat之间存在继承关系,所以没报错。
向下转型有风险吗?
Animal a6=new Bird();//表面上a6是一个Animal,运行的时候实际上是一只鸟儿。
/*
分析一下程序,编译报错还是运行报错???
编译器检测到a6这个引用是Animal类型。
而Animal和Cat之间存在继承关系,所以可以向下转型。
运行阶段,堆内存实际创建的对象是一个Bird对象。
在实际运行中,拿着Bird对象转换成Cat对象就不行了。
因为Bir和Cat之间没有继承关系
运行时出现异常,这个异常和空指针异常一样非常重要,也非常经典:
java.lang.ClassCastException:类型转换异常
java.lang.NullPointerException:空指针异常。这个也非常重要。
*/
Cat y=(Cat)a6;
y.catchMouse();
怎么避免ClassCastException异常的发生???
/*
♥运算符:instanceof
第一:instanceof可以在运行阶段动态判断引用指向的对象的类型。
第二:instanceof的语法:
(引用 instanceof 类型)
第三:instanceof运算符的运算结果只能是:time/false
第四:c是一个引用,c变量保存了内存地址指向了堆中的对象。
假设(c instanceof Cat)为true表示:
c引用指向的堆内存中的Java对象是一个Cat。
假设(c instanceof Cat)为false表示:
c引用指向的堆内存中的Java对象不是一个Cat。
程序员要养成一个好习惯:
任何时候,任何地点,对类型进行向下转型时,一定要使用instanceof 运算符进行判断
if(a6 instanceof Cat){//如果a6是一只Cat Cat y=(Cat)a6;//再进行强制类型转换 y.catchMouse(); }
⭐多态在实际开发中的作用:降低程序的耦合度,提高程序的扩展力
在实际的开发中,客户产生了新的需求。
我们怎么去满足客户的需求?在不使用多态机制的前提下,目前只能在类中添加一个新的方法。
思考:软件在扩展新需求过程中,修改类有什么问题?
软件在扩展过程中,修改的越少越好。修改的越多,你的系统当前的稳定性就越差,未知的风险就越多。
软件开发原则有七大原则(不属于Java,属于整个软件业)
其中有一条最基本的原则:OCP(开闭原则)
什么是开闭原则?
对扩展开放,对修改关闭。
在软件的扩展过程中,修改的越少越好。
高手开发项目不是仅仅为了实现客户的需求,还需要考虑软件的扩展性。
面向父类型编程,面向更加抽象进行编程,不建议面向具体编程。
因为面向具体编程会让软件的扩展力很差
package 多态的学习; public class Testt{ public static void main(String[] args){ Master m=new Master(); Pet pet1=new Cat(); m.feed(pet1); Pet pet2=new Dog(); m.feed(pet2); } } //主人类 class Master{ void feed(Pet pet){ pet.eat(); } } //父类 class Pet{ //做一个空实现 void eat(){ } } //子类 class Cat extends Pet{ void eat(){ System.out.println("猫咪喜欢吃老鼠!!"); } } //子类 class Dog extends Pet{ void eat(){ System.out.println("狗狗喜欢吃骨头!!"); } }
===============================================
总结
💒面向对象的三大特征:封装、继承、多态(一环扣一环)
有了封装,有了这种整体的概念之后。
对象和对象之间产生了继承。
有了继承之后,才有了方法的覆盖和多态。
这里提到了一个软件开发原则:
七大原则最基本的原则:OCP(对扩展开放,对修改关闭)
目的是:降低程序耦合度,提高程序扩展力
面向抽象编程,不建议面向具体编程。
本文作者:TranquilTimber
本文链接:https://www.cnblogs.com/gbrr/p/16743754.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步