Java 常用類原始碼解析——Long
阿新 • • 發佈:2019-12-31
類圖
實現了 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)
。
總結
包裝類在使用中很簡單,基本上都是呼叫型別轉化的方法。
在包裝型別中 ,Float
和 Double
沒有實現快取機制。
在 parseLong(String)
的方法實現中,設計者使用了累減而不是累加來統計引數已轉化出來的 result
,這樣可以避免在後面與 limit
比較時,產生溢位對判斷結果造成影響。在 jdk
原始碼中,有很多這樣的奇淫巧技,多瞭解細節可以進一步提升自己。