引用类型的强制转换的一般概念及深入理解

java中的数据的类型分为基本数据类型和引用类型,类型转换分为基本数据类型转换和引用类型转换两种。基本数据类型的转换分为自动转换(小→大)和强制转换(大→小)。引用类型转换也分为自动转换和强制转换。

子类继承了父类的所有成员变量和成员方法的同时,还拓展(extends)了自己的新的属性和方法,从集合的角度看,子类的范围是大于等于父类的。可以参考下面的Wayne图。所以,如果把一个子类转换成父类,就相当于一个功能丰富的类现在只取一部分功能组建一个类,显然是可以的,这就是自动转换,又叫向上转型。但是如果把一个父类转换成子类,显然需要一些强制性的操作,这就是强制转换,又叫向下转型。

举例说明一下:假设有父类Father和子类Son,一个父类对象所占内存为1M,一个子类对象所占内存为0.5M。

先看两个简单的对象声明:

Father father=new Father();这条语句,创建了一个父类的对象,同时创建了一个父类的引用指向它。从内存的角度考虑,系统会为这个对象分配1M的堆内存空间。

Son son=new Son();这条语句,创建了一个子类的对象,同时创建了一个子类的引用指向它。从内存的角度考虑,系统会为这个对象分配1.5M的堆内存空间。注意,不是0.5M,是1.5M!因为子类在继承父类的时候,编译器会自动为子类添加一个引用名为super的父类对象。这也是为什么子类可以调用父类成员的根本原因!所以,此条语句会让系统分配1.5M内存空间。

再看特殊一点的对象声明:

Father father=new Son();这条语句,创建了一个子类的对象,同时创建了一个父类的引用指向它。这个就是向上转型,是安全的,属于自动转换。从内存的角度考虑,father这个引用只是指向了那1.5M内存中的1M。也就是说,father只能访问和调用那1M内存对应的父类属性和方法,而不能访问和调用那0.5M内存对应的子类的属性和方法。虽然这个语句创建的对象真实类型是Son类型,但是可以理解为是一个被”削弱“过的Son类型对象。

紧接着上一句,刚才我们创建了一个被削弱的Son类型对象,现在我们用强制转换将它还原:

Son son=(Son)father;因为将父类引用转换为了子类对象,所以是向下转型(强制转换)。father这个引用,由于是Son类型对象转换来的,虽然它仅仅指向1.5M中的1M,但是它还是有1.5M内存空间的。所以,son引用可以成功指向1.5M。

但是,强制转换是不安全的,不一定永远都是运行成功的。考虑下面这种情况:

Father father=new Father();

Son son=(Son)father;

上面两句语句,先创建一个父类对象father,再强制转换(向下转换)为子类对象son,运行时候就会报错,系统会抛出ClassCastException异常!从内存的角度考虑,father对象只有1M内存,son引用需要1.5M内存无法得到满足,显然会报错。

比较这两种强制转换,我们可以得出结论:虽然引用的名称都是一样(都是father),但是有且仅当引用指向的真实类型是子类对象时,才会强制转换成功!如果引用指向的是父类对象,强制转换就会失败。

特别提出一点,这两种强制转换语句,在编译器中输入时,都是不会报错的,这是因为编译器只会“傻傻地”检查是否有继承关系存在,有的话就直接通过(静态绑定)。但是在运行时,RTTI(Run-time type infomation),也就是运行时类型信息程序,会检查这些引用所指向对象的真实类型。简单地说,就是你输入Father father=new Son(),编译器看到前半句就认定这是一个father对象了,但是RTTI会检查出这个father引用指向对象的真实类型是Son类(动态绑定)。

综上,最终结论可以记为:子类可以安全的、自动转换为父类,但是父类强制转换为子类只有当引用指向的对象真实类型为子类时才会转换成功!

posted @ 2017-08-28 18:21  摇头耶稣  阅读(507)  评论(0编辑  收藏  举报