有一个面试题:
求打印结果,这其实就是考验你对虚拟机堆内对象创建和integer包装类的理解。
public static void main(String[] args) {
Integer a = new Integer(100);
Integer b = 100;
int c = 100;
Integer d = new Integer(100);
System.out.println(a == d);
System.out.println(a == b);
System.out.println(a == c);
System.out.println(b == c);
System.out.println(c == d);
}
首先看这几个的创建方式
Integer a = new Integer(100):
对于这个没什么好说的,创建对象,在堆内存中分配一块内存空间,并调用<inin>方法实例化对象,返回对象的引用。
创建的执行码(看不懂自己去学习一下,对自己很有好处)
NEW java/lang/Integer
DUP
BIPUSH 100
INVOKESPECIAL java/lang/Integer.<init> (I)V
ASTORE 1
Integer b = 100:
这里返回的也一个对象,因为这里在编译后的字节码中隐式的调用了Integer的valueOf方法,最终返回的是一个Integer对象,我们来看执行码:
BIPUSH 100
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
ASTORE 2
对于这个valueOf方法有必要说一下,我们先看一下方法
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
对于这个方法,当你创建一个值大于-128和小于一个你指定(或者默认的127)时,生成的对象会被放在一个数组中,当再一次调用时,传值相同时,获得都是同一个对象。
int c = 100:
常量入栈的一个操作,没啥好说的:
BIPUSH 100
ISTORE 3
有必要说一下,在一个线程栈的栈帧里,局部变量被放在局部变量表里,对于对象的取用只跟位置有关,跟我们声明的 int c 没有关系,简单的说 声明是给开发人员用的,字节码中这些东西都不存在的。
各种声明方式说完了,我们就来说说比较时的执行过程
包装类在和基本类型一起操作时,有一个自动拆箱的操作,具体是咋操作的,我们来看一下执行码:
System.out.println(a == c) :
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 1
INVOKEVIRTUAL java/lang/Integer.intValue ()I
ILOAD 3
IF_ICMPNE L11
ICONST_1
GOTO L12
L11
FRAME SAME1 java/io/PrintStream
ICONST_0
L12
FRAME FULL [[Ljava/lang/String; java/lang/Integer java/lang/Integer I java/lang/Integer] [java/io/PrintStream I]
INVOKEVIRTUAL java/io/PrintStream.println (Z)V
我们可以看到在执行比较的时候隐式的调用了integer的intValue()方法获取到了integer的int类型的value值,所以无论是哪种integer对象的声明方式,最终和int值比较的时候都会自动拆箱后进行int值比较。
而integer对象比较就和对象类型比较一样了,最终比较的就是对象是不是同一个。
由于integer重写了equals方法,如果调用该方法比较,其实是比较的对象类内封装的int类型的值。
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
好了,关于integer和int类型的比较说完了。