1. 简介
Java的转型问题在父类引用指向子类对象时产生,可以划分为向上转型和向下转型。
向上转型:父类引用指向子类对象
向下转型:父类引用转换为子类引用(引用变量的声明类型发生变化)
2. 示例
2.1 代码示例
首先构造父类和子类,再进行向上转型和向下转型,代码如下:
1 class Father { 2 public void speak(){ 3 System.out.println("Father speak"); 4 } 5 public void work(){ 6 System.out.println("Father work"); 7 } 8 } 9 10 class Son1 extends Father{ 11 public void speak(){ 12 System.out.println("Son1 speak"); 13 } 14 void play(){ 15 System.out.println("Son1 play"); 16 } 17 } 18 19 class Son2 extends Father{ 20 public void speak(){ 21 System.out.println("Son2 speak"); 22 } 23 24 void play(){ 25 System.out.println("Son2 play"); 26 } 27 } 28 29 public class Test { 30 private static void speakFunc(Father f){ 31 f.speak(); 32 } 33 34 public static void main(String[] args){ 35 // 向上转型,父类引用指向子类对象 36 Father f1 = new Son1(); 37 Father f2 = new Son2(); 38 speakFunc(f1); // Son1 speak 39 speakFunc(f2); // Son2 speak 40 f1.work(); // Father work 41 // f1.play(); // 错误,无法调用父类中不存在的方法 42 43 // 向下转型,重新获取子类独有的方法 44 Son1 s1 = (Son1) f1; 45 s1.play(); // Son1 play 46 47 /* 48 *错误,父类引用指向父类对象本身,不能进行向下转型; 49 * 通过a instanceof A判断对象a是否是类A的实例 50 */ 51 Father f3 = new Father(); 52 // Son1 s3 = (Son1) f3; 53 System.out.println(f3 instanceof Son1); // false 54 } 55 }
2.2 注意点
向上转型的注意点:
1.父类引用指向子类对象;
2.父类引用变量不能调用只有子类中存在的方法;
3.父类引用变量可以调用只有父类中存在的方法;
关于2、3两点的说明:
变量的声明类型决定了它能够调用哪些方法,而不是堆中对象的实际类型决定它能够调用哪些方法。
在41行中f1的声明类型为Father,拥有speak和work方法,所以f1无法执行play方法。
再从另一角度考虑该问题,41行所报的是编译期的错误,而不是运行时的错误。为什么会这样呢?我们需要记住一点:
对象类型数据在程序编译期,并不会在内存中进行创建和存储工作;而是在程序运行期,才根据需要进行动态的创建和存储。
从上可知,编译器为什么要根据变量的声明类型来决定能不能调用某个方法,因为只有当程序运行时编译器才可以得知引用变量所绑定的对象是哪个。
那么如何理解f1.speak()的执行结果为子类重写的方法,这里涉及到“多态”的知识:
引用变量f1指向Son1对象,所以JVM首先查找Son1类中是否存在speak方法,如果存在则调用该方法;如果不存在该方法,则会按照继承链向上查找,
例如在执行f1.work()时在Son1类中无该方法,所以通过继承链找到父类Father中的work方法执行。
向下转型的注意点:
1.需要“父类引用指向子类对象”的前提;
2.父类引用指向父类对象本身,不可以进行向下转型;
3.通过a instance A关键字判断对象a是否是类A的实例;
3. 意义
向上转型的意义:
多个同父的对象调用某个函数方法时,无需多次编写该函数;可以通过向上转换,确定参数的统一,利用一个函数实现。
向下转型的意义:
通过向下转型实现子类独有方法的调用。
注:向上转型和向下转型的应用场景和意义详情可见下一章节。
!!!