1. 程式人生 > >如何寫一個方法交換兩個Integer型別的值?

如何寫一個方法交換兩個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了,這就是快取引起的問題。