破坏java内部Integer

看一道关于Integer的面试题,无意中发掘的一种方式。

题目是要求写一个方法,交换两个Integer的值。
看起来十分简单的一道题目,蕴含很多知识哦,不信往下看。

public static void swap(Integer i1, Integer i2){
        int tmp = i1;
        i1 = i2;
        i2 = tmp;
    }

这肯定是错误答案, 因为Integer是对象,这个方法执行过后,原始的两个对象内容未发生改变。

然后,有人给出的答案如下:

public static void swap(Integer i1, Integer i2){
        try {
            Field field = Integer.class.getDeclaredField("value");
            field.setAccessible(true);

            int tmp = i1.intValue();
            field.setInt(i1, i2.intValue());
            field.setInt(i2, tmp);

        }catch (Exception e){
            e.printStackTrace();
        }
    }

通过反射完成的交换,要是看不懂的,也可以运行一下看看

public static void main(String[] args) {
	Integer i1 = 1, i2 = 2;
        swap(i1, i2);
        System.out.println("i1: " + i1);
        System.out.println("i2: " + i2 );
}

运行结果如下:

在这里插入图片描述
似乎是正确的,很多人看到这里,没发现问题。

什么问题呢?这段自以为高明的代码,写了一堆反射,看起来很厉害,其实问题很大,而且很厉害的破坏了java内部的Integer。不行你在main方法下面加一点代码看看。

比如:

		
public static void main(String[] args) {
	Integer i1 = 1, i2 = 2;
        swap(i1, i2);
        System.out.println("i1: " + i1);
        System.out.println("i2: " + i2 );

		Integer a = 1;
        if(a.intValue() == 2){
            System.out.println("xx");
        }
}

运行结果如下:

在这里插入图片描述

什么?1==2?

问题出在哪里呢?这里定义Integer a =1, 怎么就等于2了呢?

注意:不明白反射的请自行百度。

问题在这里:
field.setInt(i1, i2.intValue());

看下Field的源码:
在这里插入图片描述
如果还没发现问题的话,请把下面代码运行一下。

public static void destroyInteger(){
        try {
            Field field = Integer.class.getDeclaredField("value");
            field.setAccessible(true);

            Integer i1 = 20;
            Integer i2 = 30;

            int tmp = i1.intValue();
            field.set(i1, i2.intValue());
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        destroyInteger();

        Integer a = 20;
        System.out.println(a.intValue());
    }

如下是我的解释:

1. Integer是int的包装对象
2. 为了提高性能,java的设计者在设计Integer和Long的时候,用到了享元模式,启动的时候,会缓存-128~127到常量池中,这些都是对象哦,不是简单的数字。
3. 在初始化局部变量Integer a=20的时候,发生的事情是,从常量池中取Integer 20这个对象的地址,然后把地址给a对象。
4. 那么为什么a的值是30呢?原因是Field的方法setInt传入的是Object,是一个对象,多以发生的事情是这样的。
Integer 20这个对象,被赋值为30。所以破坏了Integer。其他地方,用到Integer 20的时候,值都是30了。

所以,反射不要乱用哦,可能不小心,就写出bug来了。而且这种bug如果出现在大一点的系统里面很难排查。当然专业人员还是很快可以查出来的,哈哈。

总结一下这里的知识点:

  1. -128~127的整数,在启动的时候,就在jvm常量池中初始化了,程序中用到这些值的包装对象,都是直接从常量池中取。
  2. 反射的时候,Field的setXXX方法,传入的是对象,所以用的时候要注意,如果不是临时对象(尤其是jvm里面一直缓存的对象),不可以随便这样set。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值