共计 4632 个字符,预计需要花费 12 分钟才能阅读完成。
上次咱们曾经对 String
类源码做了一个简略的总结,感兴趣的小伙伴能够去看一下啃碎 JDK 源码 (一):String,明天来看看 Java 的Integer
包装类。
先来看看 Integer
实现了哪些接口
public final class Integer extends Number implements Comparable<Integer> {
能够看到 Integer
用final
润饰,代表不可被继承,继承 Number
类实现 Comparable
接口。
private final int value;
public static final int MIN_VALUE = 0x80000000;// 最大值
public static final int MAX_VALUE = 0x7fffffff;// 最小值
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
能够看出应用了 int
类型的 value
来存储值,并且定义了 Integer
的最大值为 2^31-1
,最小值为-2^31
。Integer
的根本数据类型为int
。
先来看一下 Integer
的两个构造函数:
public Integer(int value) {this.value = value;}
public Integer(String s) throws NumberFormatException {this.value = parseInt(s, 10);
}
那么这个 parseInt
办法是干啥用的呢?
办法 parseInt(String s,int radix)
的目标是输入一个十进制数。
比方:parseInt(1010,2)
意思就是:输入 2 进制数 1010 在十进制下的数。
咱们平时用到 Integer.parseInt("123");
其实默认是调用了int i =Integer.parseInt("123",10);
上面是源码:
public static int parseInt(String s, int radix)
throws NumberFormatException
{
/*
* WARNING: This method may be invoked early during VM initialization
* before IntegerCache is initialized. Care must be taken to not use
* the valueOf method.
*/
if (s == null) {throw new NumberFormatException("null");
}
// 判断基数是否在 2~36 之间
if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix" + radix +
"less than Character.MIN_RADIX");
}
if (radix > Character.MAX_RADIX) {//36
throw new NumberFormatException("radix" + radix +
"greater than Character.MAX_RADIX");
}
int result = 0;
boolean negative = false;// 是否为正数,默认为 false
int i = 0, len = s.length();
int limit = -Integer.MAX_VALUE;
int multmin;
int digit;
if (len > 0) {char firstChar = s.charAt(0);
if (firstChar < '0') { // Possible leading "+" or "-"
if (firstChar == '-') {// 如果是正数,negative 赋值为 true,限度变为 int 的最小值
negative = true;
limit = Integer.MIN_VALUE;
} else if (firstChar != '+')
throw NumberFormatException.forInputString(s);
if (len == 1) // Cannot have lone "+" or "-"
throw NumberFormatException.forInputString(s);
i++;
}
/**multmin 避免数据溢出
* 如果是负数就是 -2,147,483,64
* 如果是正数就是 -2,147,483,64 **/
multmin = limit / radix;
while (i < len) {
// Accumulating negatively avoids surprises near MAX_VALUE
// 获取字符转换成对应进制的整数
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;
}
valueOf
接下来来看一个比拟经典的问题,看上面代码:
Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;
System.out.println(a == b);
System.out.println(c == d);
你认为这里是四个不同的对象,那么输入应该都是 flase?
不,输入后果为:
true
false
通过 javap -c/javap -verbose
命令能够查看字节码;红色圈圈里就是咱们 jdk5 之后的根本类型的主动包装的字节码实现,能够看出,此处是调用了Integer.valueOf(..)
办法的:说白了就是Integer a = 100
等价于Integer a = Integer.valueOf(100)
JDK1.5 之后,java 提供了主动装箱和主动拆箱的性能。主动装箱也就是调用了 Integer 类的一个静态方法 valueOf
办法,那咱们来看看源码是如何实现的:
看看 IntegerCache
是个什么货色:
能够看到 IntegerCache
是一个动态外部类,low 的值曾经写死 -128,而 high 的值由你的虚拟机决定 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high")
,既然是一个参数也就意味着你能够动静设置。而后在循环中将low - high
之间数字的装箱后办法 cache[]
这个 Integer 类型的数组中。这样就实现了缓存。
因为当咱们调用 valueOf
办法时传入 -128 到 127 之间的数字时,Integer
会给咱们返回同样的对象,记住这一点当前面试的时候也能够和面试官吹吹牛逼了!
接下来咱们来看看 Integer
实现的 equals
、hashcode
和toString
等办法:
equals 和 hashcode
public boolean equals(Object obj) {if (obj instanceof Integer) {return value == ((Integer)obj).intValue();}
return false;
}
能够看到它会先判断类型是否合乎,而后进行拆箱比拟操作。
看下 hashcode
办法:
@Override
public int hashCode() {return Integer.hashCode(value);
}
public static int hashCode(int value) {return value;}
能够看到 hashcode
间接返回自身的 int 值。
toString
Integer
的 toString
办法 是我认为一个比拟乏味的中央:
看下 toString(int i)
源码:
在下面用到了 stringSize
办法,就是求这个 Integer
数的长度,咱们来看看他是如何实现的:
能够看到这段代码在计算 Integer
数长度时,构建了一个一维数组,而后拿 x 与数组每个值进行比拟。
还有一个 getChars
办法是获取数值对应的字符串,其中有两个中央应用了十分奇妙的形式来进行除法运算和取余运算。在计算机中,a/b
和 a%b
相比拟位运算,都是比拟费时的计算的。上面来看看 jdk 中是如何优化计算的:
static void getChars(int i, int index, char[] buf) {
int q, r;
int charPos = index;
char sign = 0;
if (i < 0) {
sign = '-';
i = -i;
}
// Generate two digits per iteration
while (i >= 65536) {
q = i / 100;
// really: r = i - (q * 100);
r = i - ((q << 6) + (q << 5) + (q << 2));
i = q;
buf [--charPos] = DigitOnes[r];
buf [--charPos] = DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
for (;;) {q = (i * 52429) >>> (16+3);
r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
buf [--charPos] = digits [r];
i = q;
if (i == 0) break;
}
if (sign != 0) {buf [--charPos] = sign;
}
}
其中有一行代码:
q = (i * 52429) >>> (16+3);
下面这行公式约等于 q * 0.1
,也就是说应用乘法计算比应用除法高效。
思路是这样:
当 i >= 65536 时,是每两位取出数字,i /= 100,例如 i = 567235474,
(1)先取最初两位 7 和 4 放入 buf 数组中,i = 5672354,buf = {, , , , , , , ‘7’, ‘4’};
(2)再取最初两位 5 和 4 放入 buf 数组中,i = 56723,buf = {, , , , , ‘5’, ‘4’, ‘7’, ‘4’};
当 i < 65536 时,跳出循环,采纳每一次取出一位数字,也就是 i /= 10
(3)取最初一位 3 放入 buf 数组中,i = 5672,buf = {, , , , ‘3’, ‘5’, ‘4’, ‘7’, ‘4’};
(4)取最初一位 2 放入 buf 数组中,i = 567,buf = {, , , ‘2’, ‘3’, ‘5’, ‘4’, ‘7’, ‘4’};
(5)取最初一位 7 放入 buf 数组中,i = 56,buf = {, , ‘7’, ‘2’, ‘3’, ‘5’, ‘4’, ‘7’, ‘4’};
(6)取最初一位 6 放入 buf 数组中,i = 5,buf = {, ‘6’, ‘7’, ‘2’, ‘3’, ‘5’, ‘4’, ‘7’, ‘4’};
(7)取最初一位 5 放入 buf 数组中,i = 0,buf = {‘5’, ‘6’, ‘7’, ‘2’, ‘3’, ‘5’, ‘4’, ‘7’, ‘4’},完结。
总结
对于 Integer
类临时介绍到这里,无关其它的包装类 Long
等源码局部也是相似的,后续就不再介绍,有趣味的小伙伴能够去钻研一下。