深入理解 Java 的整型类型:如何实现 2+2=5?

简介: 深入理解 Java 的整型类型:如何实现 2+2=5?

在开始关于 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] 取出第一个内部类,也就是 IntegerI n t e g e r C a c h e 类的 C l a s s 对象,并将其赋值给变量 c a c h e . I n t e g e r IntegerCache 类的 Class 对象,并将其赋值给变量 cache. IntegerIntegerCache类的Class对象,并将其赋值给变量cache.IntegerIntegerCache 类是 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.


14a35aebb1ce286f5cdae93c6768b8eb_0431398ed7b0225a6bfd07d6bb60a87d.png


现在这个元素的值被第 133 个元素即 5 覆盖了,所以最后得到了 2 + 2 = 5。


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


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


相关文章
|
13天前
|
存储 Java 编译器
深入理解 Java 泛型和类型擦除
【4月更文挑战第19天】Java泛型是参数化类型,增强安全性与可读性,但存在类型擦除机制。类型擦除保证与旧版本兼容,优化性能,但也导致运行时无法访问泛型信息、类型匹配问题及数组创建限制。为应对这些问题,可使用Object类、instanceof运算符,或借助Guava库的TypeToken获取运行时类型信息。
|
24天前
|
存储 安全 Java
大厂面试题详解:java中有哪些类型的锁
字节跳动大厂面试题详解:java中有哪些类型的锁
50 0
|
2天前
|
Java
Java String类型转换成Date日期类型
Java String类型转换成Date日期类型
|
2天前
|
关系型数据库 MySQL Java
Java时间转换为MySQL中的INT类型时间戳
Java时间转换为MySQL中的INT类型时间戳
|
3天前
|
Java
【Java探索之旅】数据类型与变量 字面常量 整型变量
【Java探索之旅】数据类型与变量 字面常量 整型变量
12 0
|
3天前
|
Java 编译器
【Java探索之旅】解密Java中的类型转换与类型提升
【Java探索之旅】解密Java中的类型转换与类型提升
11 0
|
6天前
|
安全 Java 程序员
Java 泛型类型:变幻中的不变性
【4月更文挑战第21天】
4 1
Java 泛型类型:变幻中的不变性
|
7天前
|
XML SQL 前端开发
【Java】实体字段传参类型线上问题记录
【Java】实体字段传参类型线上问题记录
19 1
|
9天前
|
存储 Java
JAVA变量类型
JAVA变量类型
13 0
|
14天前
|
存储 算法 安全
什么是Java泛型类型?
【4月更文挑战第13天】
13 0
什么是Java泛型类型?