Java 多态
多态基础语法
1、学习多态基础语法之前,我们需要普及两个概念:
第一个:向上转型
子 ---> 父(自动类型转换)
第二个:向下转型
父 ---> 子(强制类型转换,需要加强制类型转换符)
注意:
java中允许向上转型,也允许向下转型。
*****(五颗星)无论是向上转型,还是向下转型,两种类型之间必须有继承关系,没有继承关系编译器报错。
2、多态指的是:
父类型引用指向子类型对象。
包括编译阶段和运行阶段。
编译阶段:绑定父类的方法。
运行阶段:动态绑定子类型对象的方法。
多种形态
3、什么时候必须使用“向下转型”?
不要随便做强制类型转换。
当你需要访问的是子类对象中“特有”的方法。此时必须进行向下转型。
4、java程序分为编译阶段和运行阶段。
先来分析编译阶段:
对于编译器来说,编译器只知道a2的类型是Animal,
所以编译器在检查语法的时候,会去Animal.class
字节码文件中找move()方法,找到了,绑定上move()
方法,编译通过,静态绑定成功。(编译阶段属于静态绑定。)
再来分析运行阶段:
运行阶段的时候,实际上在堆内存中创建的java对象是
Cat对象,所以move的时候,真正参与move的对象是一只猫,
所以运行阶段会动态执行Cat对象的move()方法。这个过程
属于运行阶段绑定。(运行阶段绑定属于动态绑定。)
多态表示多种形态:
编译的时候一种形态。
运行的时候另一种形态。
5、向下转型有风险吗?
容易出现ClassCastException(类型转换异常)
怎么避免这个风险?
instanceof运算符,可以在程序运行阶段动态的判断某个引用指向的对象是否为某一种类型。
6、 instanceof (运行阶段动态判断)
第一:instanceof可以在运行阶段动态判断引用指向的对象的类型。
第二:instanceof的语法:(引用 instanceof 类型)
第三:instanceof运算符的运算结果只能是:true/false
第四:c是一个引用,c变量保存了内存地址指向了堆中的对象。
假设(c instanceof Cat)为true表示:
c引用指向的堆内存中的java对象是一个Cat。
假设(c instanceof Cat)为false表示:
c引用指向的堆内存中的java对象不是一个Cat。
程序员要养成一个好习惯:
任何时候,任何地点,对类型进行向下转型时,一定要使用instanceof 运算符进行判断。(java规范中要求的。)
这样可以很好的避免:ClassCastException
public class Test01{ public static void main(String[] args){ Animal a1 = new Animal(); a1.move(); //动物在移动!!! Cat c1 = new Cat(); c1.move(); //cat走猫步! Bird b1 = new Bird(); b1.move(); //鸟儿在飞翔!!! // 代码可以这样写吗? /* 1、Animal和Cat之间有继承关系吗?有的。 2、Animal是父类,Cat是子类。 3、Cat is a Animal,这句话能不能说通?能。 4、经过测试得知java中支持这样的一个语法: 父类型的引用允许指向子类型的对象。 Animal a2 = new Cat(); a2就是父类型的引用。 new Cat()是一个子类型的对象。 允许a2这个父类型引用指向子类型的对象。 */ Animal a2 = new Cat(); Animal a3 = new Bird(); // 没有继承关系的两个类型之间存在转型吗? // 错误: 不兼容的类型: Dog无法转换为Animal // Animal a4 = new Dog(); */ a2.move(); //cat走猫步! // 调用a3的move()方法 a3.move(); //鸟儿在飞翔!!! // ====================================================================== Animal a5 = new Cat(); // 底层对象是一只猫。 // 分析这个程序能否编译和运行呢? // 分析程序一定要分析编译阶段的静态绑定和运行阶段的动态绑定。 // 只有编译通过的代码才能运行。没有编译,根本轮不到运行。 // 错误: 找不到符号 // why??? 因为编译器只知道a5的类型是Animal,去Animal.class文件中找catchMouse()方法 // 结果没有找到,所以静态绑定失败,编译报错。无法运行。(语法不合法。) //a5.catchMouse(); // 假设代码写到了这里,我非要调用catchMouse()方法怎么办? // 这个时候就必须使用“向下转型”了。(强制类型转换) // 以下这行代码为啥没报错???? // 因为a5是Animal类型,转成Cat,Animal和Cat之间存在继承关系。所以没报错。 Cat x = (Cat)a5; x.catchMouse(); //猫正在抓老鼠!!!! // 向下转型有风险吗? Animal a6 = new Bird(); //表面上a6是一个Animal,运行的时候实际上是一只鸟儿。 /* 分析以下程序,编译报错还是运行报错??? 编译器检测到a6这个引用是Animal类型, 而Animal和Cat之间存在继承关系,所以可以向下转型。 编译没毛病。 运行阶段,堆内存实际创建的对象是:Bird对象。 在实际运行过程中,拿着Bird对象转换成Cat对象 就不行了。因为Bird和Cat之间没有继承关系。 运行是出现异常,这个异常和空指针异常一样非常重要,也非常经典: java.lang.ClassCastException:类型转换异常。 java.lang.NullPointerException:空指针异常。这个也非常重要。 */ //Cat y = (Cat)a6; //y.catchMouse(); // 怎么避免ClassCastException异常的发生??? /* 新的内容,运算符: instanceof (运行阶段动态判断) */ System.out.println(a6 instanceof Cat); //false if(a6 instanceof Cat){ // 如果a6是一只Cat Cat y = (Cat)a6; // 再进行强制类型转换 y.catchMouse(); } } }
7、多态在开发中有什么作用?
非常重要:五颗星 (多态会天天用,到处用!!!!)
多态在开发中的作用是:
降低程序的耦合度,提高程序的扩展力。
public class Master{
public void feed(Dog d){}
public void feed(Cat c){}
}
以上的代码中表示:Master和Dog以及Cat的关系很紧密(耦合度高)。导致扩展力很差。
public class Master{
public void feed(Pet pet){
pet.eat();
}
}
以上的代表中表示:Master和Dog以及Cat的关系就脱离了,Master关注的是Pet类。
这样Master和Dog以及Cat的耦合度就降低了,提高了软件的扩展性。
8、解释之前遗留的问题
私有方法无法覆盖。
方法覆盖只是针对于“实例方法”,“静态方法覆盖”没有意义。(这是因为方法覆盖通常和多态联合起来)
总结两句话:
私有不能覆盖。
静态不谈覆盖。 (静态方法 和对象无关)
在方法覆盖中,关于方法的返回值类型。
什么条件满足之后,会构成方法的覆盖呢?
1、发生具有继承关系的两个类之间。
2、父类中的方法和子类重写之后的方法:
具有相同的方法名、相同的形式参数列表、相同的返回值类型。
学习了多态机制之后:
“相同的返回值类型”可以修改一下吗?
对于返回值类型是基本数据类型来说,必须一致。
对于返回值类型是引用数据类型来说,重写之后返回值类型可以变的更小
(但意义不大,实际开发中没人这样写。)。
ps: 面向对象的三大特征:封装、继承、多态
有了封装,有了这种整体的概念之后。
对象和对象之间产生了继承。
有了继承之后,才有了方法的覆盖和多态。
这里提到了一个软件开发原则:
七大原则最基本的原则:OCP(对扩展开放,对修改关闭)
目的是:降低程序耦合度,提高程序扩展力。
面向抽象编程,不建议面向具体编程。