jdk1.8源码填坑计划-2-Integer和类似的包装类

java的基本类型有:byte short int long float double char boolean,同时这些基本数据类型又有各自对应的包装类 Byte Short Integer Long Float Double Character Boolean。这些类中前五个都继承于抽象类Number,因此都去实现了intValue(), longValue(), floatValue(), doubleValue()这些方法,对于这些方法我就不再看了,这里面也没有什么大的玄机。
因此我的想法是把Integer看了,用这个来代表这些包装类。

Integer

Integer是final的,不能再被继承了。Integer中好多个方法和静态属性,大部分都没有很特殊,我主要看那几个比较重要的方法

构造方法 Integer(int) 和 Integer(String)

1
2
3
4
5
6
public Integer(int value) {
this.value = value;
}
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}

可以看到默认参数为String的构造方法支持十进制数的直接转换,不能转的时候会抛出异常。

parseInt(String, int)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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");
}

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");
}

int result = 0;
boolean negative = 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;
limit = Integer.MIN_VALUE;
} else if (firstChar != '+')
throw NumberFormatException.forInputString(s);

if (len == 1) // Cannot have lone "+" or "-"
throw NumberFormatException.forInputString(s);
i++;
}
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;
}

这里比较神奇的是没采用我想象得 “123” = 1*10^2 + 2*10^1 + 3*10^0 这种方式,反而采用了以0去减的方法,从右往左依次计算,算出来一个-123 然后根据符号再进行取反。为什么要这么做呢,因为这里“Accumulating negatively avoids surprises near MAX_VALUE” MAX_VALUE = 2^32 -1 MIN_VALUE = -2^32 如果输入的值恰好是-2^32,采用我想像的哪种方法就会溢出了。。真的超严谨。

注释的warring中说到:这个方法在IntegerCache初始化之前就可以用了,相比而言valueOf()方法就要注意不能在IntegerCache初始化之前调用…

valueOf 方法

valueOf()一共有三个

1
2
3
4
5
6
7
8
9
10
11
public static Integer valueOf(String s, int radix) throws NumberFormatException {
return Integer.valueOf(parseInt(s,radix));
}
public static Integer valueOf(String s) throws NumberFormatException {
return Integer.valueOf(parseInt(s, 10));
}
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}

第一第二个方法直接调用了parseInt然后又做了装箱,返回Integer对象,效率不会比直接用parseInt高。
第三个方法中用到了IntegerCache,这是Integer中比较重要的一个内部类,采用缓存机制,整型缓存常量池。这也是JDK1.5中加入的,这里的Integer.valueOf(int i) 就是自动装箱的实现,这样做可以提高内存利用率,同时又能提高性能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];

static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;

cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);

// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}

private IntegerCache() {}
}

在其他的类似的类中,Byte Short Long Character都采用了这种策略,前三个范围是 -128到127,Character范围是0-127,这些范围都是不可变的,唯独Integer可以指定high边界。同样为Number子类的Float Double没有这策略,它所有的都一样计算。

自动装箱自动拆箱

jdk1.5中加入了这个,当我们写下Integer a = 2; 编译器会自动编译为Integer a = Integer.valueOf(2);
这里的2恰好在IntegerCache内,所以并没有重新创建一个新的对象,而是从cache中直接拿出来了,在jvm启动的时候,就已经把cache填满了。
同样的,当我们写下

1
2
Integer a = new Integer(128);
int m = a;

经过编译器修改之后得到的是

1
2
Integer a = new Integer(128);
int m = a.intValue();

这就是自动拆箱。总结起来就是

  • 自动装箱就是 Integer.valueOf(int i);
  • 自动拆箱就是 i.intValue();

对于以上装箱拆箱的情况,以下程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {
Integer i = 10;
Integer j = 10;
System.out.println(i == j);

Integer a = 128;
Integer b = 128;
System.out.println(a == b);

int k = 10;
System.out.println(k == i);
int kk = 128;
System.out.println(kk == a);

Integer m = new Integer(10);
Integer n = new Integer(10);
System.out.println(m == n);
}

输出的结果便是:

  • true
  • false
  • true
  • true
  • false

equals(Object obj)方法

1
2
3
4
5
6
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}

拆箱后作为int比较,这个用着很安心。

比较大小

Integer implements 了 Comparable接口,所以它实现了一个我们日常用的compare方法

1
2
3
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}

如果 x < y 返回 -1
如果 x == y 返回 0
如果 x > y 返回 1

clone()为什么可以深拷贝Integer

克隆本身是不调用构造函数的,但是克隆的结果确实产生新对象。
我找到了一个外国人说的这句话 java.lang.Integer are immutable. There is no reason to clone one. If you’re trying to waste memory, try Integer.valueOf(myInteger.intValue()). 哈哈还挺幽默。。。java.lang.Integer are immutable. 这句话我也不知道该怎么翻译,就是 Integer是个不可变对象(immutable objects) 那么对应的总要有一个mutable objects吧,我终于明白我日常写的那些类都是mutable的。
immutable objects包含如下原则

  • immutable对象的状态在创建之后就不能发生改变,任何对它的改变都应该产生一个新的对象。
  • immutable类应该是final的。
  • immutable类的所有的属性都应该是final的
  • immutable类中的mutable属性被返回时应该返回这个类的深拷贝

所以啊,Integer等包装类,String相关的那些类都是immutable的,所以能够被深拷贝,不会影响原对象。
参考这里
这才是这篇文章最大的知识点。

0%