JDK源码学习--Integer

Integer是int的的包装类,java虚拟机在两者之间提供了自动装包和拆包机制.

toString(int i),toString(int i, int radix)

返回用第二个参数指定基数表示的第一个参数的字符串表示形式,如果第二个参数不指定则默认返回10进制.

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);
    }

static void getChars(int i, int index, char[] buf) {
    int q, r;
    int charPos = index;
    char sign = 0;

    if (i < 0) {
        sign = '-';
        i = -i;
    }

    // Generate two digits per iteration
    while (i >= 65536) {
        q = i / 100;
    // really: r = i - (q * 100);
        r = i - ((q << 6) + (q << 5) + (q << 2));
        i = q;
        buf [--charPos] = DigitOnes[r];
        buf [--charPos] = DigitTens[r];
    }

    // Fall thru to fast mode for smaller numbers
    // assert(i <= 65536, i);
    for (;;) {
        q = (i * 52429) >>> (16+3);
        r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...
        buf [--charPos] = digits [r];
        i = q;
        if (i == 0) break;
    }
    if (sign != 0) {
        buf [--charPos] = sign;
    }
    }
//取出十位数的数字。这里设计的很巧妙,连续的10位数保存的值是该数组地址的十位数.所以输入任意的两位数就可以直接获得十位数的数字.
//下面数组可以看做一张表格,行表示个位数,列表示十位数. 行,列的范围都是0-10. 根据该坐标可以直接从数组中获取得到对应的十位数字.这里的做法就是将每一行都设置为相同的数字,即十位数的数字.
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',
    } ;

//取出个位数的数字,输入任意两位数能获得其个位上的数字,这里的做法是,num%10的位置存放num%10的值.
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',
} ;

代码中将(q * 100)的计算转化求解((q << 6) + (q << 5) + (q << 2)).这样做是否效率会有所提高呢?

如果我们自己去实现的话,对于十进制一般做法是循环地对10求余数,然后再除以10不断循环知道结果为0.

Integer 缓存

为提高效率,JDK将[-128,127]之间的这些常用的int值的Integer对象进行了缓存。这是通过一个静态内部类来实现的。代码如下:

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];
    static {
        int h = 127;
        //代码省略
        ...

        high = h;
        cache = new Integer[(high - low) + 1];
        int j = low;
        //为-128到127之间的每个数字都设置了缓冲
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);
        assert IntegerCache.high >= 127;
    }
    private IntegerCache() {}
    }

valueOf方法内部实现是基于IntegerCache,如果参数值在-128~127范围内则返回缓冲中的实例对象,否则创建一个新的实例,代码如下:

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

这也就解释了为什么会有如下的结果:

1 Integer a1 = Integer.valueOf(13);
2 Integer a2 = Integer.valueOf(13);
3 Integer a3 = Integer.valueOf(133);
4 Integer a4 = Integer.valueOf(133);
5 System.out.println(a1 == a2); //true
6 System.out.println(a3 == a4); //false

Integer缓冲的应用很广,除了valueOf方法里使用到该缓冲,自动包装机制其实也是采用了缓冲机制,即Integer i01 = 59; i01的值是从缓冲中获取得到.但Integer b01 =133.是创建了一个新的对象.所以:

Integer i01 = 59; //自动装箱,内部应该是采用了和valueOf()一样的生成机制生成一个对象.
    int i02 = 59;   //基本类型
    Integer i03 =Integer.valueOf(59); //59值在缓冲范围内,所以直接从缓冲中取得
    Integer i04 = new Integer(59); //明确生成一个新的对象.

    System.out.println(i01== i02); //true,与基本类型比较时是直接比较值
    System.out.println(i01== i03); //true, 说明两者都是从缓冲中取得
    System.out.println(i03== i04); //false i04是新建对象,这里比的是地址.
    System.out.println(i02== i04); //true 和基本类型比较时直接比较值,而不是地址.
    System.out.println(i01== i04); //false 比较地址,i01是从缓冲中取得的.

//当值超过了缓冲范围则:
Integer a01 = 133;
    int a02 = 133;
    Integer a03 =Integer.valueOf(133);
    Integer a04 = new Integer(133);

    System.out.println(a01== a02);// true 基本类型与包装类的比较
    System.out.println(a01== a03); //false 比较地址,因为133超过了缓冲范围,所以两者都是创建新的对象.
    System.out.println(a03== a04); //false 
    System.out.println(a02== a04); // true
    System.out.println(a01== a04); // false 比较地址

以上代码可以总结得到一下内容:

  1. 基本类型与包装类比较时是比较数值
  2. 自动装箱操作,即将一个int变量直接赋值给Integer变量,内部采用了和valueOf一样的生成机制.即如果数值在缓冲范围,则直接从缓冲中取得一个实例.否则生成一个实例对象.
0%