基本数据类型的类型转换
Java中基本数据类型共有8种,分别是:布尔型boolean,字符型char和数值型byte/short/int/long/float/double。由于字符型char所表示的【单个字符】与Ascii码中相应【整形】对应,因此,有时也将其划分到数值型中。
基本数据类型中,布尔类型boolean占有一个字节(或一个二进制位,关于布尔类型的长度,详见另一篇笔记),由于其本身所代表的特殊含义,boolean类型与其他基本类型不能进行类型的转换(既不能进行自动类型的提升,也不能强制类型转换), 否则,将编译出错。
数值类型在内存中直接存储其本身的值,对于不同的数值类型,内存中会分配相应的大小去存储。相应的,不同的数值类型会有与其存储空间相匹配的取值范围。具体如下所示:
byte->【char】->short->int->long->float->double
图中依次表示了各数值类型的字节数和相应的取值范围。
在Java中,整数类型(byte/short/int/long)中,对于未声明数据类型的整形,其默认类型为int型;浮点类型(float/double)中,对于未声明数据类型的浮点型,默认为double型。
强制类型转换
当我们需要将【数值范围较大】的数值类型赋给【数值范围较小】的数值类型变量时,由于此时可能会丢失精度或者超界溢出(注:下面说的隐式转换除外),为了保证程序的可靠性、严谨性、健壮性......计算机不会自动帮我们进行转换,而是要求我们人为进行转换。我们称这种为强制类型转换。
float b = 1.5; // 编译出错 Type mismatch: cannot convert from double to float,1.5默认是一个double型,不能自动转换为float类型
float b2 = (float) 1.5; // 编译正确。进行强制类型转换,转换后精度变低了
float b3 = 1.5f; // 编译正确。直接用一个float类型的数值给float变量b3初始化
double d = 0;//这里的0其实是0D,而不是int 0
float f = (float) d;
int i2 = (int) f;//由于float类型的数值范围远大于int类型,所以强转后可能因超出int边界而导致数值异常
long l = (long) f;//注意,虽然long有64位,float只有32位,但float类型的数值范围仍是远大于long类型的,所有float->>long时也需要强制类型转换
强制类型转换时,如果超出范围较小的数据类型的数值范围(溢出),很可能出现一些意外情况,如下:
System.out.println((byte) 127 + " " + (byte) 128 + " " + (byte) 255 + " " + (byte) 256); //127 -128 -1 0
这种情况在编程语言设计层面是不可避免的,因为即使是同一数据类型的两个数进行简单的加减运算,都可能溢出,如下:
System.out.println(Integer.toBinaryString(Integer.MAX_VALUE));//[0]1111111111111111111111111111111,注意最前面的符号位0省略了
System.out.println(Integer.toBinaryString(Integer.MIN_VALUE));//10000000000000000000000000000000,32位,注意这里都是用原码表示的
System.out.println(Integer.toBinaryString(1));//1,前面所有的0都被省略了
System.out.println(Integer.toBinaryString(-1));//11111111111111111111111111111111,32位
System.out.println((Integer.MAX_VALUE + 1) == Integer.MIN_VALUE);//true,溢出了,提示:Comparing identical相同的 expressions
System.out.println((Integer.MIN_VALUE + (-1) == Integer.MAX_VALUE));//true,溢出了
所以,在进行强制类型转换时,只能由我们自己去保证代码的可靠性。
隐式类型转换
JVM在编译过程中,对于默认为int类型的数值,当赋给一个比int型数值范围小的数值类型变量时(在此统一称为数值类型k,k可以是byte/char/short类型),会进行判断,如果此int类型的数值超过k类型的数值范围,那么会直接编译出错;但是如果此int型数值尚在类型k的数值范围内,则JVM会自动进行一次隐式类型转换,将此int类型数值转换成k类型,而不需要我们手动强制类型转换。如上图中的虚线箭头。
接下来我们看看如下一个较为经典例子:
byte b1 = 3; //编译正确。如果此int类型的数值在byte类型的数值范围内,则jvm会自动进行一次隐式类型转换,将此int数值数值转换成byte类型
byte b2 = 1000; //编译出错 Type mismatch: cannot convert from int to byte,如果此int类型的数值超过k类型的数值范围,会直接编译出错
byte b3 = (byte) 1000; //编译正确。进行强制类型转换
我们再看另一个经典例子:
byte p = 3; // 编译正确:int到byte编译过程中发生了【隐式类型转换】。3是直接量,编译期间可以直接进行判定
int a = 3;
byte b = a; // 编译出错:cannot convert from int to byte。a为一变量,需要到运行期间才能确定
byte c = (byte) a; // 编译正确。强制类型转换
为什么将一个值为3的int类型变量a赋值给byte类型变量b时发生了编译错误呢?
区别在于前者3是直接量,编译期间可以直接进行判定;后者a为一变量,需要到运行期间才能确定,也就是说,编译期间为以防万一,当然不可能编译通过了。此时,需要进行强制类型转换。
自动类型提升
当将一个【数值范围较小】的类型赋给一个【数值范围较大】的数值型变量时,jvm在编译过程中均将此数值的类型进行了自动提升。如上图所示,从byte到double一路都可以自动类型提升。
byte->【char】->short->int->long->float->double
float f = 5;//编译正确。整数默认是int类型,此时直接发生了【自动类型提升】
double d = 1.5f;//编译正确。float类型的浮点数赋值给double类型,此时直接发生了【自动类型提升】
long l = 'a';//编译正确。字符'a'被当做int类型的97后赋值给long类型,此时直接发生了【自动类型提升】
不能 byte->char,char->short
由于字符型char所表示的【单个字符】与Ascii码中相应【整形】对应,因此,有时也将其划分到数值型中,所以上面中的"强转"和"隐转"都无条件包括char类型。
The char data type is a single 16-bit Unicode character. It has a minimum value of 'u0000' (or 0) and a maximum value of 'uffff' (or 65,535 inclusive).
但是由于char型具有两个字节(2*8=16-bit),且可看做是unsigned型(其他整型默认都是signed型),因此,这直接导致byte型不能自动类型提升到char(byte可能有负数),char型也不能自动类型提升到short(可能超出short边界)。
当然,byte型是可以直接提升到short等类型的。
byte b = 3;//隐式类型转换
char c1=b;//编译错误:Type mismatch: cannot convert from byte to char。byte型不能自动类型提升到char!
char c2 = 'a';
short s1 = c2;//编译错误:Type mismatch: cannot convert from char to short。char型不能自动类型提升到short!
short s2 = b;//编译正确。自动类型提升。byte型可以直接提升到short等类型!
实际上,你可以认为"不能 byte->char,char->short"是特例,也可以认为"能char->int,char->long,char->float,char->double"是特例,就看你怎么解释了。
自动类型提升规则
注意以下讨论的是二元操作符。
Java定义了若干适用于表达式的类型提升规则:
- 所有的byte型、short型和char型将被提升到int型(例外:final修饰的byte、short、char变量相加后不会被自动提升)
- 如果一个操作数是long型,计算结果就是long型
- 如果一个操作数是float型,计算结果就是float型
- 如果一个操作数是double型,计算结果就是double型
另一种归纳方式,《Java核心技术卷I》P43:
- 如果两个操作数其中有一个是double类型,另一个操作就会转换为double类型。
- 否则,如果其中一个操作数是float类型,另一个将会转换为float类型。
- 否则,如果其中一个操作数是long类型,另一个会转换为long类型。
- 否则,两个操作数都转换为int类型【这是最最最最重要的一条】
如下示例
byte b = 50;
char c = 'a';
short s = 1024;
int i = 50000;
float f = 5.67f;
double d = 0.1234;
double result = (f * b) + (i / c) - (d * s);
System.out.println("(f * b)=" + (f * b) + " (i / c)=" + (i / c) + " (d * s)=" + (d * s) + " result=" + result);
//(f * b)=283.5 (i / c)=515 (d * s)=126.3616 result=672.1384
//第一个表达式 f * b 中,b被提升为float类型,该子表达式的结果也提升为float类型。
//第二个表达式 i / c 中,变量c被提升为int类型,该子表达式的结果提升为int类型。
//第三个表达式 d * s 中,变量s被提升为double类型,该子表达式的结果提升为double型。
//最后,这三个结果类型分别是float,int和double类型,想减后该表达式的最后的结果就是double类型。
自动类型提升的一个经典案例
在分析自动类型提升时,不要被一些虚假的表象欺骗了,如下经典案例:
long l_maxInt = 21474_83647;//编译正确。
long l_bigerThanMaxInt = 21474_83648;//编译出错:The literal ** of type int is out of range。由于数值**【超过了int类型范围的边界】,故而其自身已经编译出错
long l_endWithL = 21474_83648L;//编译正确。如果一个整数以字母L或l结尾,则其类型为long类型;否则为默认的int类型
int i_outOfRange = Integer.MAX_VALUE + 1;//编译正确。虽然结果溢出,但是程序没有任何问题,所以需要开发中自己留意溢出问题
如上:定义long类型的 l_bigerThanMaxInt 变量时,将编译出错,原因在于 21474_83648 默认是int类型,而 21474_83648 已经超出int类型范围内的最大值,故而其自身已经编译出错,更谈不上赋值给long型变量 l_bigerThanMaxInt 了。
自动类型提升后的精度问题
在数值类型的自动类型提升过程中,数值精度问题要分情况来看
- 整型-->整型,自动类型提升后精度保持不变
- float-->double,自动类型提升后精度将变高
- 整型-->float/double,暂且定义为"精度将变低"
这个我表示很纠结啊,按上面的节奏来说的话,应该是精度将变高(比如int转为float),但是实际上数值都可能被"消尾",也即数据都已经由"精确值"变成了"近似值",那还哪来的"精度变高",很明显是"精度变低"啊?但是,怎么也找不到相应的说法。
float f = Integer.MAX_VALUE;
System.out.println(Integer.MAX_VALUE + " " + f);//21474_83647 2.1474_8365E9(呐,被"消尾"了,已经由"精确值"变成了"近似值")
等以后找到更多更权威的资料后再补充
再来一个经典案例
进行数学运算时的数据类型自动提升与可能需要的强制类型转换
byte b = 0, b1 = 1, b2 = 2;//【隐式类型转换】
b = b1 + b2; //编译出错,Type mismatch: cannot convert from int to byte。进行 b1 + b2 运算时会先将b1和b2【自动类型提升】成为int,运算结果也是int,赋给byte类型的b1时需要强制转换(向下转型)
b = b + 0;//编译出错,同上面的错误一样,运算时会先将b【自动类型提升】成为int,运算结果也是int
b += b2; //编译正确。【自增】没有类型提升问题。编译器在编译的时候自动进行了强转,相当于【b = (byte) (b + b2)】
//final修饰的byte、short、char
final byte b3 = 3, b4 = 4;
b = b3 + b4;//编译正确。有【final】修饰的byte、short、char变量相加后【不会被自动提升】为int类型
//一个奇葩的情况
final byte b6 = Byte.MAX_VALUE, b7 = 0, b8 = 1;
b = b6 + b7;//编译正确。
b = b6 + b8;//编译出错,Type mismatch: cannot convert from int to byte。注意,错误的原因和【long l = 21474_83648】一样,是因为由于数值**【超过了byte类型的范围】,故而其自身已经编译出错,而不是因为需要强制转换(向下转型)
b = 128;//编译出错,但是这里出错的原因我感觉和上面的原因是不一样的
2017-8-27