Skip to main content

JVM源码解读-Integer类

finen...About 11 minJava-SourceJava-Source

1. 类图

Integer-Class
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. 源码解读

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字符串获得class,最终到Java层则为Class<Integer>

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/1389544331186147328open in new window

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8