天天看點

Java源碼系列-Integer源碼

1. 類圖

Java源碼系列-Integer源碼

Integer-Class

2. 存儲位置

  • -128-127的值存儲在JVM方法區的靜态區,是因為Integer内部有一個靜态内部類IntegerCache
  • 其餘的存儲在JVM堆區,都是通過new Integer(x)建立出來
public class IntegerDemoTest {

    @Test
    public void newIntegerTest() {
        Integer a = 3;
        Integer b = 3;
        Integer a = 3; <==> Integer.valueOf(3);
        // a == b 為true,是因為對應同一個IntegerCache遊标的位址
        System.out.println(a == b);

        Integer c = 129;
        Integer d = 129;

        // c == d 為false,是因為本身就是兩個不同的位址
        System.out.println(c == d);
    }
}
           

3. 源碼解讀

Java源碼系列-Integer源碼

3.1 主要屬性

3.1.1 第一部分-常量參數

/**
 * A constant holding the minimum value an {@code int} can
 * have, -2^31.
 */
@Native public static final int   MIN_VALUE = 0x80000000;

/**
 * A constant holding the maximum value an {@code int} can
 * have, 2^31-1.
 */
@Native public static final int   MAX_VALUE = 0x7fffffff;

/**
 * The {@code Class} instance representing the primitive type
 * {@code int}.
 *
 * @since   JDK1.1
 */
@SuppressWarnings("unchecked")
public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");

/**
 * The number of bits used to represent an {@code int} value in two's
 * complement binary form.
 *
 * @since 1.5
 */
@Native public static final int SIZE = 32;

/**
 * The number of bytes used to represent a {@code int} value in two's
 * complement binary form.
 *
 * @since 1.8
 */
public static final int BYTES = SIZE / Byte.SIZE;
           
  • MIN_VALUE為Integer可定義的最小值,為-2^31 = -2147483648。
  • MIN_VALUE為Integer可定義的最大值,為2^31-1 = 2147483647。
  • SIZE用來表示二進制補碼形式的int值的比特數,值為32,靜态變量且不可變。
  • BYTES用來表示二進制補碼形式的int值的位元組數,值為SIZE除于Byte.SIZE,不同機器的值可能不一樣,在16位機中為2位元組,在32位機和64位機中為4位元組。
  • TYPE表示執行toString後的類型為int,Class的getPrimitiveClass是一個native方法,在Class.c中有個Java_java_lang_Class_getPrimitiveClass方法與之對應,是以JVM層面會通過JVM_FindPrimitiveClass函數根據”int”字元串獲得jclass,最終到Java層則為Class。

3.1.2 第二部分-數組

final static char [] DigitTens = {
        '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
        '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
        '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
        '3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
        '4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
        '5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
        '6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
        '7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
        '8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
        '9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
        } ;

final static char [] DigitOnes = {
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        } ;

final static char[] digits = {
        '0' , '1' , '2' , '3' , '4' , '5' ,
        '6' , '7' , '8' , '9' , 'a' , 'b' ,
        'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
        'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
        'o' , 'p' , 'q' , 'r' , 's' , 't' ,
        'u' , 'v' , 'w' , 'x' , 'y' , 'z'
        };

final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
        99999999, 999999999, Integer.MAX_VALUE };

// Requires positive x
static int stringSize(int x) {
        for (int i=0; ; i++)
        if (x <= sizeTable[i])
        return i+1;
}
           
  • DigitTens和DigitOnes兩個數組放到一起講更好了解,它們主要用于擷取0到99之間某個數的十位和個位,比如48,通過DigitTens數組直接取出來十位為4,而通過DigitOnes數組取出來個位為8。
  • digits數組用于表示數字的所有可能的字元,因為int支援從2進制到36進制,是以這裡需要有36個字元才能表示所有不同進制的數字。
  • sizeTable數組主要用在判斷一個int型數字對應字元串的長度。比如相關的方法如下,這種方法可以高效得到對應字元串長度,避免了使用除法或求餘等操作。

3.1.3 IntegerCache靜态内部類

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");
        // 如果設定了cache的高位,緩存就設定為[-128, high]
        if (integerCacheHighPropValue != null) {
            try {
                // 解析為int的靜态值
                int i = parseInt(integerCacheHighPropValue);
                // 取最大值
                i = Math.max(i, 127);
                // 最大值是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空間為256
        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            // 生成cache值的緩存空間
            cache[k] = new Integer(j++);

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

    private IntegerCache() {}
}
           

IntegerCache為靜态内部類,存儲在方法區中的靜态區,IntegerCache中的cache參數存儲在靜态區,包含了int可能值的Integer數組,預設範圍是[-128,127],最大值可通過指定方式進行調整,在啟動時 通過JVM參數Djava.lang.Integer.IntegerCache.high=xxx進行調整。當Integer的值範圍在[-128,127]時則直接從緩存中擷取對應的Integer對象, 不必重新執行個體化。這些緩存值都是靜态且final的,避免重複的執行個體化和回收。

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            // IntegerCache.cache空間如下:
            [0] = -128
            [1] = -127
            ...
            [129] = 1
            [130] = 2
            [256] = 127
            // e.g. i = 1, 那就從IntegerCache.cache[1+128] => Integer.cache[129] = 1
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
           

由上述代碼可知,當值在[-128,127]之間時,取值是直接從IntegerCache的cache中取值。

3.2 核心方法

max/min/sum方法

public final class Integer extends Number implements Comparable<Integer> {
    // 取兩者之間的最大值
    public static int max(int a, int b) {
        return Math.max(a, b);
    }
    // 取兩者之間的最小值
    public static int min(int a, int b) {
        return Math.min(a, b);
    }
    // 取兩者之間的和
    public static int sum(int a, int b) {
        return a + b;
    }
}
public class IntegerDemoTest {
    @Test
    public void maxTest() {
        // a => 97, b => 98 => max => 98
        System.out.println(Integer.max('a', 'b'));
        // a => 97, b => 98 => min => 97
        System.out.println(Integer.min('a', 'b'));
        // a => 97, b => 98 => sum => 195
        System.out.println(Integer.sum('a', 'b'));
    }
}
           

getInteger方法

public final class Integer extends Number implements Comparable<Integer> {
    public static Integer getInteger(String nm) {
        return getInteger(nm, null);
    }
    public static Integer getInteger(String nm, int val) {
        Integer result = getInteger(nm, null);
        return (result == null) ? Integer.valueOf(val) : result;
    }
    public static Integer getInteger(String nm, Integer val) {
        String v = null;
        try {
            v = System.getProperty(nm);
        } catch (IllegalArgumentException | NullPointerException e) {
        }
        if (v != null) {
            try {
                return Integer.decode(v);
            } catch (NumberFormatException e) {
            }
        }
        return val;
    }
}
           

Integer.getInteger(String)的功能是根據指定的名稱得到系統屬性的整數值。第一個參數将被認為是系統屬性的名稱。系統屬性可以通過 System.getProperty(java.lang.String)方法通路得到。屬性值字元串将被解釋成一個整數,并且以表示這個值的Integer對象形式傳回。可能出現的數字格式的詳細說明可以在 getProperty 的定義說明裡找到。

@Test
    public void testGetInteger() {
        System.setProperty("test-integer", "55");
        Integer testNumber = Integer.getInteger("test-integer");
        Assertions.assertEquals(55, testNumber);
    }
           

toString方法

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

    public static String toString(int i) {
        if (i == Integer.MIN_VALUE)
            return "-2147483648";
        int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
        char[] buf = new char[size];
        getChars(i, size, buf);
        return new String(buf, true);
    }

    public String toString() {
        return toString(value);
    }
    
    public static String toString(int i, int radix) {
        if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
            radix = 10;

        /* Use the faster version */
        if (radix == 10) {
            return toString(i);
        }

        char buf[] = new char[33];
        boolean negative = (i < 0);
        int charPos = 32;

        if (!negative) {
            i = -i;
        }

        while (i <= -radix) {
            buf[charPos--] = digits[-(i % radix)];
            i = i / radix;
        }
        buf[charPos] = digits[-i];

        if (negative) {
            buf[--charPos] = '-';
        }

        return new String(buf, charPos, (33 - charPos));
    }
}
           

一共有3個toString方法,兩個靜态方法一個是非靜态方法,第一個toString方法很簡單,就是先用stringSize得到數字是多少位,再用getChars擷取數字對應的char數組,最後傳回一個String類型。第二個toString調用第一個toString。第三個toString方法是帶了進制資訊的,它會轉換成對應進制的字元串。凡是不在2到36進制範圍之間的都會被處理成10進制,我們都知道從十進制轉成其他進制時就是不斷地除于進制數得到餘數,然後把餘數反過來串起來就是最後結果,是以這裡其實也是這樣子做的,得到餘數後通過digits數組擷取到對應的字元,而且這裡是用負數的形式來運算的。

reverseBytes方法

public final class Integer extends Number implements Comparable<Integer> {
    public static int reverseBytes(int i) {
        return ((i >>> 24)           ) |
                ((i >>   8) &   0xFF00) |
                ((i <<   8) & 0xFF0000) |
                ((i << 24));
    }
}
           

該方法傳回通過反轉指定int值的二進制補碼表示形式的位元組順序而獲得的值。

signum方法

public final class Integer extends Number implements Comparable<Integer> {
    public static int signum(int i) {
        // HD, Section 2-7
        return (i >> 31) | (-i >>> 31);
    }
}
           

改方法确定輸入的數的符号,如果輸入的是正數則傳回1,如果輸入的是零則傳回0,如果輸入的是負數則傳回-1。

@Test
    public void testSignum() {
        // the result is -1
        System.out.println(Integer.signum(-99));
        // the result is 1
        System.out.println(Integer.signum(99));
        // the result is 0
        System.out.println(Integer.signum(0));
    }
           

reverse方法

public final class Integer extends Number implements Comparable<Integer> {
    public static int reverse(int i) {
        // HD, Figure 7-1
        i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;
        i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;
        i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;
        i = (i << 24) | ((i & 0xff00) << 8) |
                ((i >>> 8) & 0xff00) | (i >>> 24);
        return i;
    }
}
           

用于傳回指定int值的二進制補碼二進制表示形式的位的相反順序。

@Test
    public void testReverse() {
        // the result is 100663296
        System.out.println(Integer.reverse(96));
    }
           

rotateLeft/rotateRight方法

public final class Integer extends Number implements Comparable<Integer> {
    public static int rotateRight(int i, int distance) {
        return (i >>> distance) | (i << -distance);
    }
    public static int rotateLeft(int i, int distance) {
        return (i << distance) | (i >>> -distance);
    }
}
           

rotateRight該方法通過将指定int值a的二進制補碼二進制表示形式向右旋轉指定位數來傳回獲得的值。位向右移,即低位。

public static void main(String[] args){ 
        int a = 64; 
        int shifts = 0; 
        while (shifts < 3) {
        // It will return the value obtained by rotating left
            a = Integer.rotateRight(a, 2); 
            System.out.println(a); 
            shifts++; 
        } 
    }
           
16
4
1
           

rotateLeft該方法傳回通過将指定int值的二進制補碼二進制表示形式向左旋轉指定數量的移位位數而獲得的值。

public static void main(String[] args){
        int a = 2; 
        int shifts = 0; 
        while (shifts < 6) {
        // It will return the value obtained by rotating left
            a = Integer.rotateLeft(a, 2); 
            System.out.println(a); 
            shifts++; 
        } 
    }
           
8
32
128
512
2048
8192
           

numberOfLeadingZeros/numberOfTrailingZeros方法

public final class Integer extends Number implements Comparable<Integer> {
    public static int numberOfLeadingZeros(int i) {
        // HD, Figure 5-6
        if (i == 0)
            return 32;
        int n = 1;
        if (i >>> 16 == 0) { n += 16; i <<= 16; }
        if (i >>> 24 == 0) { n +=  8; i <<=  8; }
        if (i >>> 28 == 0) { n +=  4; i <<=  4; }
        if (i >>> 30 == 0) { n +=  2; i <<=  2; }
        n -= i >>> 31;
        return n;
    }
    public static int numberOfTrailingZeros(int i) {
        // HD, Figure 5-14
        int y;
        if (i == 0) return 32;
        int n = 31;
        y = i <<16; if (y != 0) { n = n -16; i = y; }
        y = i << 8; if (y != 0) { n = n - 8; i = y; }
        y = i << 4; if (y != 0) { n = n - 4; i = y; }
        y = i << 2; if (y != 0) { n = n - 2; i = y; }
        return n - ((i << 1) >>> 31);
    }
}
           

highestOneBit/lowestOneBit方法

public final class Integer extends Number implements Comparable<Integer> {
    // 傳回一個 int 值,最多有一個單個位,位于指定 int 值中最高(“最左邊”)一位的位置。 如果指定的值在其二進制補碼表示中沒有一位,即等于零,則傳回零。
    public static int highestOneBit(int i) {
        // HD, Figure 3-1
        i |= (i >>  1);
        i |= (i >>  2);
        i |= (i >>  4);
        i |= (i >>  8);
        i |= (i >> 16);
        return i - (i >>> 1);
    }
    public static int lowestOneBit(int i) {
        // 傳回一個 int 值,最多隻有一個一位,位于指定 int 值中最低位(“最右邊”)一位的位置。 如果指定的值在其二進制補碼表示中沒有一位,即等于零,則傳回零。
        // HD, Section 2-1
        return i & -i;
    }
}
           

toUnsignedLong方法

public final class Integer extends Number implements Comparable<Integer> {
    /**
     * 
     * @param x 它是一個用于轉換為 unsigned long 的值。
     * @return
     */
    public static long toUnsignedLong(int x) {
        return ((long) x) & 0xffffffffL;
    }
}
           

它通過無符号轉換将參數轉換為 long。在對 long 的無符号轉換中,long 的 high-order 32 位為零,低 32 位等于整數參數地位。

divideUnsigned方法

public final class Integer extends Number implements Comparable<Integer> {
    /**
     * e.g. dividend=55,divisor=5, result為11
     * @param dividend 它是一個将被分割的 int 值。
     * @param divisor 将進行除法過程的值。
     * @return
     */
    
    public static int divideUnsigned(int dividend, int divisor) {
        // In lieu of tricky code, for now just use long arithmetic.
        return (int) (toUnsignedLong(dividend) / toUnsignedLong(divisor));
    }
}
           

該方法它傳回第一個參數除以第二個參數的無符号商,其中每個參數和結果都被解釋為一個無符号值。

remainderUnsigned方法

public final class Integer extends Number implements Comparable<Integer> {
    /**
     * dividend=56, divisor=5, the result is 1
     * @param dividend 它是一個将被分割的 int 值
     * @param divisor 将進行除法過程的值
     * @return
     */
    public static int remainderUnsigned(int dividend, int divisor) {
        // In lieu of tricky code, for now just use long arithmetic.
        return (int) (toUnsignedLong(dividend) % toUnsignedLong(divisor));
    }
}
           

它傳回第一個參數除以第二個參數的無符号餘數,其中每個參數和結果都被解釋為無符号值。

4.經典面試題

1. Integer a = 1、Integer a = new Integer(1)、Integer a = Integer.valueOf(1)的差別

  • Integer a = 1與Integer.valueOf(1)含義相同,所存儲的對象在方法區的靜态區。new Integer(1)存儲的位置在堆區。
  • -128-127的值存儲在JVM方法區的靜态區,是因為Integer内部有一個靜态内部類IntegerCache。
  • new Integer(1)存儲在JVM堆區,都是通過new Integer(x)建立出來。

2. 你所了解到IntegerCache

  • IntegerCache是Integer類的中的内部靜态類
  • IntegerCache中有一個低位,有一個高位,有一個Integer類型的數組緩存。其中低位為-128,不可改變,高位如果不設定則為127,高位可以通過通過JVM參數Djava.lang.Integer.IntegerCache.high=xxx進行調整,用緩存的好處在于快速讀取,不用再重新建立對象。

5. 文章參考

1.https://www.cmsblogs.com/article/1389544331186147328