MAX_VALUE
/**
* A constant holding the maximum value an {@code int} can
* have, 2<sup>31</sup>-1.
*/
@Native public static final int MAX_VALUE = 0x7fffffff;
Integer.MAX_VALUE 是整型可以支持的最大数。用 8 位 16 进制表示,即 32 位 2 进制,最大值为 2^31-1
思考
因为 Integer 是有范围的 -2^31 ~ 2^31-1,所以当我们在 Integer 类型的变量达到最大值后 +1 ,会发生什么?
分析
测试代码
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MAX_VALUE+1);
System.out.println(Integer.MIN_VALUE);
打印结果分别为:2147483647 \ -2147483648 \ -2147483648 ,很明显 MAX_VALUE +1 后变成了 MIN_VALUE。
原理
在计算机中,数据都是以二进制表示的,运算都是基于补码进行。
了解原码、反码、补码(以 8 位平台,3、-5 为例)
首位为符号位, 0 表示正数 1 表示负数,码型转换不改变符号
原码:
3 :0000 0011
-5 :1000 0101
反码: 正数的反码(原码) ,负数的反码(符号位不变,其他位取反)
3 :0000 0011
-5 :1111 1010
补码: 正数的补码(原码) ,负数的补码(反码+1)
3 :0000 0011
-5 :1111 1011
学会加减法
计算机中的加减法都是基于二进制补码进行的,例如计算 3+3 和 3-5
3+3 可以转化补码运算为 0000 0011 + 0000 0011 => 0000 0110
符号位是 0 为正数,其原码 = 补码
(原码)0000 0110 => 2^2 +2^1 = 6
3 - 5 即 3 +(-5)可以转化补码运算为
0000 0011+1111 1011 =>1111 1110
符号位为 1 表示负数
(补码)1111 1110 =>(反码=补码-1)1111 1101=>(原码=反码取反)1000 0010 =>-(2^1) = -2
计算Integer.MAX_VALUE + 1
根据上面的计算原理,我们可以很轻松的得到
Integer.MAX_VALUE + 1 = (2^31-1) + 1 => (补)0111 1111 ... 1111 1111 + (补)0000 .... 0001
=> (补)1000 0000 ... 0000 0000 =>(反)1111 1111 ... 1111 1111=>(原)1000 0000 ... 0000 0000
=> -2^32 = Integer.MIN_VALUE
避坑指南
根据 MAX_VALUE + 1 = MIN_VALUE ,下面这段代码就可以说是丧心病狂了:
for(int i = 0;i<=Integer.MAX_VALUE;i++){
//TODO
}
当 i 遍历达到最大之后,i++ 变为 MIN_VALUE,实现无限循环 ,直接跑路
但是大部分时候我们写这种逻辑是无心操作,比如我们循环条件中的最大值是通过参数传递过来的,如下:
public void doForeach(@NonNull Integer min_value,@NonNull Integer max_value){
if(Integer.compare(min,max)>0){
throw new RuntimeException("min_value must be less than or equal to max_value");
}
for(int i = min_value;i<=max_value;i++){
//TODO
System.out.println(i);
}
}
这段代码看似没什么异常,但是却暗藏杀机,因为不确定传入的参数到底是什么,因此问题会很难复现,不仔细排查的话,很难料想到传进来的 max 会是个MAX_VALUE。
当然,知道原因的我们解决问题也很简单,把循环中的条件 i<=max_value 改为 i<max_value ,或者加一个判断就可以了
if(max==Integer.MAX_VALUE){
throw new RuntimeException("max_value is out of range");
}