Integer實現原理及緩存機制

Java涉及知識點

  1. 裝箱和拆箱
  2. 反射及設置對象訪問權限原理
  3. 函數參數的值傳遞和引用傳遞
  4. Integer原理及緩存機制
  5. 實現一個函數來交換參數的兩個值
1. 裝包和拆包

java提供的基本類型以及其對於的包裝類型

基本類型 對於包裝類型
byte Byte
char Character
short Short
int Integer
long Long
float Float
double Double
void Void

以Integer爲例子介紹裝箱和拆箱:”

裝箱存在的情況:
int 賦值給Integer 即基本類型賦值給包裝類型時

Integer i = 1; 等價於 Integer i = Integer.valueOf(1); java自動完成裝箱操作

拆箱存在的情況:
int與Integer比較 及基本類型和包裝類型比較時

Integer i = new Integer(1);

int j = 1;

i == j  //等價於 i.intValue() == j  i對象會自動拆箱int類型和j進行比較
2. 反射及設置對象訪問權限原理

JAVA反射機制是運行過程中,任意實體都可可以知道這個實體類的對象的所有方法和屬性信息
對於任何對象都可以調用它的任意方法和屬性,這種動態獲取信息以及動態調用對象方法的功能稱爲java語言的反射機制。

這裏我們反射的目標是修改某個類的private屬性值。以Integer爲例:

//獲取Integer類對象的方式
//Class clazz = Class.forName("java.lang.Integer");

Class clazz = Integer.class;

//獲取Integer類中私有屬性value的域 getDeclaredField獲取類本身對應的所有訪問
//權限的屬性  getField獲取類以及父類所有的public屬性
Field field = clazz.getDeclaredField("value"); 
field.setAccessible(true); //設置private域訪問權限
Integer a = 1;
field.set(a, 2);//將a 對象中的value屬性設置爲2 

對象訪問權限原理

反着訪問對象的原理主要看Field類的setAccessible和set即可。一個設置域的可見性,一個設置屬性值。

File類聲明:public final
class Field extends AccessibleObject implements Member

//setAccessible源碼 父類AccessibleObject的方法
 public void setAccessible(boolean flag) throws SecurityException {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
        setAccessible0(this, flag);//前面是一安全管理器判斷,這裏纔是真正設置的地方
    }
 private static void setAccessible0(AccessibleObject obj, boolean flag)
        throws SecurityException
    {
        if (obj instanceof Constructor && flag == true) {
            Constructor<?> c = (Constructor<?>)obj;
            if (c.getDeclaringClass() == Class.class) {
                throw new SecurityException("Cannot make a java.lang.Class" +
                                            " constructor accessible");
            }
        }
        obj.override = flag; //field父類中的override屬性設置爲flag,也就是我們設置的true
    }
//set Feild中的set方法
public void set(Object obj, Object value)
        throws IllegalArgumentException, IllegalAccessException
    {
        if (!override) {//同樣是判斷field父類中的override,爲true則不檢查對象訪問權限 直接設值
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        getFieldAccessor(obj).set(obj, value);
    }
3. 函數參數的值傳遞和引用傳遞

java中的函數參數傳遞分爲值傳遞引用傳遞實際上都可以認爲是值傳遞(引用傳遞實質傳遞的是引用的值(可以看作C++中指針,存放地址的變量),也同樣需要拷貝副本,不過不是對象的副本,而且存放對象地址的變量的副本)。

值傳遞:針對於基本數據類型

引用傳遞:針對於對象

int i = 1
void add1 (int i){//值傳遞 在i參數傳入的時候 實際上會拷貝i的一副本,然後在函數中進行操作
    i++;//實際操作的是i的副本,所以函數外部的i變量不受影響
}
System.out.println(i);//輸出爲1

Integer j = 1;// j = Integer.valueOf(1)
void add2 (Integer j){//引用傳遞 在j參數傳入的時候 實際上會拷貝j對象的引用 然後傳入函數中進行操作
    j++; //裝箱和拆箱的等價? j=j+1   j = Integer.valueOf(j.intValue()+1)  實際上是副本j引用指向了新的Integer對象,外面的j引用還是指向原來的對象 
}
System.out.println(j);//輸出同樣爲1
4. Integer原理及緩存機制

Integer是int基本數據類型的包裝類,無非是在int基本類型的基礎上增加了一些操作和其他屬性。

Integer的實際對應int值是通過intValue()方法獲取的,源碼如下:

 private final int value;//對應int基本類型的數值 是一個常量整型
 public int intValue() {
        return value;
 }

前面說過的裝箱用到的一個方法是valueOf(),讓我們看看源碼:

//可以看到傳入的i 先和IntegerCache比較 在IntegerCache中則返回IntegerCache中的Integer不存在則new一個Integer對象
 public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

IntegerCache實現如下:

//IntegerCache是個Integer的內部類,在類加載的時候創建了256個緩存Integer對象,範圍-128至127
    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

知道Integer緩存的存在,下面我們看看下面的幾個例子:

注:== 對象比較比較的是對象的引用是否相等 equals則根據對象內部的實現情況進行比較

Integer i = 1; //i = Integer.valueOf(1) 取緩存對象 IntegerCache.cache[129]
Integer j = 1; //j = Integer.valueOf(1) 取緩存對象 IntegerCache.cache[129]
System.out.println(i == j);//輸出true  i 和j指向同一個對象 
Integer i = 1; //i = Integer.valueOf(1) 取緩存對象 IntegerCache.cache[129]
Integer j = new Integer(1); // 新創建一個對象
System.out.println(i == j);//輸出false i 和j指向的不是同一個對象 
Integer i = 128; //i = Integer.valueOf(128) 不在緩存訪問內 new Integer(128)
Integer j = 128; //j = Integer.valueOf(128) 不在緩存訪問內 new Integer(128)
System.out.println(i == j); //輸出false i 和j指向的不是同一個對象 

所以Integer對象在比較是否相等的時候 不要用 == 用equals Integer內部實現了自己用equals,源碼如下:

public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }
5. 實現一個函數來交換參數的兩個值

前面已經補充了一些Java的相關知識點了,現在我們就來實現一個函數來交換參數的兩個值

public class Main {

    static void swap1(Integer a, Integer b){ //第3節已經說明了 這裏函數中只是操作的引用副本,是不影響函數外a,b的變化的
        Integer temp = a;
        a = b;
        b = temp;
    }//輸出a=1 b=2

    static void swap2(Integer a, Integer b) throws NoSuchFieldException, IllegalAccessException {//通過反射機制來修改引用指向對象的value值即改改變函數外a,b中的value數屬性
        Field field = Integer.class.getDeclaredField("value");//value是private final
        field.setAccessible(true);//繞過安全檢查
         //temp = 1 temp ->Integer.valueOf(a.intValue()) -> Integer.valueOf(1) ->IntegerCache.cache[129]
        Integer temp = a.intValue();
        //public void set(Object obj, Object value)
        //a.value->Integer.valueOf(b.intValue()).intValue(); 修改a指向對象中value值爲2 
        //a指向的是緩存中的IntegerCache.cache[129] 所以IntegerCache.cache[129]
        //中的value被修改成2
        field.set(a, b.intValue()); 

        //b -> IntegerCache.cache[130]
        //b.value->IntegerCache.cache[129].value  所以 IntegerCache.cache[130] IntegerCache.cache[129] value都是2 所以a b中value 都是2
        field.set(b, temp);
    }//輸出a=2 b=2
    
    static void swap3(Integer a, Integer b) throws NoSuchFieldException, IllegalAccessException {
        Field field = Integer.class.getDeclaredField("value");
        field.setAccessible(true);//繞過安全檢查
        Integer temp = new Integer(a.intValue());//和swap2唯一區區別就在這一行 
        field.set(a, b.intValue()); //a->Integer.valueOf(b.intValue()).intValue();
        field.set(b,temp);//b.value->new Integer(a.intValue()).intValue() 而不是修改成IntegerCache.cache[129].intValue();
    }//輸出a=2 b=1 但是存在一個問題就是同樣修改了IntegerCache中緩存值 後續存在隱患
    
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Integer a = 1, b = 2;  //裝箱操作 等價於 Integer a = Integer.valueOf(1); Integer b = Integer.valueOf(2);
        swap1(a, b);
        System.out.println("a="+a+" b="+b);//輸出a=1 b=2
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章