如下代码:

public class Example005 {
	public static void main(String[] args) {
		System.out.println("out1="
				+ Long.toHexString(0x100000000L + 0xcafebabe));
		System.out.println("out2="
				+ Long.toHexString(0x100000000L + 0xcafebabeL));
	}
}


    输出结果:

out1=cafebabe
out2=1cafebabe


    原因分析:

    首先要知道的一个问题是,十进制数是依靠前导一元操作符(+/-)来表达正负的。而十六进制、八进制、二进制在计算机中是用补码表示的。补码的优势在于:使用补码,可以将符号位和其它位统一处理;同时,减法也可按加法来处理。另外,两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。在上述程序中,数字 0xcafebabe是一个 int 常量,它的最高位被置位了 ,所以它是一个负数。它等于十进制数值-889275714。

    out1执行的这个加法是一种混合类型的计算(mixed-type computation):左操作数是 long 类型的,而右操作数是 int 类型的。为了执行该计算,Java 将int 类型的数值用拓宽原始类型转换提升为一个 long 类型,然后对两个 long 类型数值相加。因为 int 是一个有符号的整数类型,所以这个转换执行的是符合扩展:它将负的 int 类型的数值提升为一个在数值上相等的 long 类型数值。这个加法的右操作数0xcafebabe 被提升为了 long 类型的数值0xffffffffcafebabeL。这个数值之后被加到了左操作数 0x100000000L 上。当作为 int 类型来被审视时, 经过符号扩展之后的右操作数的高 32 位是-1,而左操作数的高 32 位是 1,将这两个数值相加就得到了 0,这也就解释了为什么在out1的前导 1 丢失了。

    在out2中,0xcafebabeL使用十六进制字面常亮来表示,避免了有破坏力的符号扩展,所以打印结果也就正确了。


(注:本【java解惑】系列,均是博主阅读《java解惑》原书后,将原书上的讲解和例子部分改编,然后写成博文进行发布的。所有例子均亲自测试通过,并共享在github上。通过这些例子,激励自己,惠及他人。同时,本系列所有博文会同步发布在博主个人微信公众号(搜索“爱题猿”或者“ape_it”),方便大家阅读。如果文中有任何侵犯原作者权利的内容,请及时告知博主,以便及时删除;如果读者对文中的内容有异议或者问题,欢迎通过博客留言或者微信公众号留言等方式共同探讨。)

源代码地址:https://github.com/rocwinger/java-disabuse