如何寫一個方法交換兩個Integer型別的值?
剛看到這個問題的時候,可能會覺得很簡單,但是真正實現出來就會遇到一些奇怪的現象。下面我先我第一感覺的解決方法。
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了,這就是快取引起的問題。