发现问题:
最近在学习java的时候在java群里面看到一个问题,就是用反射来实现两个值的交换,然后结果没有按群友的预期去执行.群里的代码如下:
群友想的结果是a=1,b=2;a=2,b=1;
然而结果是
然后就觉得很奇怪,交换完以后,a,b都变成了2.
问题探究过程
(和群友一起讨论出来的,一开始自己也觉得很奇怪,涉及到java的自动装箱和Integer类的源码设计问题):
1.首先将源代码反编译,推荐使用jad下载地址,eclipse和idea上也有相应的插件.反编译出来如下:
可以看到Integer a = 1;这种类似的地方进行了自动装箱;int tmp = num1,进行了自动拆箱.分别使用的是valueOf(),intValue();
2.然后我们去查看Integer的源码发现,如下图:
(1).Integer类里面有个叫IntegerCache的Integer类型的缓存数组;
(2).cache里面的值从图中可以看出是从-128到127,一共256个整型数值;
(3).我们使用的Integer的值来源于final类型的value,正常情况是不可变的,但是用了反射,设置了field.setAccessible(true);就可以改变value的值,群友也正是想用反射来改变value的值,从而达到交换数值的目的.
3.在自动装箱valueOf()打了断点,在进行第二次的交换field.set(num2, tmp)的时候如图:
发现cache的129位置原来应该是1的值,变成了2.所以内存中的Integer的cache[129]的值都变成了2,因此结果b=2,tmp=2.
结果分析:
1.field.set(num1, num2)的时候,反射将原本value=1的IntegerCache中的cache[129]的值修改为了2.所以在field.set(num2, tmp)的时候,是将num2(cache[129]等于2)的值赋值给了tmp(也变为2).
2.因为缓存数组的大小是在-128-127之间,所以我们将a,b的值都大于127的时候用反射来交换数值的大小是不会出问题的,因为不在缓存数组中的数值,会重新生成.如图:
结果:
这样交换是没有问题的.
自我总结:
一.是反射不要乱用,可能会有你想不到的后果;
二是涉及到自动装箱的可能会有坑,最好使用Integer i = new Integer(‘数字’);这样就不会有Cache数组的坑,因为是重新新建了一个Integer的对象.