Java 經常使用類源碼解析——Long

類圖

實現了 Comparable 接口,用於兩個 Long 型變量直接的比較。全部的包裝類型都實現了該接口。java

成員變量

/** * 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 值,將直接從緩存中獲取。git

構造方法

/** * 構建一個新對象,對象值爲 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(String) 對比能夠看出,valueOf(String) 方法會去使用 Long 的緩存機制,而 parseLong 不會。因此在實際使用中,首選使用 valueOf(String)less

總結

包裝類在使用中很簡單,基本上都是調用類型轉化的方法。this

在包裝類型中 ,FloatDouble 沒有實現緩存機制。spa

parseLong(String) 的方法實現中,設計者使用了累減而不是累加來統計參數已轉化出來的 result,這樣能夠避免在後面與 limit 比較時,產生溢出對判斷結果形成影響。在 jdk 源碼中,有不少這樣的奇淫巧技,多瞭解細節能夠進一步提高本身。設計

相關文章
相關標籤/搜索