这几天看《Java Puzzlers》里面好些题目都是关于byte,char,int float,double的一些隐式和显示的类型转换问题,今天仔细看了一下JLS里面关于类型转换的问题.
Widening Primitive Conversion
主要是小范围类型向大范围类型进行转换,主要有19种
-
byte
toshort
,int
,long
,float
, ordouble
以下几种宽类型转换是不会丢失信息的,(大范围类型的精度足够存放小范围类型的信息)
-
from an integral type to another integral type
以下几种转换是会丢失信息的:int to float, long to float, float to double
转换规则如下:
二进制补码的符号位扩展(char是两字节的无符号类型,所以是0扩展)
Narrowing Primitive Conversion
主要是大范围类型向小范围类型进行转换,主要有22种:
窄型类型转换是可能出现值的大小和精度的变化(lose information)
当整型S向整型D(大小是n个字节)进行窄型转化时,最终结果是截取整型S的低N个字节
int i = -1; // i = 0xffffffff
short s = (short)i; //s = 0xffff
当浮点型S向整形D进行转换时,遵循规则如下:
1. 当D是int or long:
1) 当S是NAN,D = 0
2)其他情况,如果D能表示S,则D = S,若不能表示D为D的最小值(当S比较小的时候),D为它的最大值(当S比较大的时候)
2. 当D是byte ,char, short:
先将D看成int or long,然后将得到的结果窄型转换成byte,char,short
针对浮点型向整型我们看一个例子:
public static void testNarrowPrimitiveConversion() {
System.out.println("===============NarrowPrimitiveConversion===============");
float fmin = Float.NEGATIVE_INFINITY;
float fmax = Float.POSITIVE_INFINITY;
System.out.println("long: " + (long) fmin + ".." + (long) fmax);
System.out.println("int: " + (int) fmin + ".." + (int) fmax);
System.out.println("short: " + (short) fmin + ".." + (short) fmax);
System.out.println("char: " + (int) (char) fmin + ".."
+ (int) (char) fmax);
System.out.println("byte: " + (byte) fmin + ".." + (byte) fmax);
}
===============NarrowPrimitiveConversion===============
long: -9223372036854775808..9223372036854775807
int: -2147483648..2147483647
short: 0..-1
char: 0..65535
byte: 0..-1
从结果可以看到float转换成long, int ,char都是它们对应类型的最大值和最小值,而对于short和byte的结果,可以这么理解,先转换成int的结果,在进行窄化类型转换。
转换成int的结果是 Tmin = 8000 0000 Tmax = 7fff ffff,转换成short和byte都是取低位两个字节为0x0000和0xffff,所以结果如上所示.
整合宽型转换和窄型转换,在来看个例子,它是《Java Puzzlers》的第六个问题:
public static void main(String[] args) {
System.out.println((byte) -1);
System.out.println((int)(char)(byte) -1);
}
程序第一行:-1是个int型字面量,4个字节,0xffffffff,窄型转换成byte,截取低位1个字节为0xff,输出-1。
程序第二行:(byte)-1的结果是0xff,符号扩展成char:0xffff,在转换成int,因为char是无符号类型,是0扩展的,所以最终结果是0x0000 ffff,输出是65535
在说一下基本数据类型在算术运算中的类型转换:
单目操作运算符:
- 如果操作数的类型是
Byte
,Short
,Character
, orInteger,首先回进行unbox conversion,然后转换成int
- 如果操作数的类型是Long,Double,Long,直接进行unbox conversion
- 如果是byte,short,char,则直接转换成int
单目操作运算符有如下几个:创建数组的表达式,数组选择,正负符号,位取反,左移,右移等,JLS里面有个解释非常好的例子如下所示:
class Test {
public static void main(String[] args) {
byte b = 2;
int a[] = new int[b]; // dimension expression promotion
char c = '\u0001';
a[c] = 1; // index expression promotion
a[0] = -c; // unary - promotion
System.out.println("a: " + a[0] + "," + a[1]);
b = -1;
int i = ~b; // bitwise complement promotion
System.out.println("~0x" + Integer.toHexString(b)
+ "==0x" + Integer.toHexString(i));
i = b << 4L; // shift promotion (left operand)
System.out.println("0x" + Integer.toHexString(b)
+ "<<4L==0x" + Integer.toHexString(i));
}
}
对于双目运算符:
基本数据类型的转换如下:
- 如果操作数是引用类型,直接进行unbox conversion
- 如果两个操作数中有double,则另外一个也转换成double
- 如果两个操作数中有float,则另外一个也转换成float
- 如果两个操作数中有long,则另外一个也转换成long
- 其他情况都两个操作数转换成int
双目运算符主要包括算术运算,逻辑运算,关系运算,在贴一个JLS的例子
class Test {
public static void main(String[] args) {
int i = 0;
float f = 1.0f;
double d = 2.0;
// First int*float is promoted to float*float, then
// float==double is promoted to double==double:
if (i * f == d) System.out.println("oops");
// A char&byte is promoted to int&int:
byte b = 0x1f;
char c = 'G';
int control = c & b;
System.out.println(Integer.toHexString(control));
// Here int:float is promoted to float:float:
f = (b==0) ? i : 4.0f;
System.out.println(1.0/f);
}
}
这里要提的是在JLS中有一句话是这么说的 In certain cases, the conditional operator
? : 也使用双目运算符的转换规则,这让我想起了《Java Puzzlers》的第八个题目
public static void main(String[] args) {
char x = 'X';
//final int i = 0;
int i = 0;
System.out.println(true ? x : 0);
System.out.println(false ? i : x);
}
如果按照双目运算符的规则来看的话,因为0,和i都是int,那么x会转换成int,所以输出应该是两个88,但是结果不是的!!!第一个输出的X,第二个输出的是88
书中给出的解释是这样的:
1.如果第二个和第三个操作数具有相同的类型,那么它就是条件表达式的类型。换句话说,你可以通过绕过混合类型的计算来避免大麻烦。
2.如果一个操作数的类型是 T,T 表示 byte、short 或 char,而另一个操作数是一个 int 类型的常量表达式,它的值是可以用类型T表示的,那么条
件表达式的类型就是 T。(这就是为什么第一个输出是X的缘故,0是常量表达式,所以输出是System.out.println(char))
3.否则,将对操作数类型运用二进制数字提升(就是双目运算符的转换规则),而条件表达式的类型就是第二个和第三个操作数被提升之后的类型。
然后我看了一下JLS对条件表达式的官方说明,上述3条规则只是一个子集而已,但是对于一般的问题上述规则是可以解决的!
OK!今天就这样吧,类型转换的知识肯定不止这一点,以后遇到在补上吧!