欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【Java源码分析】Integer源码分析

程序员文章站 2022-07-14 16:14:47
...

今天带来的是Integer源码的分析。代码已经注释的非常清晰了。

1.parseInt方法

public static int parseInt(String s, int radix)
                throws NumberFormatException
    {
        /*
         * WARNING: This method may be invoked early during VM initialization
         * before IntegerCache is initialized. Care must be taken to not use
         * the valueOf method.
         */
        //String转换为Integter,不能是null
        if (s == null) {
            throw new NumberFormatException("null");
        }
        //最小二进制,最大36进制
        if (radix < Character.MIN_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " less than Character.MIN_RADIX");
        }

        if (radix > Character.MAX_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " greater than Character.MAX_RADIX");
        }

        int result = 0;       //转换后的结果
        boolean negative = false;	//如果是正数negative = false,如果是负数negative = true
        int i = 0, len = s.length();	//i是遍历String的下标,len是长度
        int limit = -Integer.MAX_VALUE;	//限制,不能超过Integer的最大值。负的MAX_VALUE(以下都是再负数范围内进行判断的,为了统一处理,因为正数的最大值转化为负数不会溢出,而负数的最小值转换为正数会溢出,我这里所说的溢出是指的Integer范围内)
        int multmin;	//一个限制,后面讲
        int digit;		//每个字符转换为数字后的值

        //如果是非空字符串
        if (len > 0) {
            char firstChar = s.charAt(0);	
            if (firstChar < '0') { // Possible leading "+" or "-"//ASCII码表小于‘0’的都是字符
                if (firstChar == '-') {		//如果是负号
                    negative = true;	//标志改为负号
                    limit = Integer.MIN_VALUE;		//界限是Integer最小值
                } else if (firstChar != '+')	//如果不是加号,说明第一个符号是个非法字符,抛出异常
                    throw NumberFormatException.forInputString(s);

                if (len == 1) // 不能只有一个 "+" 或者 "-"
                    throw NumberFormatException.forInputString(s);
                i++;	//i指向第一个数字的位置
            }
            //下面就是转换成数字的逻辑了。
            /*有很多小伙伴到这里可能不是很懂。首先limit表示溢出边界范围(这个程序中,无论正负,都统一成了负数),radix表示进制。
            	举个例子:假设十进制,我们到77就代表溢出了,那么multmin = -77 / 10 = -7,为什么要算这个数,是为了后面的操作。*/
            //这里可以用移位操作吧?
            multmin = limit / radix;
            while (i < len) {	//i没越界,执行字符转换成数字
                // Accumulating negatively avoids surprises near MAX_VALUE
                digit = Character.digit(s.charAt(i++),radix);	//字符转换成数字
                if (digit < 0) {	//转换出来如果每一位是负数,抛出异常
                    throw NumberFormatException.forInputString(s);
                }
                //关键来了:当前结果是否小于multmin,如果小于,抛出溢出异常。注意multmin一直是负数,如果result比multmin还要小,说明执行 result *= radix 的时候肯定是溢出了(以咱们的例子继续,multmin为-7,result < multmin,假设result为-9,那么执行result *= radix,result变成了-90,就发生了溢出 ),如果不提前抛出异常,那么我们的result中存的是溢出后的结果(结果不准确了)
                if (result < multmin) {
                    throw NumberFormatException.forInputString(s);
                }
                result *= radix;	
                //这里又是一个溢出判断,注意:digit是非负数,result,limit是负数。
                //换成result - digit < limit你可能更好理解,继续我们的例子,假设result = -70,digit = 9, -70 - 9 =- 79, 是小于-77的,这就发生了溢出。
                //但是为什么不换成result - digit < limit呢,因为在这里,做减法可能发生溢出。当我们算出-79的时候,result就已经发生了溢出,所以就将表达式变换了一下result < limit + digit,防止了溢出
                if (result < limit + digit) {
                    throw NumberFormatException.forInputString(s);
                }
                result -= digit;	//在没有溢出的情况下,算出result
            }
        //如果字符串是空串,直接抛出异常
        } else {
            throw NumberFormatException.forInputString(s);
        }
        return negative ? result : -result;	//判断标志,如果是负数,返回result,如果是正数,返回-result
    }

2.IntegerCache内部类

/**
     * Integer默认缓存-128到127之间的数,缓存在第一次使用时初始化。缓存的大小是可控制的,在server模式下用-XX:AutoBoxCacheMax=<size>
     */

    private static class IntegerCache {
        static final int low = -128;	//注意,这个值自始至终没有变过,所以下限一直是-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");	//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);	//注意,这里刚开始看可能很突兀,为什么是Integer.MAX_VALUE - (-low) -1呢?因为创建的是一个在low到high范围的Integer数组,如果大于了这个值,那么在为数组分配空间的时候就溢出了
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];	//缓存的最大空间是Integer.MAX_VALUE,这就是为什么h的最大值是Integer.MAX_VALUE - (-low) -1的原因了
            int j = low; 
            for(int k = 0; k < cache.length; k++)	//缓存的最大范围是-128到MAX_VALUE - (-low) -1
                cache[k] = new Integer(j++);

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

        private IntegerCache() {}
    }
3.toString方法
/**
     * 返回第一个参数用第二参数表示的字符串形式,第二个参数是进制数,默认是10进制
     *
     * 如果第一个参数是负的,那么返回结果中第一个字符是'-',如果第一个参数是正的,那么返回的结果中没有符号
     * 结果中剩余的字符表示第一个参数的大小
     *
     * @see     java.lang.Character#MAX_RADIX
     * @see     java.lang.Character#MIN_RADIX
     */
    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)];   //取余法进行进制转换,从buf的最后向前面添加
            i = i / radix;							//取整数部分
        }							
        buf[charPos] = digits[-i];	//最后剩下一位单独处理

        if (negative) {			//如果是负数就添加'-'号
            buf[--charPos] = '-';
        }

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