为何2^32范围内整数溢出未抛出ArithmeticException异常?
嘿,这个问题我碰到过好多次了,咱们一步步来理清楚:
Java里整数运算的溢出是「静默式」的:
当你用int这类基础整数类型做运算时,如果结果超出了类型的取值范围,Java不会主动抛出ArithmeticException,而是直接把结果高位截断,只保留对应类型位数的低部分(比如int是32位,就留低32位),这就是大家常说的「环绕溢出」。你的例子具体拆解:
65535的十六进制是0xFFFF,它的平方是0xFFFF * 0xFFFF = 0xFFFFFFFFFFFF。但int只能存32位,所以会砍掉高位,只留低32位的数值。换算成十进制更直观:65535×65535=4294836225,而int的最大值是2147483647,这个结果比2^32(4294967296)小一点,用4294836225减去4294967296,得到的就是-131071——也就是你看到的那个奇怪的负数,这是截断后按补码规则解析出来的结果。你说的「赋值超范围抛异常」是另一种场景:
要是你直接给int赋值一个超出范围的字面量(比如int x = 2147483648;),编译器直接就会报错,因为这个数根本塞不进int里。但运算时的溢出不一样,Java默认不做溢出检查——这是语言设计时为了性能考虑的选择,总不能每次加减乘除都额外做一次范围判断吧?想让溢出时抛异常?有办法:
如果你希望运算溢出时立刻抛出ArithmeticException,可以用java.lang.Math里的multiplyExact、addExact这些方法,比如把你的代码改成这样:int x = 65535; System.out.println(Math.multiplyExact(x, x));这时候运行就会抛出
ArithmeticException: integer overflow,完全符合你预期的异常行为。
内容的提问来源于stack exchange,提问作者Henry Li

