星星之火-Java中的2+2=5和1+1=3等原理

本文最后更新于:December 3, 2021 pm

积土成山,风雨兴焉;积水成渊,蛟龙生焉;积善成德,而神明自得,圣心备焉。故不积跬步,无以至千里,不积小流无以成江海。齐骥一跃,不能十步,驽马十驾,功不在舍。面对悬崖峭壁,一百年也看不出一条裂缝来,但用斧凿,能进一寸进一寸,能进一尺进一尺,不断积累,飞跃必来,突破随之。

目录

我们接着上一篇说,上一篇讲了java中的1000==1000和100==100的区别。留下了一个为什么2+2=5的问题。现在说一说。

先再看一下那个代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Class cache = Integer.class.getDeclaredClasses()[0]; //1
Field myCache = cache.getDeclaredField("cache"); //2
myCache.setAccessible(true);//3
Integer[] newCache = (Integer[]) myCache.get(cache); //4
newCache[132] = newCache[133]; //5

//System.out.printf("2 + 2 = %d",2 + 2); //输出:5
//System.out.println(Integer.valueOf(4)); //输出:5
int a = 2;
int b = a + a; //在输出的时候,a 和 b 分别装箱成了 Integer 对象,
System.out.printf("%d + %d = %d", a, a, b); //输出:5
}

后面的那些输出语句不是重点,重点是前面的5句代码。
注意:我多放了一条输出语句,这是为了后面的时候好理解。所有,要记住输出4,但结果为5。

1.解释

由上一篇我们知道,java中Integer在小整数范围内的整形数据是有缓存的,获取到的都是缓冲区中的。而那5句核心代码中,前四行获取了 Integer 类中的缓存区,第五行将缓存区133位置的对象放到了132位置。

重点理解:cache[]数组存了-128~127之间的数据,即cache[0] = -128; cache[1]=-127….cache[132] = 4。

经过推算,可以发现,133位置放的是5,132原来放的是4。所以现在132位置就从之前的4变成了现在的5。

这里通过反射缓存中的第133号数据(既整数5)赋值给了第132号数据(既整数4),所以4就会变成5来表示。在使用int数据计算时结果是正常的,但是在打印时由于做了装箱,int数据变成了Integer,这时会采用缓存,所以4就会打印出5来。

再看看以下代码,如何让1+1=3:

1
2
3
4
5
6
7
8
public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException {
Class cache = Integer.class.getDeclaredClasses()[0];
Field c = cache.getDeclaredField("cache");
c.setAccessible(true);
Integer[] array = (Integer[]) c.get(cache);
array[130] = array[131];
System.out.printf("%d", 1 + 1); // 3
}

原理和上面的一样,
原来:130位置是2,131位置是3;现在用131的给130的覆盖了,所以现在130的是3。而1+1是输出130位置的数。

2.参考

内部类IntgerCache的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
}
high = h;

cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}