Puzzle 06: Multicast

cast的问题

以下代码都运行结果应该是?

System.out.println((int) (char) (byte) -1);

根据前面都经验, 简单地回答是-1未免没经过大脑, 但是否过于小心了呢? 也可能确实是-1啊, 实做一下, 输出为:

65535

也就是2^16 - 1.

以下分析运算过程:

  1. -1 从32位都int型转为8位的byte型, 值仍为-1, 但二进制表达变化:
    1. 11111111111111111111111111111111 int -1, signed -> 11111111 byte -1, signed
    2. 这里发生的是窄化转型(JLS 5.1.3)
  2. byte型的-1转化为char型. 这里就问题了, byte是带符号的, 而char是无符号类型. char里没有负数, -1 怎么从byte往char转呢? Java也考虑到这个问题了, 这种情况下将发生宽窄化转型(JLS 5.1.4). 具体, byte先宽转为int的, 再由int窄转为char型的. 具体如下:
    • byte -> int: 11111111 (8位)-> 11111111111111111111111111111111 (32位)
    • int -> char: 11111111111111111111111111111111 (32位) -> 1111111111111111 (16位)
  3. char型转为int型, 因为char是无符号的, 所以没有符号位扩展, 直接在高位补零:
    1. 1111111111111111 (16位) -> 00000000000000001111111111111111 (32位)
    2. 这个00000000000000001111111111111111的值就是2^16 - 1 = 65535.

鉴于转型在JLS中都规定, 如果想要把byte转成char, 但又不扩展符号位, 可以使用掩码:

cahr c = (char) (b & 0xff)

在用到转型的相关代码时, 最好把明确都要求写出来, 而不是猜测Java的默认实现机制.

以上的问题是在c为负数的情况, 当c为int型都正整数时, 是不会出现上面都意料之外的.

  1. JLS 5.1.2 Widening Primitive Conversion
  2. JLS 5.1.3 Narrowing Primitive Conversions
  3. JLS 5.1.4 Widening and Narrowing Primitive Conversions