1. 程式人生 > 程式設計 >Java 常用類原始碼解析——Long

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(String) 對比可以看出,valueOf(String) 方法會去使用 Long 的快取機制,而 parseLong 不會。所以在實際使用中,首選使用 valueOf(String)

總結

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

在包裝型別中 ,FloatDouble 沒有實現快取機制。

parseLong(String) 的方法實現中,設計者使用了累減而不是累加來統計引數已轉化出來的 result,這樣可以避免在後面與 limit 比較時,產生溢位對判斷結果造成影響。在 jdk 原始碼中,有很多這樣的奇淫巧技,多瞭解細節可以進一步提升自己。