多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(摘自“Delphi4 编程技术内幕”)。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。多态性在Object Pascal和C++中都是通过虚函数实现的。 ----------百度百科。
向上转型 & 向下转型
前提是要有继承关系!!!!!。
向上转型(upcasting):子转父。 父类 x = new 子类();
向下转型(downcasting):父转子。 子类 x = new 父类();
分析:
java程序分为编译阶段和运行阶段。
//Animal父类
public class Animal_01 {
public void move(){
System.out.println("动物在移动");
}
}
//Animal的子类,猫类
public class Cat_Animal extends Animal_01{
//对move重写
public void move(){
System.out.println("猫在喵喵喵");
}
// catchMouse 方法是 Cat_Animal类特有的方法
public void catchMouse(){
System.out.println("猫正在抓老鼠。");
}
}
//test类
public class D001 {
public static void main(String[] args) {
Animal_01 a1 = new Animal_01();
a1.move(); //输出:动物在移动
Cat_Animal a2 = new Cat_Animal();
a2.move(); //输出:猫在喵喵喵
Animal_01 q3 = new Cat_Animal();
q3.catchMouse();
//编译不通过,因为编译器只知道q3的类型是Animanl,去Animal.class 找catchMouse()方法,
//结果没找到,所以静态绑定失败,编译报错,无法运行。
编译阶段:
对于编译器来说,编译器只知道q1 的类型的Animal,
所以编译器在检查语法的时候,会去 Animal.class字节码文件中查找move()方法,
找到了,绑定上move()方法,编译通过,静态绑定成功。(编译阶段属于静态绑定)。
运行阶段:
运行阶段的时候,实际上在堆内存当中创建的java对象是Cat对象,所以move的时候真正参与的是Cat,
所以运行阶段会动态执行cat对象的move方法,这个过程属于运行阶段绑定。(运行阶段绑定属于动态绑定)。
多态的多种形态:
编译的时候一种形态,
运行的时候一种形态。
多态指的是(向上):
父类引用指向子类对象。
包括编译阶段和运行阶段。
编译阶段:绑定父类的方法。
运行阶段:动态绑定子类对象的方法。
由此也可以看出什么是多态:
多种形态,多种状态,运行和编译有着不同的状态。
编译期叫动态绑定,运行期叫静态绑定。
向下转型:
(强制转换)
什么时候使用向下转型?
当访问的是子类对象中 “特有” 的方法时,此时必须使用向下转型,才可以调用。
//在test文件
//向下转型。
Animal_01 w1 = new Cat_Animal();
Cat_Animal w2 =(Cat_Animal)w1;
w2.catchMouse(); //输出:猫正在抓老鼠
向下转型有没有风险?
有。 类型转换异常
java.lang.ClassCastException:
下面程序错误示范:
//在test文件
Animal_01 a6 = new Bird_Animal(); //表面上是 animanl_01 ,其实是 Bird_Animal 。
Cat_Animal y = (Cat_Animal)a6;
y.catchMouse(); //报错, java.lang.ClassCastException:
编译阶段:编译器监测到a6这个引用还是Animal_01 类型,而Animal_01 与 Cat_Animal 之间存在继承关系,所以可以向下转型。 编译没问题。
运行阶段: 堆内存实际创建的是Bird对象,,在实际运行过程中,你用Bird对象转换成Cat对象,是不行的,因为Bird 和 cat 之间没有继承关系。
异常:(和空指针异常一样重要,也很经典)
java.lang.ClassCastException:
class Duotai.Bird_Animal cannot be cast to class Duotai.Cat_Animal
(Duotai.Bird_Animal and Duotai.Cat_Animal are in unnamed module of loader 'app')
类型转换异常。
怎么解决类型转换异常?
运算符: instanceof
1. 在运行阶段 动态判断 引用指向的对象的类型。
2. instanceof 的语法是: (引用 instanceof 类型)。
3. instanceof 运算符的运算结果只能是 true & false。
4. c是一个引用,是一个对象,c变量保存了内存地址指向堆中的对象,
假设 (c instanceof cat) 为true:c引用指向了堆内存中的java对象 是 一个cat。
假设 (c instanceof cat) 为false:c引用指向了堆内存中的java对象 不是 一个cat。
//怎么避免异常发生?
//向下转型 时都要使用 instanceof 运算符判断。
if (a6 instanceof Cat_Animal) { //如果a6是cat,再进行强制类型转换。
Cat_Animal y = (Cat_Animal) a6;
y.catchMouse();
}
所以,要养成习惯,向下转型之前一定要用 instanceof 运算符进行判断。
我为什么要用instanceof?
Animal_01 q1 = new Bird_Animal(); //虽然你可以看出来是对象是鸟,但还是要用instanceof。
Animal_01 q2 = new Cat_Animal(); //原因:你以后可能肉眼看不到。 比如 方法不是你自己写的。我不知道传过来一个什么变量。
//所以我加个判断,要向下转型,我要确定XXX。
多态的作用
-
多态在开发中的作用?
降低程序耦合度,提高程序的扩展力。
软件拓展过程,修改代码越少越好,修改的代码繁多,稳定性差,未知的风险大。
什么是扩展性?
比如电脑内存条坏了,买条新的换上就好了,就说明电脑扩展性好。软件开发七大原则: OCP(开闭原则)
对扩展开发,对修改关闭。
在软件的扩展过程中,代码修改的越少越好。