这道面试题所设计的知识点:
- 传值和传引用的区别
- 装箱和拆箱
- java的内存模型
- 反射
面试题需求:
主方法定义两个Integer变量,并赋值,然后通过一个swap()方法交换变量的值,请写出swap()中的实现
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
System.out.println("before swap :a = " + a + ",b = " + b);
swap(a, b);
System.out.println("after swap :a = " + a + ",b = " + b);
}
当看到这个面试题的时候第一感觉就是在swap()方法中定义一个变量,例如:
private static void swap(Integer num1, Integer num2) {
Integer tmp = num1;
num1 = num2;
num2 = tmp;
}
但是运行结果不尽人意:
before swap :a = 1,b = 2
after swap :a = 1,b = 2
这就牵扯到传值的方式,传值的方式有两种,按值传递
,引用传递
,那上面这个swap()方法是按那种方式传值的呢?
这里是按值传递;
值传递:
在方法被调用时,实参通过形参把它的内容副本传入方法内部,此时形参接收到的内容是实参值的一个拷贝,因此在方法内对形参的任何操作,都仅仅是对这个副本的操作,不影响原始值的内容。
到了swap()方法,还没有进行交换时:
经过了swap()方法之后,num1和num2进行了交换:
可以看到其实并没有影响到a和b的值;
这里还有一个问题就是Integer a = 1; 是自动装箱,怎么实现自动装箱的? 大家都用过Integer.valueOf()
Integer a = Integer.valueOf(1);
所以说Integer a = 1就是一个装箱的操作;
那valueOf()这个方法中又做了什么事呢?
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
有两个变量 low ,high ,low=-128,high=127;也就是说在这两个值之间的值都会从缓存里去取;不在这两个值之间的才会去new 一个Integer对象;
上面我们使用一个中间变量是不能实现的交换的,那我们使用发射试试;
private static void swap(Integer num1, Integer num2) {
try {
int tmp = num1.intValue();
Field field = Integer.class.getDeclaredField("value");
field.setAccessible(true); // private final int value; 私有属性,使用暴力反射
field.set(num1, num2);
field.set(num2, tmp);
} catch (Exception e) {
e.printStackTrace();
}
}
运行结果:
before swap :a = 1,b = 2
after swap :a = 2,b = 2
发现还是不行;只是a成功交换,但是b没有交换;这里因为num1的下标对应的值被改成了2,所以当给num2设置值的时候,其实设置的还是2,这里可以打个断点到valueOf()
这个方法中看看;这里就不阐述过多;
为了引出正确的解决方法我们引入一个简单的知识点:上面我们也看了在[-128~127]之间是从缓存中取的;
那下面这两段代码是打印true还是false呢?
public static void main(String[] args) {
Integer a = 1;
Integer b = 1;
System.out.println(a==b);
}
public static void main(String[] args) {
Integer a = 128;
Integer b = 128;
System.out.println(a==b);
}
1之间的比较是true ,128之间的比较是false,1没有超出这个缓存的范围,但是128已经超出了缓存的范围,就要就new Integer();
所以这个面试题正确的解决方法是:
private static void swap(Integer num1, Integer num2) {
try {
int tmp = num1.intValue();
Field field = Integer.class.getDeclaredField("value");
field.setAccessible(true); // private final int value; 私有属性,使用暴力反射
field.set(num1, num2);
field.set(num2,new Integer(tmp));
} catch (Exception e) {
e.printStackTrace();
}
}
打印结果:
before swap :a = 1,b = 2
after swap :a = 2,b = 1
成功的进行了交换;对num1–>Integer tmp = new Integer(num1);
不要让它取缓存里的值;缓存中下标对应的值改变也对num2的赋值没有影响;因为num1是重新new 的对象;
知道了上面的原理,我们把a和b的值分别变成777 和999 那会不会成功交换呢?自己可以下去试一试;
还有没有其他的解决方式呢?当然是有的;我们可以把
field.set(num1, num2);
field.set(num2, tmp);
//改成
field.setInt(num1,num2);
field.setInt(num2,tmp);
field.setInt(num1,num2);
中num2是需要int类型;这就不是装箱了而是拆箱
至此 这道面试题所涉及的东西就说完了;