在开始对于 Java 的整型类型探讨之前,让咱们先看下这段神奇的Java代码:

public static void main(String[] args) throws Exception {      doSomethingMagic();      System.out.printf("2 + 2 = %d", 2 + 2);}

执行后果,控制台打印的内容:2 + 2 = 5

那么 doSomethingMagic 办法到底做了什么神奇的事件呢?

private static void doSomethingMagic() throws Exception {   Class cache = Integer.class.getDeclaredClasses()[0];   Field c = cache.getDeclaredField("cache");   c.setAccessible(true);   Integer[] array = (Integer[]) c.get(cache);   array[132] = array[133];}

所以这个例子其实蕴含了 Java 中整型类型 Integer 的一个知识点。

可能有的敌人对于 doSomethingMagic 外面的代码有点摸不着头脑,让咱们先查看上图第17行 2 + 2 反编译进去的代码:

编译器将 2 + 2 的值先计算出来,等于 4.

最初 System.out.println 打印进去的值,实际上是 Integer.valueOf(4) 表达式的返回值。

那么咱们进一步查看 JDK 里 Integer.valueOf 的实现:

下面的实现代码,从 830 行到 832行,逻辑是:如果 valueOf 办法调用的参数 i 在 IntegerCache.low 和 IntegerCache.high 之间,即位于 [-128, 127] 的闭区间,则间接从 IntegerCache 这个外部类的缓存数组里取出元素,作为办法的返回值。

只有当输出参数 i 不在 [-128,127] 区间内,才执行代码第 832 行,基于输出参数 i 创立一个新的 Integer 实例。

带着这个理念,咱们再看 doSomethingMagic 的代码就分明多了。

这段代码的作用是拜访 Java Integer 类实现的某个外部类保护的缓存数组,对其中一个元素的值进行批改。具体解释如下:

  • Class cache = Integer.class.getDeclaredClasses()[0]:通过 Integer.class.getDeclaredClasses() 办法获取 Integer 类的所有外部类,而后 [0] 取出第一个外部类,也就是 Integer$IntegerCache 类的 Class 对象,并将其赋值给变量 cache. Integer$IntegerCache 类是 Java 8 引入的一个外部类,它用于缓存整数对象(Integer 类型的对象),默认缓存范畴是 -128 到 127。
  • Field c = cache.getDeclaredField("cache"): 接下来,通过 cache.getDeclaredField("cache") 办法获取 cache 字段的 Field 对象,并将其赋值给变量 c。该字段存储了 Integer 类的缓存数组。
  • c.setAccessible(true):通过 c.setAccessible(true) 办法设置 c 字段的拜访权限,以便能够通过反射来批改该字段的值。
  • Integer[] array = (Integer[]) c.get(cache):通过 c.get(cache) 办法获取缓存数组的值,并将其强制转换为 Integer 类型的数组,而后将其赋值给变量 array.
  • array[132] = array[133]:最初,将缓存数组中下标为 133 的元素的值赋给下标为 132 的元素,从而批改了缓存数组中下标为 132 的元素的值。

咱们从 Eclipse 调试器里发现,Integer 外部类的 cache 数组里,第 132 个元素的值为 4,第 133 个元素的值为 5. 原本 Integer.valueOf 办法,对于输出 4,从Integer 外部类缓存数组里,返回第 132 个元素的值,即 4.

当初这个元素的值被第 133 个元素即 5 笼罩了,所以最初失去了 2 + 2 = 5。

用一句话概括这个场景:2 + 2 = 4 = Integer.valueOf(4) = 5 ( 因为 4 在 Integer 外部类 cache 数组里对应的记录,曾经被咱们的 doSomethingMagic 办法,显式从 4 替换成了 5)。

这就是 Java 里实现 2 + 2 = 5 这个算式的办法之一。