之前朋友面试遇到了一个挺有趣的问题,题目如下:
public static void main(String[] args) throws Exception{ int a = 1; int b = 2; swap(a, b); System.out.println("a is "+a+", b is "+b); }
编写swap方法使a、b两个值交换数据,即a=2,b=1
这在个人知识范围内,觉得不可能实现,找了一段写的比较好的解释,如下:
java的基本数据类型共有8种,即int,short,long,byte,float,double,boolean,char(注意,并没有String的基本类型 )。
这种类型的定义是通过诸如int a = 1;long b = 255L;的形式来定义的。
如int a = 1;这里的a是一个指向int类型的引用,指向1这个字面值。这些字面值的数据,
由于大小可知,生存期可知(这些字面值定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。
另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。比如:
我们同时定义:
int a=1;
int b=2;
编译器先处理int a = 1;
首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为1的地址,没找到,就开辟一个存放1这个字面值的地址,然后将a指向3的地址。
接着处理int b = 2;在创建完b这个引用变量后,由于在栈中已经有2这个字面值,便将b直接指向2的地址。这样,就出现了a与b同时均指向2的情况。
定义完a与b的值后,再令a = 4;
那么,b不会等于4,还是等于2。在编译器内部,遇到时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;
如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。
那怎么解决这个呢?
贴代码:
public static void main(String[] args) throws Exception { Integer a = 1333; Integer b = 2333; swap(a, b); System.out.println("a is "+a+", b is "+b); } private static void swap(Integer x, Integer y) throws Exception { int z= x; Field field = Integer.class.getDeclaredField("value"); field.setAccessible(true); field.set(x, y); field.set(y, z); System.out.println("x is "+x+", y is "+y); }
将基本类型改为对象类型,这样是不是就好了呢?看上去应该没什么问题,输出结果也是正确的,可试试a=1,b=2呢!
结果成了2 2,是不是觉得很怪异,JD反编译后如下:
private static void swap(Integer x, Integer y) throws Exception { int z= x; Field field = Integer.class.getDeclaredField("value"); field.setAccessible(true); field.set(x, y); field.set(y, Integer.valueOf(z)); System.out.println("x is "+x+", y is "+y); }
查看Integer源码valueOf(i)方法,就会发现原因了!
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
Integer是有自己的缓冲池[-128-128],field.set(x, y);这行代码会把缓冲池里面的值给修改了,将IntegerCache.cache[129]=IntegerCache.cache[130]=2
所以导致a=b=2,怎么破呢?
无非就是将获取的z值不走IntegerCache,那就直接new吧!修改代码
field.set(y, z);改为field.set(y, new String(z));问题解决,可还是最好不要使用公用的IntegerCache,继续修改代码:
public static void main(String[] args) throws Exception { Integer a = new Integer(1); Integer b = new Integer(2); swap(a, b); System.out.println("a is "+a+", b is "+b); }
成功了。
总结一下:
一)int数据存储在常量池,地址在栈。
二)Integer 定义的两个变量(如:integer a = 1; integer b = 2),它们公用一个IntegerCache。