Java 常用类源码解析——Long

类图

实现了 Comparable 接口,用于两个 Long 型变量直接的比较。所有的包装类型都实现了该接口。

成员变量

    /**
     * Long 型最小值,-2^63
     * have, -2<sup>63</sup>.
     */
    @Native public static final long MIN_VALUE = 0x8000000000000000L;

    /**
     * Long 型最大值,2^63 - 1
     */
    @Native public static final long MAX_VALUE = 0x7fffffffffffffffL;

    /**
     * 基础类型 long 的 Class 对象
     */
    @SuppressWarnings("unchecked")
    public static final Class<Long>     TYPE = (Class<Long>) Class.getPrimitiveClass("long");
    
    /**
     * 实际存储 Long 变量的值
     */
    private final long value;

    /**
     * long 型值的位数
     */
    @Native public static final int SIZE = 64;
复制代码

静态内部类

    private static class LongCache {
        private LongCache(){}
		// 缓存,范围从 -128 到 127,+1 是因为有个 0
        static final Long cache[] = new Long[-(-128) + 127 + 1];
		// 静态代码块,容器初始化时,进行加载
        static {
             // 缓存 Long 值,注意这里是 i - 128 ,所以获取的时候就需要 + 128
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Long(i - 128);
        }
    }
复制代码

Long 在内部类中实现了缓存机制,缓存了 [-128,127] 的所有 Long 值,如果是该范围内的 Long 值,将直接从缓存中获取。

构造方法


    /**
     * 构建一个新对象,对象值为 value
     */
    public Long(long value) {
        this.value = value;
    }

    /**
     * 使用 parseLong 方法将传入的 s 转化成 Long 值
     */
    public Long(String s) throws NumberFormatException {
        this.value = parseLong(s, 10);
    }
复制代码

常用方法

public static Long valueOf(long l)

    public static Long valueOf(long l) {
        final int offset = 128;
        if (l >= -128 && l <= 127) { // will cache
            return LongCache.cache[(int)l + offset];
        }
        return new Long(l);
    }
复制代码

valueOf 方法会根据传入的 long 值的范围来判断是否从缓存中获取值。在 LongCache 类中 cache 数组初始化时,索引 index 对应的值为 index - 128,因此这里使用 cache 数组时增加了向右偏移了 128 位。

public static long parseLong(String s)

    public static long parseLong(String s) throws NumberFormatException {
        // 默认 s 中的 long 值为十进制
        return parseLong(s, 10);
    }

    public static long parseLong(String s, int radix) throws NumberFormatException {
        if (s == null) {
            throw new NumberFormatException("null");
        }

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

        long result = 0;
        boolean negative = false;//是否为负数标识
        int i = 0, len = s.length();
        long limit = -Long.MAX_VALUE;
        long multmin;
        int digit;//存储每位字符的数值

        if (len > 0) {
            char firstChar = s.charAt(0);

            if (firstChar < '0') { 
                // 如果字符串第一位 ASC 码 < '0',那第一位必须为 '+' 或 '-',且字符串位数必须大于 1
                if (firstChar == '-') {
                    negative = true;
                    limit = Long.MIN_VALUE;
                } else if (firstChar != '+')
                    throw NumberFormatException.forInputString(s);

                if (len == 1) 
                    throw NumberFormatException.forInputString(s);
                i++;
            }
            // 执行 result *= radix 前 result 的最小值
            multmin = limit / radix;
            while (i < len) {
                // 累减,避免计算溢出问题影响结果
                // 计算出 s.charAt(i++) 在 radix 进制下的值
                digit = Character.digit(s.charAt(i++),radix);
                if (digit < 0) {
                    throw NumberFormatException.forInputString(s);
                }
                if (result < multmin) {
                    throw NumberFormatException.forInputString(s);
                }
                result *= radix;
                if (result < limit + digit) {
                    throw NumberFormatException.forInputString(s);
                }
                result -= digit;
            }
        } else {
            throw NumberFormatException.forInputString(s);
        }
        // 若是负数,则直接返回,若是正数,先取反结果再输出
        return negative ? result : -result;
    }
复制代码

parseLong 方法使用累减来计算 result。因为当数值接近 MAX_VALUE 时,累加产生的溢出值,会影响判断的结果,而累减产生的溢出值不会。

这里和 valueOf 对比可以看出,valueOf 方法会去使用 Long 的缓存机制,而 parseLong 不会。所以在实际使用中,首选使用 valueOf

总结

包装类在使用中很简单,基本上都是调用类型转化的方法。

在包装类型中 ,FloatDouble 没有实现缓存机制。

parseLong(String) 的方法实现中,设计者使用了累减而不是累加来统计参数已转化出来的 result,这样可以避免在后面与 limit 比较时,产生溢出对判断结果造成影响。在 jdk 源码中,有很多这样的奇淫巧技,多了解细节可以进一步提升自己。

猜你喜欢

转载自juejin.im/post/5db955bb6fb9a0207326ad97