• Java实现2+2=5


    今天在网上看到一篇文章,如何编程实现 2 + 2 = 5? - 编程 - 软件编程 - 深度开源

    以前没看过类似的题目,引起了我的好奇心,于是仔细看了看文章,知道原来是利用了Java包装类型的实例池(也就是缓存数组)特性才实现 2+2=5。

    一、缓存数组

    1.1、在Java中,每个基本类型(byte、char、 short、int、long、float、double)都有对应的包装类型。

    例如 int 对应的包装类型为 Integer。

    查看 Integer 源码可知 Java 在Integer内部定义了一个静态内部类 IntegerCache,里面声明了一个 Integer类型的 cache缓存数组

    JVM启动程序时,加载 Integer 类到内存后,先通过静态代码块,从位置0开始按照顺序创建 Integer 对象 (value范围 [-128,127]) 赋值给cache缓存数组

    1. private static class IntegerCache {
    2. static final int low = -128;
    3. static final int high;
    4. static final Integer cache[];
    5. static {
    6. ...//省略其他代码
    7. cache = new Integer[(high - low) + 1];
    8. int j = low;
    9. for(int k = 0; k < cache.length; k++)
    10. cache[k] = new Integer(j++);
    11. ...
    12. }
    13. private IntegerCache() {}
    14. }

    注意点

    • byte、short、int、long 的包装类型都有范围 [-128,127] 的缓存数组。
    • char 的包装类型的缓存数组值范围是 [0,127]
    • float 和double 的包装类型没有缓存数组。

    1.2、在需要创建Integer对象时,先判断赋值给Integer的 int 值是不是在cache缓存数组的范围 [-128,127] 内。如果是,则不创建Integer对象,而是根据 int 计算找到对应cache缓存数组的Integer对象的地址,返回对象地址。如果不在范围内,则创建一个新的Integer对象并返回对象地址,例如 Integer的valueOf(int i) 方法。

    1. public static Integer valueOf(int i) {
    2. if (i >= IntegerCache.low && i <= IntegerCache.high)
    3. //先判断 i 是否在缓存数组的范围内
    4. return IntegerCache.cache[i + (-IntegerCache.low)];
    5. return new Integer(i);
    6. }

    二、面试题

    2.1、判断值相同的2个 Integer 对象是否相等

    如图所示

    Debug模式下,从b1、i1、i2、i3、i4的引用地址可看出,

    i1和i2指向同一个Integer对象,因为i1和i2的值都为0,在Integer的cache缓存数组 [-128,127] 范围内,所以在自动装箱成Integer对象时,不创建对象,而是返回缓存数组中存储的对应的对象地址,所以 i1 == i2 为 true

    而i3和i4的值为129,不在 cache缓存数组 [-128,127] 范围内,所以会创建新的Integer对象并返回对象地址,所以i3和i4分别指向不同对象,引用地址不一样,所以 i3 == i4 为false。 

    b1虽然值也为0,但是它对应的是 Byte 包装类型的cache缓存数组的对象,与i1和i2对应的对象是2个不同类型的对象。

    注意点:编译器不允许不同包装类型的对象使用 == 符号。

    2.2、编程实现2+2=5

    1. @Test
    2. public void test21() throws NoSuchFieldException, IllegalAccessException {
    3. //通过反射获取Integer的内部类
    4. Class[] declaredClasses = Integer.class.getDeclaredClasses();
    5. //因为Integer只有一个IntegerCache静态内部类,所以可直接通过索引=0得到IntegerCache 类
    6. Class cacheClass = declaredClasses[0];
    7. //得到IntegerCache的cache缓存数组
    8. Field cache = cacheClass.getDeclaredField("cache");
    9. cache.setAccessible(true);
    10. Integer[] o = (Integer[]) cache.get(null);
    11. for (Integer integer : o) {
    12. //从 -128 到 127
    13. //System.out.println(integer);
    14. }
    15. //o[132]原本存储的是值为4,o[133]原本存储的是值为5
    16. o[132] = o[133];
    17. Integer i = 2+2;
    18. System.out.println(i);
    19. }

    之所以 2+2 赋值给Integer对象i后, 输出为 5,是因为在Integer中有一个范围为 [-128,127] 的cache缓存数组,当需要创建Integer对象时,判断赋值给Integer对象的值是否在 [-128,127] 范围内,如果在,则不会创建新的Integer对象,而是返回cache缓存数组的对应对象的地址,而 Integer i = 2+2,2+2 计算得到 4 后,自动装箱创建Integer对象时,由于 4 在 [-128,127] 的缓存范围内,所以不会创建新的Integer对象,而是返回缓存数组 o[132](索引位置计算:4+(-)(-128)) 存储的对象地址,所以 i 和 o[132] 指向同一个对象。cache缓存数组是从0开始存储 [-128,127] 的值,o[132] 原本指向值为 4 的Integer对象,后面通过反射将 o[133] 的地址赋值给 o[132],而 o[133] 指向的是值为 5 的Integer对象,o[132] 实际指向值为 5 的Integer对象,o[132] 赋值给 i,i 也指向了值为 5 的Integer对象,打印 i 时,会将指向的对象的value值对应出来,也就是打印 5,所以得到 2+2=5。

    注意点:

    1、实现 2+2=5 需要通过包装类型对象才能实现,因为需要借助包装类型的缓存数组,普通的 2+2 还是为4。

    2、如果是将 o[132] = o[133] 和 Integer i = 2+2 代码调换位置,而输出 i 还是会得到正确的结果 4,因为调换位置后,o[132]此时指向的是值为 4 的正确Integer对象,赋值给 i,所以 i 指向的值为4 的Integer对象,后面 o[133] 再赋值给 o[132],i 指向的地址也不会变。

     

  • 相关阅读:
    swagger使用教程——快速使用swagger
    自动化测试有必要学吗?
    java: 程序包com.alibaba.excel.annotation不存在
    转铁蛋白Tf功能化β-榄香烯-雷公藤红素/紫杉醇PLGA纳米粒/雷公藤甲素脂质体(化学试剂)
    Windows安全日志分析
    基于PHP+MySQL医药信息查询系统的设计与开发
    最全分布式面试题整理
    安装项目运行环境(python依赖包+allure)
    书桌台灯怎么选?分享儿童卧室灯品牌
    【精选】HTML5最全知识点集合
  • 原文地址:https://blog.csdn.net/weixin_37607613/article/details/127093705