类图
实现了 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
。
总结
包装类在使用中很简单,基本上都是调用类型转化的方法。
在包装类型中 ,Float
和 Double
没有实现缓存机制。
在 parseLong(String)
的方法实现中,设计者使用了累减而不是累加来统计参数已转化出来的 result
,这样可以避免在后面与 limit
比较时,产生溢出对判断结果造成影响。在 jdk
源码中,有很多这样的奇淫巧技,多了解细节可以进一步提升自己。