Widening Primitive Conversion & Narrowing Primitive Conversion

这几天看《Java Puzzlers》里面好些题目都是关于byte,char,int float,double的一些隐式和显示的类型转换问题,今天仔细看了一下JLS里面关于类型转换的问题.


Widening Primitive Conversion

主要是小范围类型向大范围类型进行转换,主要有19种
  • byte to shortintlongfloat, or double

  • short to intlongfloat, or double

  • char to intlongfloat, or double

  • int to longfloat, or double

  • long to float or double

  • float to double

以下几种宽类型转换是不会丢失信息的,(大范围类型的精度足够存放小范围类型的信息)
  • from an integral type to another integral type

  • from byteshort, or char to a floating point type

  • from int to double

  • from float to double in a strictfp expression 

以下几种转换是会丢失信息的:int to float, long to float, float to double

转换规则如下:
二进制补码的符号位扩展(char是两字节的无符号类型,所以是0扩展)


Narrowing Primitive Conversion

主要是大范围类型向小范围类型进行转换,主要有22种:
  • short to byte or char

  • char to byte or short

  • int to byteshort, or char

  • long to byteshortchar, or int

  • float to byteshortcharint, or long

  • double to byteshortcharintlong, or float

窄型类型转换是可能出现值的大小和精度的变化(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


在说一下基本数据类型在算术运算中的类型转换:
单目操作运算符:
  • 如果操作数的类型是ByteShortCharacter, or Integer,首先回进行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!今天就这样吧,类型转换的知识肯定不止这一点,以后遇到在补上吧!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值