先介绍向上转型和向下转型,再介绍自动类型转换和强制类型转换
1、向上转型和向下转型
先给出大致的结论
向上转型是父类引用指向子类对象
向下转型是将"父类引用指向子类对象"的父类引用再转给子类引用
下面看分析过程
java中,"父类引用指向子类对象"叫向上转型。实际还有一种情况的向上转型:new出来一个类的对象赋值给了此类实现的一个接口(如,类A实现接口B,即A implements B,向上转型就是B b=new A();)。这里是认为接口的向上转型和类的向上转型是有区别的,那向上转型区分类、接口,自然向下转型也区分类和接口,分两类讨论->一般都是讲类的向上转型和类的向下转型,这里又划分出接口的其实逻辑相近,没啥新的东西
看向下转型,也有两种:一种是子类的实例赋值给父类引用,再将父类引用赋值给子类引用;另一种情况是实现类的实例赋值给接口引用,再将接口引用赋值给实现类引用
看具体代码理解这四种转型
Cat类继承Animal类,Cat类是子类,Animal类是父类
父类Animal有一个成员变量String head和一个成员方法String getHead()
子类Cat类有一个成员变量String eyes和一个成员方法String getEyes()
下面画图理解(想画类图的,忘了,勉强看看吧;再下面举例就不放代码了直接看图理解)
类的向上转型和向下转型很简单,记住
1、前提是父子类有继承关系
2、向上转型是父类引用指向子类对象
Animal a=new Cat();
注:向上转型不用强制转型
3、向下转型是先经历向上转型后再将父类引用再转给子类引用(当然子类对象及后来的子类引用是同一个类型的)
Animal a =new Cat(); 向上转型
Cat c=(Cat)a; 向下转型
注:需要强制转型
再看接口的向上转型和向下转型,原理同类的向上转型和向下转型
所以,接口的向上转型和向下转型是
1、前提是父接口和子类间有实现关系
2、向上转型是父接口的引用指向子类对象
Ianimal i=new Tiger();
3、向下转型是先经历向上转型后再将父接口的引用再转给子类引用(当然子类对象及后来的子类引用是同一个类型的)
Ianimal i =new Tiger(); 向上转型
Tiger t=(Tiger)i; 向下转型
讲了四种,其实记向上转型、向下转型还是记类的向上/向下转型就行,分类分接口的情况意义不大
下面测试向上、向下转型有啥区别及为什么要转型
还是先给出(有区别的)结论
体现在调用方法的不同
上图意思是
左图,(向上转型)父类引用指向子类对象
父类有a方法,子类无a方法,调用父类的a方法
父类有a方法,子类有a方法,调用子类的a方法
其余情况均不能调用
右图,(向下转型)子类引用指向子类对象(自然该子类引用是经历过向上转型的)
同理(注:下面"父类"其实就是在说的"子类引用")
父类有a方法,子类无a方法,调用父类的a方法
父类有/无a方法,子类有a方法,都调用子类的a方法
对比可知,向下转型多允许一种父无子有的情况,原理后面说,一般记结论就行
文字记忆上图就是
向上转型后,子无父有用父类,子有父有用子类,其他都不行(父无子有、父无子无)
向下转型是:父类有子类无的用父类,父类有子类有用子类
看测试
测试类testClass
向上转型:可以看出把Cat()的对象赋值给父类引用animal,不能调用getEyes()。此方法是Cat中的方法,而Animal类中无此方法
向下转型:此时却发现cat_s引用不仅能访问Animal类成员,还能访问Cat中新增的成员
测试程序testInterface.java
接口的向上转型和向下转型实际同理
向上转型:可以看到new出来的Tiger对象赋值给了IAnimal接口引用,此时animal只能访问接口的方法sing()而不能访问Tiger类中新增的成员方法
向下转型:把animal再赋值给了tiger引用后,发现tiger既可以访问接口方法也可以访问Tiger类中新增的方法
这是因为
向上转型之后会失去父类中没有定义的方法,只能调用父类中定义的方法->即前提是父类有无该方法,有再看子类有无重写或自己有。所以只允许两种情况:1是父有子有用子类的方法;2是父有子无用父类的方法
向下转型后因为都是指向子类对象,所以调用的当然全是子类的方法->子类有就用子类,子类没父类有就用父类的方法
再注意向下转型:转型时转的类型必须是父类对象所指的真实子类对象,不是任意的强制类型转换->也就是父类引用再转给子类引用必须这个子类引用的类型是同向上转型时的子类对象的类型)
推荐记结论就行,即会用就行;下面看为什么需要转型(我们知道推荐使用向上转型)
父类作为参数,调用时子类作为参数,就是利用了向上转型。向上转型可以减少重复代码,使代码变得简洁,增强可扩展性。体现了JAVA的抽象的面向对象编程思想
为什么推荐父类引用指向子类对象
因为父类型的形参通用性更强(指的是编写时写父类形参,真正调用时才传入具体子类对象,拓展性好),子类引用指向子类对象能够可以调用覆盖方法和子类独有方法,太固定就拓展性不好
补充1:前面说的向下转型是正确的、安全的,下面介绍不正确、不安全的向下转型
Fruit f=new Fruit();
Apple aaa=(Apple)f; 不安全的向下转型,编译无错但会运行会出错
aaa.myName();
aaa.myMore();
注意第一行代码,=右侧是父类型的对象不是子类型的对象
f是父类对象,子类的实例aaa肯定不能指向父类f啊
为了安全的类型转换,最好先用 if(A instanceof B) 判断一下
补充2
2、下面研究基本数据类型的转换
直接cv笔记内容了
整数型字面量被当成int处理
自动类型转换
过大的整数
强制类型转换
大类型转为小类型需要强转
1.语法不支持,所以为了编译通过要强转,即加强制类型转换符
2.强转可能会精度损失(像上面的100L强转为int不会损失是因为100太小了)
我的理解:小类型遇到大类型,比如long abc=12;首先右边字面量是默认int类型,字面量要符合int范围,再赋值给左边long类型变量,小范围赋值给大范围合理,4字节赋值给8字节的就直接补上4字节的0(就这个意思0000=0000,00是补上去的)
再大类型赋值给小类型需要强转就需要砍掉多出的字节部分,所以就有可能导致精度缺失
精度损失以及不超范围可以直接赋值byte,short
大类型赋值给小类型的需要强转,再为了方便编写,java允许int类型字面量赋值给小类型(整数类型的byte,short)不需要加强制类型转换符,前提是右边的值不超出左边的数据类型范围
我还想=右边是short类型的字面量赋值给左边的byte也可以不加强制类型转换符吗?
不一样,因为你右边默认是int类型的字面量,如果byte b2=(short)128;是报错的’mismatch,不能short to byte’;要是先声明一个short类型的变量再赋值给byte类型的变量,这是之后讨论的(=两边都是变量),不过这个的结果是需要强转
short s=124;
byte b2=(byte)s;
之前将这种行为简单认为是’整数类型自带转换器’,现在解释清楚了;比如byte b=127;可以不用强制转换;因为int类型的127符合byte的范围,但是byte b2=128;就不可以了,需要byte b2=(byte)128;
整数没有超范围可以赋值给char
总结
大类型强转为小类型需要加强制类型转换符,float f=(float)12.3;但整数类型为了方便编写,允许右边的字面量没有超出左边的数据类型的范围不需要强制转换;超出范围则还是需要强制转换,如byte b2=(byte)128;
左边是byte,short,char类型的变量,右边是默认int类型的字面量,则在不超出左边范围的情况下可以不用加强制类型转换符,相当于默认会加
int和char可以互转,不超出char范围则右边的字面量可以直接赋值;但是char c=65536;超出范围还是需要强制转换char c2=(char)65536;注意char的范围是0-65535
java八种基本数据类型,除了boolean都可以互转,现在看char和int互转
char为16位的数据,为无符号数,其范围为:0 至 2 ^ 16 -1,即 0 - 65535,用十六进制码来看,则为:’\u0000’ - ‘\uffff’
=左边是char类型变量,右边是int类型的字面量
不超出左边的数据范围,右边字面量可以直接赋值;超出数据范围则仍要加强制类型转换符
=左边是int类型变量,右边是char类型的字面量
符合小类型遇到大类型自动转换,属于隐式转换
现在再看=右边是变量的情况
之前=右边是字面量,可以直接将一个数字赋值给char类型的变量,那现在我们可以直接将一个int类型的变量赋给一个char类型的变量吗?
这里涉及到隐式转换与显式转换的知识。简单地来讲就是从一个范围较小的数字转换到一个范围较大的数字,如32位的int类型到64位的long是可以隐式转换的,即可以直接把一个int类型的变量赋给一个long类型的变量。
类似的我们可以直接把一个16位的char类型的变量赋给一个32位的int类型的变量,但是反之便不可以,需要用显式转换。所以答案是不行
或许这么理解,变量是存放数据的基本单元也就是房子,声明的数据类型能决定你的房子多大;=右边是字面量,那么看你字面量的大小能不能住进=左边这个房子,比如byte b=126;房子够大可以入住,再byte b2=129需要强制转换;默认带强制类型转换符就是默认你瘦身之后才可以入住,再看=右边是变量的情况,就是房子能不能对等,比如int i=156;byte b3=i;那么i的房子太大要瘦身就要加强制类型转换符,所以byte b3=(byte) i;这样才对
示例
int num=97;
char c=num; //编译报错,显示类型不匹配
而
char c2=97;
int num2=c2;
正确;可以看出从char到int是可以隐式转换的
如果我们确实需要int型引用到char型引用的话,需要显式的强制转换,对上面示例进行修改
int num=97;
char c=(char)num; //这样就对了
在上面,我们看到了char其实也是一个数字,所以可以利用这样的特性在编程的时候使用一些小技巧
比如有字符为'1',我们想将其变为数字1,或者我们想将一个int类型的数字1转换为字符'1',这时我们就可以这样写
//1->’1’
int num2=1;
char c2=(char)(num2+’0’); //=(char)(1+48)
// char->int('1'->1)
char c = '1';
int a;
System.out.println(a = c - 48);// 1
解释:因为字符’0’对应十进制的48,’1’对应49,遇到int类型的都转为int的进行运算,转为最大整数类型进行运算(这个结论在后面)
byte,short,char的混合运算
short s=short +int;即short=int,需要加强制转换符;编译器看到+知道是加法,加完之后的结果再给左边的变量就不能算自带转换器了要记住
当右边要进行运算时,比如有加号存在就不会自带类型转换器了,需要强转就必须加强转类型转换符
多种数据类型混合运算
不超过int范围的多个变量进行混合运算都各自转为int类型再做运算;如果多个变量进行混合运算,其中一个超出int则都按超出int的那个最大数据类型进行运算;强转可能会有损失,但是不加强制转换符连编译都报错
浮点型数据