jdk之java.lang.Integer源碼理解

基本數據類型的包裝類java.lang.Integer是咱們頻繁使用的一個系統類,那麼經過一個示例反應出的幾個問題來深刻理解一下此類的源碼。java

需求:實現Integer類型的兩個數值交換。數組

 1 package cn.integer;
 2 
 3 public class Demo {
 4     public static void main(String[] args) {
 5         Integer a = 1;
 6         Integer b = 2;
 7         System.out.println("bofore swap a:"+a+",b:"+b);
 8         //交換a和b的值
 9         swap(a,b);
10         System.out.println("after swap a:"+a+",b:"+b);
11     }
12     /**
13      * 數據交換
14      * @param a
15      * @param b
16      */
17     private static void swap(Integer a, Integer b) {
18         /**
19          * 如何實現呢?嘗試幾種方法
20          */
21         /*01.方式一(沒法實現)
22         a = a^b;
23         b = a^b;
24         a = a^b;*/
25         
26         /*02.方式二(沒法實現)
27         int tmp = a;
28         a = b;
29         b = tmp;*/
30         
31         /**
32          * 以上兩種方式是由於java的值傳遞特性故沒法實現數據交換。
33          */
34     }
35 }

Java值傳遞的示意圖以下:緩存

當調用swap(..)方法時,在堆中會建立這兩個值得副本,形參num1和num2指向副本的數據。原ab指向的數據不會改變。ide

 

那麼如何經過修改swap()方法實現呢?ui

觀察java.lang.Integer源碼:this

public final class Integer extends Number implements Comparable<Integer> {
          ...  
}

能夠發現 Integer和String同樣,是final修飾的,這是由於jdk將這些系統類封裝起來不但願被破壞。spa

 

繼續看Integer類:code

 /**
     * The value of the {@code Integer}.
     *
     * @serial
     */
    private final int value;

    /**
     * Constructs a newly allocated {@code Integer} object that
     * represents the specified {@code int} value.
     *
     * @param   value   the value to be represented by the
     *                  {@code Integer} object.
     */
    public Integer(int value) {
        this.value = value;
    }

這裏的value也是使用final修飾的,那麼若是想修改它的值,可使用反射的方式獲取value字段並進行修改。orm

修改swap()方法中代碼:對象

 1 public class Demo {
 2     public static void main(String[] args) {
 3         Integer a = 1;
 4         Integer b = 2;
 5         System.out.println("bofore swap a:"+a+",b:"+b);
 6         //交換a和b的值
 7         swap(a,b);
 8         System.out.println("after swap a:"+a+",b:"+b);
 9     }
10     /**
11      * 數據交換
12      * @param a
13      * @param b
14      */
15     private static void swap(Integer a, Integer b) {
16         try {
17             //獲取Integer類中私有字段value
18             Field field = Integer.class.getDeclaredField("value");
19             /**
20              * 開啓訪問權限
21              * public final class Field extends AccessibleObject implements Member {}
22              * 跟進AccessibleObject
23              * public void setAccessible(boolean flag) throws SecurityException {
24                     SecurityManager sm = System.getSecurityManager();
25                     if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
26                     setAccessible0(this, flag);
27                 }
28                 
29                 private static void setAccessible0(AccessibleObject obj, boolean flag)
30                     throws SecurityException
31                     {
32                         if (obj instanceof Constructor && flag == true) {
33                         Constructor<?> c = (Constructor<?>)obj;
34                         if (c.getDeclaringClass() == Class.class) {
35                             throw new SecurityException("Cannot make a java.lang.Class" +
36                                                 " constructor accessible");
37                         }
38                     }
39                     obj.override = flag;
40                 }
41              */
42             field.setAccessible(true);
43             //臨時變量
44             int tmp = a.intValue();
45             //數據交換
46             field.set(a, b);
47             field.set(b, tmp);
48         } catch (Exception e) {
49             e.printStackTrace();
50         }
51     }
52 }

 

運行結果:

誒,我去。怎麼只改變了一個值?咋成功了一半呢?

 

01.首先,Integer a = 1; 這裏會自動裝箱,將 int類型的1 轉換成 Integer類型。

經過valueOf方法進行裝箱

1 public static Integer valueOf(int i) {
2         if (i >= IntegerCache.low && i <= IntegerCache.high)
3             return IntegerCache.cache[i + (-IntegerCache.low)];
4         return new Integer(i);
5     }

這個方法很是重要,若是傳入的參數在 -128-127之間,會返回一個緩存中的數據。不然就 new出一個Integer對象!

Integer.IntegerCache是Integer中的內部類:

 1 private static class IntegerCache {
 2         static final int low = -128;
 3         static final int high;
 4         static final Integer cache[];
 5 
 6         static {
 7             // high value may be configured by property
 8             int h = 127;
 9             String integerCacheHighPropValue =
10                 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
11             if (integerCacheHighPropValue != null) {
12                 try {
13                     int i = parseInt(integerCacheHighPropValue);
14                     i = Math.max(i, 127);
15                     // Maximum array size is Integer.MAX_VALUE
16                     h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
17                 } catch( NumberFormatException nfe) {
18                     // If the property cannot be parsed into an int, ignore it.
19                 }
20             }
21             high = h;
22 
23             cache = new Integer[(high - low) + 1];
24             int j = low;
25             for(int k = 0; k < cache.length; k++)
26                 cache[k] = new Integer(j++);
27 
28             // range [-128, 127] must be interned (JLS7 5.1.7)
29             assert IntegerCache.high >= 127;
30         }
31 
32         private IntegerCache() {}
33     }

 

02.當咱們獲取數值1時

return IntegerCache.cache[i + (-IntegerCache.low)] 
計算出IntegerCache.cache[129]的值(也就是1)返回。

cache這個數組存放着 -128-127這些數據。故index=129就是返回1。

03. 執行這行時 field.set(a, b);
將傳入的2複製給a,此時a爲2.
注意:這裏經過set方法改變的的是緩存cache數組中的數據!

04. 當執行到 field.set(b, tmp);
tmp爲1,而set方法的參數類型是Object,能夠傳入int類型的tmp
public void set(Object obj, Object value)
        throws IllegalArgumentException, IllegalAccessException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        getFieldAccessor(obj).set(obj, value);
    }
 

因此這個tmp=1是從Integer的緩存數組中取得,因上一行操做已經將cache中index爲129位置得原數值1改變爲了2,故在cache中獲取的是2! 那麼把2賦值給了b天然是2!(b對應的下標[130],a對應的下標[129])

 

咱們發現,問題的關鍵在於這個緩存cache!

若是說能夠避開走緩存這一步,咱們就能實現數據交換。除了傳入 -128-127以外的數據,咱們還能夠:

001.將tmp轉換成Integer對象後在傳入Field的set方法:

private static void swap(Integer a, Integer b) {
        try {
            //獲取Integer類中私有字段value
            Field field = Integer.class.getDeclaredField("value");
            field.setAccessible(true);
            //臨時變量
            int tmp = a.intValue();
            //數據交換
            field.set(a, b);
            field.set(b, new Integer(tmp));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

運行結果:

 

 002. 使用 Field類的 setInt方法
public void setInt(Object obj, int i)
        throws IllegalArgumentException, IllegalAccessException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        getFieldAccessor(obj).setInt(obj, i);
    }
 

修改代碼 field.setInt(b,tmp); 也能夠實現。

003. 非要實現打印效果的話,使用很是規手短,直接在swap中打印結果...

private static void swap(Integer a, Integer b) {
        System.out.println("after swap a:2,b:1");
        System.exit(0);
    }

 

這個示例中涉及到技能點:

相關文章
相關標籤/搜索