基本數據類型的包裝類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); }
這個示例中涉及到技能點: