刚看到这个问题的时候,可能会觉得很简单,但是真正实现出来就会遇到一些奇怪的现象。下面我先我第一感觉的解决方法。
1.错误的版本(也是第一感觉)
/**
* @author
*/
public class ObjectShallowSize {
/**
第一步
* 交换两个integer类型的变量
*/
public static void swap(Integer value1,Integer value2){
//第二步定义临时变量tempt,将value1赋值给tempt
Integer tempt = value1;
//第三步 将value2赋值value1变量
value1 = value2;
//第四步 将tempt赋值给value2
value2 = tempt;
}
public static void main(String[] args){
Integer firstValue = 200;
Integer anotherValue = 300;
System.out.println("交换前:firstValue="+firstValue + " anotherValue=" +anotherValue);
**swap(firstValue,anotherValue);**
System.out.println("交换后:firstValue="+firstValue + " anotherValue=" +anotherValue);
}
}
上面的方法并不能交换firstValue 和anotherValue 的值,我们来分析下原因。
在执行swap方法之前的firstValue 和anotherValue在堆中如下图:
执行到方法swap时,经历了三步,我们来分析下这三步分别有什么变化
第一步:
调用swap方法,会创建两个局部变量value1和value2,其分别指向堆中200和300的内存区域,这个过程并不会改变firstValue 和anotherValue的引用。
第二步:
Integer tempt = value1;定义tempt临时变量,tempt的应用和value1相同指向同一内存区域
第三步:
value1 = value2;改变了value1的应用指向300的内存。
第四步:
value2 = tempt;改变value2的应用指向tempt的应用的内存
经历过以上四个步骤,方法swap执行结束,但是我们可以看倒firstValue和anotherValue的应用始终没有改变,所以我们打印的结果中前后并没有变化,也就没有交换两个变量的值。
交换前:firstValue=200 anotherValue=300
交换后:firstValue=200 anotherValue=300
分析:以前,我们遇到过交换两个int类型的变量值,但是其实基本数据类型,这里是对象类型,并不能通过改变应用交换两个值。通过查看Integer的源码,我们可以知道内部也是使用int类型存储的,因此我们可以修改这个int的value。但是value属性是private类型的,因此我们需要通过反射去修改其值。
正确的版本:
public class ObjectShallowSize {
public static void swap(Integer value1, Integer value2) throws Exception {
//反射获取value属性对象
Field declaredField = Integer.class.getDeclaredField("value");
declaredField.setAccessible(true);
//这一步很重要,决定是不是从缓存中取,一定不能Integer val = value1.intValue();这么写
Integer val = new Integer(value1.intValue());
declaredField.set(value1,value2);
declaredField.set(value2,val);
}
public static void main(String[] args) throws Exception {
Integer firstValue = 200;
Integer anotherValue = 300;
System.out.println("交换前:firstValue="+firstValue + " anotherValue=" +anotherValue);
swap(firstValue,anotherValue);
System.out.println("交换后:firstValue="+firstValue + " anotherValue=" +anotherValue);
}
}
第一步:
Integer val = new Integer(value1.intValue());
创建val变量,并且在堆中重新分配一块内存,其值是200
第二步:
declaredField.set(value1,value2);
修改value1引用指向内存的值为300
第三步:
declaredField.set(value2,val);
修改value2应用指向的内存值为200.
打印结果:
交换前:firstValue=200 anotherValue=300
交换后:firstValue=300 anotherValue=200
上面的 Integer val = new Integer(value1.intValue());这一步非常重要,如果直接value1.intValue(),那么对于-128~127内的整数是从缓存中取的,导致出现很奇怪的现象,如下图分析:
为了分析出结果,我们修改firstValue 和anotherValue的值分别为1和2
第一步:
Integer val = value1.intValue();该方法首先会判断int值是否在-128~127之间,是则直接从缓存中取,否则才会在堆中分配一块内存。
第二步:
declaredField.set(value1,value2);
第三步:
declaredField.set(value2,val);
从上面可以看出,value1和value2的值都是2了,这就是缓存引起的问题。