强制类型转换的使用
强制类型转换 VS 专用转换方法
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
1. 强制类型转换的使用
1). 不同类型引用之间的转换
(1). 转换的两个类 (A和B) 之间不具有继承关系使用强制类型转换
[1]. 类型A的对象强转到类型B的引用 (对象 ---> 引用)
直接编译无法通过。这里面假设A类是Integer类的对象,B类是String类的引用
[2]. 类型A的引用强转到类型B的引用 (引用 ---> 引用)
编译同样无法通过。这里面假设A类是Integer类的引用,B类是String类的引用
[3]. 方法返回值类型为A的引用强转赋值给B的引用
public static Integer test(){
return new Integer(123);
}
调用测试:
同样也是编译无法通过。
【提示给出的修正措施】
{1}. 将B的类型手动修改成A的类型
{2}. 将返回值为B类型的方法的返回值类型修改成A类型的返回值
【结论】当不具有继承关系的两个不同类型的数据进行转换的时候,无法通过编译
(2). 多态中常见的向下类型转换----编译通过,但是运行抛出异常
直接将父类的实例对象赋值给子类的引用
[1]. 举例1将父类Object的对象强转成子类Integer引用
看出可以通过编译,但是没有办法运行成功,抛出了ClassCastException异常。
[2]. 举例2将自定义父类Fu的对象强转成子类Zi引用
{1}. 自定义Fu类和Zi类
class Fu{}
class Zi extends Fu{}
{2}. 测试代码
【结论】
直接将父类对象强转成子类引用,编译可以通过但是运行抛出异常ClassCastException。
(3). 多态中常见的向下类型转换----编译和运行都通过
有时候为了增强代码的扩展性,在方法声明的返回值位置使用父类型引用。这个时候如果方法内部返回的实际上是子类类型对象,在调用这个方法的时候,返回值的类型就是父类类型。这个时候如果需要将方法的返回值类型强转成子类类型,编译和运行均正常。
[1]. 测试方法
public static Fu pacakgeZi(){
return new Zi();
}
[2]. 测试代码
Zi zi =(Zi) pacakgeZi();
System.out.println(zi);
[3]. 打印结果
Zi@6e1408
【结论】尽管指向对象的引用可能是父类类型,但是只要对象本身是子类类型的对象,那就可以通过强转被子类类型引用重新指向并通过编译和运行。
(4). Object类型对象向其他类型引用强制类型转换
[1]. 从Object类型对象或者引用向任意类型的引用进行强转一定能通过编译
编译能通过的原因就是Object类型是一切引用类型的直接或者间接父类。所以这种类型的强转一定能通过编译。
[2]. 运行时是否运行正常就要看指向的对象类型和最终子类引用类型是否为同一种类型
[3]. 编译通过但是运行抛出异常举例
{1}. 测试方法
public static Integer test(){
return new Integer(123);
}
{2}. 测试代码
Class clazz =TestI.class;
String s4 =(String) clazz.getMethod("test", null).invoke(null, null);
【分析】Method类的invoke方法返回值类型就是Object的,所以从Object类型向String进行强制类型转换一定是编译可以通过的。
【运行抛出异常】
抛出异常的原因:invoke方法实际上是对方法的调用,返回的对象一定是具体类型的对象。但是为了程序具有扩展性,invoke方法的返回值类型写成了Object类型。实际上Object类型就是返回值是指向实际子类的引用是Object类型的。所以运行的时候,对象的实际类型仍然子类对象。所以运行的时候,通过反射调用的test方法返回值类型是Integer类型的对象。Integer类型的对象没有办法被String类型的引用所指向,因此运行抛出异常。
[4]. 编译通过但是运行也正常举例
{1}. 测试方法
public static String test2(){
return "12345r";
}
{2}. 测试代码
Class clazz =TestI.class;
String s5 =(String) clazz.getMethod("test2", null).invoke(null, null);
System.out.println(s5);
【运行结果】12345r
【分析】强转后的引用变量的类型和原始对象的类型都是Integer类型,保持一致,运行正常。
(5). 向上转型和向下转型剖析
[1]. 向上转型
{1}. 向上转型编译和运行都成功的原因
父类类型引用可以指向子类对象不用强转就可以通过编译并且可以正常运行的原因就是子类从父类继承了父类的结构,所以通过父类的引用可以访问到子类从父类继承到的属性或者方法。
{2}1. 向上转型的优点:提高了程序的扩展性
{2}2. 向上转型的缺点
父类类型引用指向子类对象之后,通过父类引用只能访问子类从父类继承的部分,子类特有的部分无法被访问到。
[2]. 向下转型
向下转型的诞生背景
由于向上转型的缺点,当需要访问子类对象但是此时子类对象被父类引用指向的时候,就可以采用向下转型进行扩展以达到访问子类特有数据。由于强转之后,对象本身的类型和指向对象引用类型保持一致,因此编译和运行都正常。
(6). 判断通过编译之后运行是否抛出异常的步骤
强转之后/向上转型后判断是否抛出异常标准(前提通过了编译)
[1]. 先观察指向对象的引用的数据类型和对象本身的数据类型
[3]. 如果引用类型能访问的内容范围都在对象本身所属的类的范围,则这样的转型不但能够通过编译,也能运行正常。
e.g.1向上转型
class Fu{}
class Zi extends Fu{}
******测试********
Fu fuZi =new Zi();
System.out.println(fuZi);
打印结果:Zi@6e1408
【运行正常的原因】fuZi是Fu类型的引用类型是Fu,Fu的访问范围是Fu的构造方法 (隐式)。对象是Zi类型的,Zi类型的访问范围是Zi的构造方法和Fu的构造方法。对象所属类提供的访问范围>Zi类型引用可以访问的范围,所以运行正确。
e.g.2向下转型
public static Fu pacakgeZi(){
return new Zi();
}
******测试********
Zi zi =(Zi) pacakgeZi();
System.out.println(zi);
【运行正常的原因】Zi zi =(Zi) pacakgeZi();
System.out.println(zi);
【运行正常的原因】pacakgeZi返回的对象是Zi类类型,仅仅是被Fu类对象指向(向上转型)。调用并强转之后,引用类型对象本身都是Zi类型,访问范围一边大,因此运行正常。
[4]. 如果引用类型能访问的内容范围超过了对象本身所属的类的范围,则这样的转型不但能够只能通过编译,运行一定抛出ClassCastException异常
e.g.向下转型
Zi zi =(Zi) new Fu();
System.out.println(zi);
运行抛出异常。Zi zi =(Zi) new Fu();
System.out.println(zi);
运行抛出异常
【分析】对象的类型是Fu类型。访问的范围仅仅Fu类的构造方法。但是引用是Zi类型,访问的范围是Zi的构造和Fu的构造,因此左边的范围大于了右边实际可以访问的范围,因此会抛出异常。
【注意】无论子类是否显式有任何成员(哪怕子类里面是空的),但是编译之后,子类的可访问的范围一定是>父类可访问的范围。
(7). 总结
[1]. 当两个类类型之间的数据不具备继承关系的时候,强转失效,编译无法通过。
[2]. 当两个引用数据类型之间是具有子父类的继承关系的时候,如果要进行向下类型转换(从父类转换成子类),此时使用强制类型转换编译可以通过。
{1}. 当被指向的对象最初是子类类型对象,只不过中间被父类引用指向过。如果最后再次强转成被子类类型引用指向的时候,运行正常。
{2}. 当被指向的对象最初是父类类型对象,最后强转成被子类类型引用指向,尽管编译通过,但是运行的时候会抛出ClassCastException异常。
【数学上的语言】将访问的内容的范围看做集合
【数学语言】引用类型可访问的集合是对象类型可访问集合的子集,运行一定正确。否则一定抛出运行时异常。
【注意】编译通过但是运行是否报异常的原因
根本原因是:子类是从对父类类型的继承和变异。正常情况下,子类是在继承父类属性或者方法的基础上,又会增加新的属性或者新的方法。
2). 引用数据类型和基本数据类型之间的转换
(1). 基本数据类型和一般的引用类型
基本数据类型本身不是引用数据类型,两者在Java数据类型上是并列的
因此,原则上不能直接进行强制类型转换。【JDK5之前】
(2). 和基本数据类型有关系的引用数据类型 ----基本数据类型的包装类
[1]. JDK5引入了自动拆箱,因此一旦出现了从非包转类型向基本数据类型进行强转,一定是先将非包装类型转成包装类数据,再将包装类数据自动拆箱成基本数据类型。
【注意】基本数据类型不能强转成非包装数据类(Object和Number除外),无法通过编译
e.g1. 将整数强转成String
【分析】(String)56的过程可以理解为56先被自动装箱成Integer类型,但是Integer类型和String没有继承关系,所以编译无法通过。
e.g2. 将整数强转成Object (Integer类型的间接父类)
【分析】
编译成功的原因:567 首先被自动装箱成Integer类型的对象。Integer是Object的子类,因此直接自动向上转型即可。
运行成功的原因:Object类型的引用可访问的范围是子类对象Integer提供的访问范围的子集,因此运行成功!
[2]. 非包装类型 ----->包装数据类型的强转的可行性可行性分析
{1}. 如果非包装类型不是包装数据类型的父类,编译无法通过
e.g.将String类型对象强转成int
【分析】String类型的对象要先强转到int对象的包装类Integer,但是Integer类型和String类型不具备继承关系,所以直接编译出错。
{2}. 如果非包装类型是包装数据类型的父类,即Object类型或者Number类型,第一步的强转可以通过编译。
【注意】此时在()中填写的强转的类型一定是包装类类型,不能直接写基本数据类型。否则编译出错
e.g1. ()中直接填写基本数据类型,编译出错
e.g2. ()中只能填写基本数据类型对应的包装类,编译才能通过
{1}1. 此时非包装类型的引用指向的是不是对应包装类型的对象,这步骤强转运行时会可以抛出异常。因为强转后引用类型的的可访问的集合并不是对象提供的可访问的集合的子集。
【分析】运行时,强转到Integer的引用指向String类型的对象。Integer的引用可访问的范围不是String类型对象提供的访问范围的子集,因此抛出异常。
{1}2. 此时非包装类型的引用指向的是对应包装类型的对象,这步骤强转可以正常运行。因为强转后的引用类型的可访问的集合和对象的集合相等,所以运行正常
e.g1. Object ----> int
2. 强制类型转换 VS 专用转换方法
【综述】很多时候我们需要对不同数据类型的数据进行转换。有时候我们直接使用强制类型转换进行不同数据类型之间的转换,但是有时候又使用专用的转换的方法进行不同数据类型的数据之间的转换。这里面就阐述一下强制类型转换和专用转换方法的使用场合。
1). 强制类型转换使用的场合
(1). 示例
[1]. 测试方法
public static Fu pacakgeZi(){
Zizi =new Zi();
System.out.println("Inner: "+ zi);
return zi;
}
[2]. 测试代码
Fu ziFu = pacakgeZi();
System.out.println("after1:"+ ziFu);
Zi zi = (Zi)ziFu;
System.out.println("after2:"+ zi);
[3]. 打印结果
无论如何强转,运行的时候,对象的类型一定是唯一的,不会因为强转前后指向对象本身的引用类型变化而变化。
(2). 强制类型转换的结论
[1]. 强制类型转换使用的场合
这种方式主要用于两类数据之间具有继承关系的时候,可以直接使用强制类型转换。
[2]. 强制类型转换过程对象类型的唯一性
转换的过程仅仅是指向对象的引用的类型在变化,对象本身并没有变化。
【核心】强制类型转换作用的目标是指向对象的引用。强制转换的类型是引用的类型,从始至终和对象本身没有任何关系。
2). 专用转换方法被使用的场合
(1). 示例
[1]. 需求将字符串对象"12345"转化为整形数字
分析:字符串对象"12345"中的内容是1、2、3、4和5这五个字符拼接在一起的,所以逻辑上这个字符串也可以看成是数字,因此可以转换。
这里面直接使用Integer类的parseInt方法将String类型数据直接转换成Integer类型的数据。
[2]. 测试代码
String str ="12345";
System.out.println("转换前:"+ str.getClass().getName()+"@"+str.hashCode());
Integer it =Integer.parseInt(str);
System.out.println("转换后:"+it.getClass().getName()+"@"+it.hashCode());
[3]. 打印结果
可以看出来转换前后,对象不再是同一个对象。
(2). 强制类型转换的结论
[1]. 专有方法进行类型转换使用的场合
这种方式主要用于两种类型的数据并不具备继承关系,但是其中一方的数据在逻辑上是可以转换到另一方的类型上,此时要自行编写特殊的方法,并对一种类型的数据进行分析并转换到另一种数据上。
[2]. 专有方法进行类型转换对象类型的不唯一性
这种方式的转换过程中,对象是变化的。转换前后不仅仅是引用类型发生了变化,同时指向的对象也发生了变化。说明在转换方法内部一定使用了new关键字来在内存中开辟了新空间
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------