注:本文作者是阿里巴巴B2B的曹龙强;
引言
在平时的开发中,声明一个数字类型的变量,一般有以下几种形式:
1
int
a = 1;
2
Integer b =
1;
3
Integer c =
new Integer(1);
4
Integer d =
Integer.valueOf(1);
int类型为基本数据类型,a指向的是一个字面量,不是类的对象实例,它是编译期可知的。b、c、d一样,都是指向的类对象实例的引用。
字节码分析
上面这段代码编译后的class文件,用javap命令解析后,可以看到字节码指令如下:
1
Code:
2
Stack=3,
Locals=5, Args_size=1
3
//将int型值1推至栈顶
4
0: iconst_1
5
//将栈顶的int型值放入本地变量表中的第二个
6
1: istore_1
7
//将int型值1推至栈顶
8
2: iconst_1
9
//调用java.lang.Integer的静态方法valueOf(java.lang.Integer),参数是栈顶的元素,并将返回值放入栈顶
10
3: invokestatic #16;
//Method
java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
11
//将栈顶的引用类型值放入本地变量表中的第三个
12
6: astore_2
13
//创建一个Integer对象
14
7: new
#17;
//class java/lang/Integer
15
//将该对象的引用压入栈顶
16
10: dup
17
//将int型值1推至栈顶
18
11: iconst_1
19
//调用java.lang.Integer类的初始化方法,该方法参数是栈顶的int型元素,返回值放入栈顶
20
12: invokespecial #22;
//Method
java/lang/Integer."":(I)V
21
//将栈顶的引用类型值放入本地变量表中的第四个
22
15: astore_3
23
//将int型数值1压制栈顶
24
16: iconst_1
25
//调用java.lang.Integer的静态方法valueOf(java.lang.Integer),参数是栈顶的元素,并将返回值放入栈顶
26
17: invokestatic #16;
//Method
java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
27
//将栈顶的引用类型值放入本地变量表中的第四个
28
20: astore 4
29
22: return
通过分析上面的java字节码,可以印证开始的b/c/d都是指向Integer类的对象实例的论证。
但是又有了下面的问题:
1
System.out.println(a
== b);
2
System.out.println(a
== c);
3
System.out.println(a
== d);
4
System.out.println(b
== c);
5
System.out.println(b
== d);
6
System.out.println(c
== d);
输出是:
1
true
2
true
3
true
4
false
5
true
6
false
可见,a与bcd都相等而bc、cd不相等,bd相等,这是为什么呢?
首先,a与bcd都相等,这是常识了。当和包装类对比的对象是其对应的基本类型时,包装类会自动拆箱成基本数据类型与之对比。而bcd都是对象,相互对比的话,自然就是对比的引用地址了,由此,因为c是new
Integer的操作,所以bc、cd不相等也是很显而易见的,那为什么bd是相等的呢?
源码分析
我们可以在编译后的字节码的分析中看到,b和d的初始化过程是一样的,都是调用了Integer的valueOf静态方法,难道这个方法里有乾坤?
1
public
static Integer valueOf(int i) {
2
if(i
>= -128
&& i
<= IntegerCache.high)
3
return
IntegerCache.cache[i + 128];
4
else
5
return
new Integer(i);
6
}
上面是Integer类valueOf方法的源码。
代码中,首先根据参数值和IntegerCache.high值和-128做了对比,如果参数值在此范围内的话,则返回IntegerCache中的一个数组元素,否则,则返回一个新的Integer对象。
那如此看来,b和d初始化时,返回的都是IntegerCache的同一个数组元素值了?
再去看看IntegerCache。
1
private
static class IntegerCache
{
2
static
final int high;
3
static
final Integer cache[];
4
static
{
5
final
int low
= -128;
6
// high value may
be configured by property
7
int
h = 127;
8
if
(integerCacheHighPropValue !=
null)
{
9
// Use Long.decode
here to avoid invoking methods that
10
// require
Integer's autoboxing cache to be initialized
11
int
i =
Long.decode(integerCacheHighPropValue).intValue();
12
i = Math.max(i,
127);
13
// Maximum array
size is Integer.MAX_VALUE
14
h = Math.min(i,
Integer.MAX_VALUE - -low);
15
}
16
high = h;
17
cache =
new Integer[(high - low) + 1];
18
int
j = low;
19
for(int
k = 0; k <
cache.length; k++)
20
cache[k] =
new Integer(j++);
21
}
22
private
IntegerCache() {}
23
}
IntegerCache
是Integer类的私有静态内部类,有两个静态元素,int型的high和Integer数组cache[].分析源码可以看到,该类主要是在初始化的时候初始化cache[]数组,其大小是high-low+1,默认情况下,大小是256,元素数值范围是[-128,127],这不也是byte类型的数值范围么?如果在JVM启动的时候设置了java.lang.Integer.IntegerCache.high参数,则cache的大小及数值由integerCacheHighPropValue决定。
由此说明,b和d返回同一个实例论证成功,那么对于如下程序呢?
1
Integer e =
200;
2
Integer f =
200;
3
System.out.println(e
== f);
这个是打印true还是false呢?不用说了吧~~
练习一下
下面例子的打印呢?
1
Integer a =
1;
2
Integer b =
2;
3
Integer c =
3;
4
Integer d =
3;
5
Integer e =
321;
6
Integer f =
321;
7
Long g =
3L;
8
9
System.out.println(c
== d);
10
System.out.println(e
== f);
11
System.out.println(c
== (a+b));
12
System.out.println(c.equals(a+b));
13
System.out.println(g
== (a+b));
14
System.out.println(g.equals(a+b));
结果:
true
false
true
true
true
false