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缓存数组

private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {            
            ...//省略其他代码

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

            ...
        }

        private IntegerCache() {}
    }

注意点

  • 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) 方法。

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

二、面试题

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

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

之所以 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 指向的地址也不会变。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值