先看一段demo:
package com.onlyou;
/**
* Created by cd_huang on 2017/5/25.
*/
public class Test {
public static void main(String args[]) {
intTest();
}
public static String intTest(){
int aa =100;
Integer bb =100;
System.out.println(aa==bb);
Integer cc =100;
System.out.println(bb==cc);
int dd =1000;
Integer ee =1000;
System.out.println(dd==ee);
Integer ff =1000;
System.out.println(ee==ff);
Integer gg =null;
System.out.println(bb==gg);
System.out.println(aa==gg);
return null;
}
}
然后看运行结果:
true
true
true
false
false
Exception in thread "main" java.lang.NullPointerException
at com.onlyou.Test.intTest(Test.java:23)
at com.onlyou.Test.main(Test.java:8)
第一个对比是int和Integer的对比,在编译期会处理成int100和Integer.intVal
ue获得int100,两个int100的对比,所以是true。
第二个对比是Integer和Integer的对比,所以纯粹是两个对象,如果指向同一个内存位置就会为true,这里理论上是false。但是编译期做了优化,Integer bb =100,其实等同于Integer bb =Integer.valueOf(100),这两种写法编译后的字节码是一样的。Integer.valueOf方法代码如下:
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
代码对-127到127之间的Integer对象做了缓存,所以这里会是同一个对象,所以是true。
第三个对比是int和Integer的对比,是true的原因同第一个对比。
第四个对比是Integer和Integer的对比,是false的原因是1000不在-127到127之间,没有缓存,所以是两个对象。
第五个对比是Integer和Integer的对比,Integer100和null肯定是false。
第六个对比是int和Integer的对比,在编译期会处理成int和Integer.intVal来进行两个int的对比,Integer为空,所以这里直接报空指针异常。
所以,上面那段代码其实相当于下面这段代码。
package com.onlyou;
/**
* Created by cd_huang on 2017/5/25.
*/
public class Test {
public static void main(String args[]) {
intTest();
}
public static String intTest(){
int aa =100;
Integer bb =Integer.valueOf(100);
System.out.println(aa==bb.intValue());
Integer cc =Integer.valueOf(100);
System.out.println(bb==cc);
int dd =1000;
Integer ee =Integer.valueOf(1000);
System.out.println(dd==ee.intValue());
Integer ff =Integer.valueOf(1000);
System.out.println(ee==ff);
Integer gg =null;
System.out.println(bb==gg);
System.out.println(aa==gg.intValue());
return null;
}
}
解释完了后我们来看编译后的指令是什么样子的。
C:\Users\hcd>javac c:\Test.java
C:\Users\hcd>javap -v c:\Test.class
Classfile /c:/Test.class
Last modified 2017-5-25; size 1037 bytes
MD5 checksum c36ad2112f8e55c60615146d0a116c8d
Compiled from "Test.java"
public class com.onlyou.Test
SourceFile: "Test.java"
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #8.#22 // java/lang/Object."<init>":()V
#2 = Methodref #7.#23 // com/onlyou/Test.intTest:()Ljava/la
ng/String;
#3 = Methodref #24.#25 // java/lang/Integer.valueOf:(I)Ljava
/lang/Integer;
#4 = Fieldref #26.#27 // java/lang/System.out:Ljava/io/Prin
tStream;
#5 = Methodref #24.#28 // java/lang/Integer.intValue:()I
#6 = Methodref #29.#30 // java/io/PrintStream.println:(Z)V
#7 = Class #31 // com/onlyou/Test
#8 = Class #32 // java/lang/Object
#9 = Utf8 <init>
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 main
#14 = Utf8 ([Ljava/lang/String;)V
#15 = Utf8 intTest
#16 = Utf8 ()Ljava/lang/String;
#17 = Utf8 StackMapTable
#18 = Class #33 // java/lang/Integer
#19 = Class #34 // java/io/PrintStream
#20 = Utf8 SourceFile
#21 = Utf8 Test.java
#22 = NameAndType #9:#10 // "<init>":()V
#23 = NameAndType #15:#16 // intTest:()Ljava/lang/String;
#24 = Class #33 // java/lang/Integer
#25 = NameAndType #35:#36 // valueOf:(I)Ljava/lang/Integer;
#26 = Class #37 // java/lang/System
#27 = NameAndType #38:#39 // out:Ljava/io/PrintStream;
#28 = NameAndType #40:#41 // intValue:()I
#29 = Class #34 // java/io/PrintStream
#30 = NameAndType #42:#43 // println:(Z)V
#31 = Utf8 com/onlyou/Test
#32 = Utf8 java/lang/Object
#33 = Utf8 java/lang/Integer
#34 = Utf8 java/io/PrintStream
#35 = Utf8 valueOf
#36 = Utf8 (I)Ljava/lang/Integer;
#37 = Utf8 java/lang/System
#38 = Utf8 out
#39 = Utf8 Ljava/io/PrintStream;
#40 = Utf8 intValue
#41 = Utf8 ()I
#42 = Utf8 println
#43 = Utf8 (Z)V
{
public com.onlyou.Test();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>
":()V
4: return
LineNumberTable:
line 6: 0
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
0: invokestatic #2 // Method intTest:()Ljava/lang/Str
ing;
3: pop
4: return
LineNumberTable:
line 8: 0
line 9: 4
public static java.lang.String intTest();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=7, args_size=0
0: bipush 100
2: istore_0
3: bipush 100
5: invokestatic #3 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
8: astore_1
9: getstatic #4 // Field java/lang/System.out:Ljav
a/io/PrintStream;
12: iload_0
13: aload_1
14: invokevirtual #5 // Method java/lang/Integer.intVal
ue:()I
17: if_icmpne 24
20: iconst_1
21: goto 25
24: iconst_0
25: invokevirtual #6 // Method java/io/PrintStream.prin
tln:(Z)V
28: bipush 100
30: invokestatic #3 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
33: astore_2
34: getstatic #4 // Field java/lang/System.out:Ljav
a/io/PrintStream;
37: aload_1
38: aload_2
39: if_acmpne 46
42: iconst_1
43: goto 47
46: iconst_0
47: invokevirtual #6 // Method java/io/PrintStream.prin
tln:(Z)V
50: sipush 1000
53: istore_3
54: sipush 1000
57: invokestatic #3 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
60: astore 4
62: getstatic #4 // Field java/lang/System.out:Ljav
a/io/PrintStream;
65: iload_3
66: aload 4
68: invokevirtual #5 // Method java/lang/Integer.intVal
ue:()I
71: if_icmpne 78
74: iconst_1
75: goto 79
78: iconst_0
79: invokevirtual #6 // Method java/io/PrintStream.prin
tln:(Z)V
82: sipush 1000
85: invokestatic #3 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
88: astore 5
90: getstatic #4 // Field java/lang/System.out:Ljav
a/io/PrintStream;
93: aload 4
95: aload 5
97: if_acmpne 104
100: iconst_1
101: goto 105
104: iconst_0
105: invokevirtual #6 // Method java/io/PrintStream.prin
tln:(Z)V
108: aconst_null
109: astore 6
111: getstatic #4 // Field java/lang/System.out:Ljav
a/io/PrintStream;
114: aload_1
115: aload 6
117: if_acmpne 124
120: iconst_1
121: goto 125
124: iconst_0
125: invokevirtual #6 // Method java/io/PrintStream.prin
tln:(Z)V
128: getstatic #4 // Field java/lang/System.out:Ljav
a/io/PrintStream;
131: iload_0
132: aload 6
134: invokevirtual #5 // Method java/lang/Integer.intVal
ue:()I
137: if_icmpne 144
140: iconst_1
141: goto 145
144: iconst_0
145: invokevirtual #6 // Method java/io/PrintStream.prin
tln:(Z)V
148: aconst_null
149: areturn
LineNumberTable:
line 11: 0
line 12: 3
line 13: 9
line 14: 28
line 15: 34
line 16: 50
line 17: 54
line 18: 62
line 19: 82
line 20: 90
line 21: 108
line 22: 111
line 23: 128
line 24: 148
StackMapTable: number_of_entries = 12
frame_type = 255 /* full_frame */
offset_delta = 24
locals = [ int, class java/lang/Integer ]
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ int, class java/lang/Integer ]
stack = [ class java/io/PrintStream, int ]
frame_type = 255 /* full_frame */
offset_delta = 20
locals = [ int, class java/lang/Integer, class java/lang/Integer ]
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ int, class java/lang/Integer, class java/lang/Integer ]
stack = [ class java/io/PrintStream, int ]
frame_type = 255 /* full_frame */
offset_delta = 30
locals = [ int, class java/lang/Integer, class java/lang/Integer, int,
class java/lang/Integer ]
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ int, class java/lang/Integer, class java/lang/Integer, int,
class java/lang/Integer ]
stack = [ class java/io/PrintStream, int ]
frame_type = 255 /* full_frame */
offset_delta = 24
locals = [ int, class java/lang/Integer, class java/lang/Integer, int,
class java/lang/Integer, class java/lang/Integer ]
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ int, class java/lang/Integer, class java/lang/Integer, int,
class java/lang/Integer, class java/lang/Integer ]
stack = [ class java/io/PrintStream, int ]
frame_type = 255 /* full_frame */
offset_delta = 18
locals = [ int, class java/lang/Integer, class java/lang/Integer, int,
class java/lang/Integer, class java/lang/Integer, class java/lang/Integer ]
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ int, class java/lang/Integer, class java/lang/Integer, int,
class java/lang/Integer, class java/lang/Integer, class java/lang/Integer ]
stack = [ class java/io/PrintStream, int ]
frame_type = 82 /* same_locals_1_stack_item */
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ int, class java/lang/Integer, class java/lang/Integer, int,
class java/lang/Integer, class java/lang/Integer, class java/lang/Integer ]
stack = [ class java/io/PrintStream, int ]
}
上面就是intTest()方法编译后的二进制指令。这边简单的解释下二进制指令怎么看。
首先简单介绍下jvm内存模型。
没花太多时间找到更合适的图片了。在我的理解里,更希望以线程私有还是线程共享来区分上图的每个部分,感觉会更清楚。上图转自http://blog.csdn.net/u012152619/article/details/46968883,对内存模型没什么了解的可以先看链接里的内容,当然,最好是看一下周志明老师的《深入理解Java虚拟机》,最经典的虚拟机学习书本。上图中方法区和堆是线程共享的,即每个线程都可以访问,所以存在线程安全的问题。而虚拟机栈,本地方法栈,程序计数器是线程独有的。程序的执行,调用一个方法时,就相当于把方法进栈,在方法执行结束后出栈,符合后进先出。
因为二进制指令主要是描述java代码的执行,所以主要关注虚拟机栈内的局部变量表和操作数栈。操作数栈是执行的真正工作区,是重中之重。二进制指令可以对照http://www.blogjava.net/DLevin/archive/2011/09/13/358497.html查看含义。注意每次操作数栈的内容使用完后都会出栈。下面是我给二进制指令加了些注释,以及写了在每一条指令执行完后,操作数栈和本地变量表的变化。
public static java.lang.String intTest();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=7, args_size=0(操作数栈长度为3,本地常量表的长度为7,因为是静态方法,所以方法的参数个位为0)
0: bipush 100 //valuebyte值带符号扩展成int值入栈。
(此时操作数栈内容为:100,null,null,局部变量表为:null,null,null,null,null,null,null)
2: istore_0 //将栈顶int类型值保存到局部变量0中。
(此时操作数栈内容为:null,null,null,局部变量表为:100,null,null,null,null,null,null)
3: bipush 100 //valuebyte值带符号扩展成int值入栈。
(此时操作数栈内容为:100,null,null,局部变量表为:100,null,null,null,null,null,null)
5: invokestatic #8 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
//调用静态方法Integer.valueOf
(此时操作数栈内容为:Integer(100)的引用,null,null,局部变量表为:100,null,null,null,null,null,null)
8: astore_1 //将栈顶引用类型值保存到局部变量1中。
(此时操作数栈内容为:null,null,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
9: getstatic #4 // Field java/lang/System.out:Ljav
a/io/PrintStream;
(此时操作数栈内容为:PrintStream的引用,null,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
12: iload_0 //从局部变量0中装载int类型值入栈。
(此时操作数栈内容为:100,PrintStream的引用,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
13: aload_1 //从局部变量1中装载引用类型值入栈。
(此时操作数栈内容为:Integer(100)的引用,100,PrintStream的引用,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
14: invokevirtual #9 // Method java/lang/Integer.intVal
ue:()I
(此时操作数栈内容为:100,100,PrintStream的引用,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
17: if_icmpne 24 //若栈顶两int类型值不相等则跳转。(跳转到24,即'24: iconst_0'这行)
(此时操作数栈内容为:PrintStream的引用,null,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
20: iconst_1 //1(int)值入栈。
(此时操作数栈内容为:1,PrintStream的引用,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
21: goto 25 //无条件跳转到指定位置。(因为执行了20: iconst_1,所以跳过了24: iconst_0,直接跳到25: invokevirtual)
24: iconst_0 //0(int)值入栈。
(此时操作数栈内容为:0,PrintStream的引用,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
25: invokevirtual #5 // Method java/io/PrintStream.prin
tln:(Z)V
(此时操作数栈内容为:null,null,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
28: bipush 100
(此时操作数栈内容为:100,null,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
30: invokestatic #8 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
(此时操作数栈内容为:Integer(100)的引用,null,null,局部变量表为:100,Integer(100)的引用,null,null,null,null,null)
33: astore_2
(此时操作数栈内容为:null,null,null,局部变量表为:100,Integer(100)的引用,Integer(100)的引用,null,null,null,null)
34: getstatic #4 // Field java/lang/System.out:Ljav
a/io/PrintStream;
(逻辑同上面。。这里就不再重述了)
37: aload_1
38: aload_2
39: if_acmpne 46
42: iconst_1
43: goto 47
46: iconst_0
47: invokevirtual #5 // Method java/io/PrintStream.prin
tln:(Z)V
50: sipush 1000
53: istore_3
54: sipush 1000
57: invokestatic #8 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
60: astore 4
62: getstatic #4 // Field java/lang/System.out:Ljav
a/io/PrintStream;
65: iload_3
66: aload 4
68: invokevirtual #9 // Method java/lang/Integer.intVal
ue:()I
71: if_icmpne 78
74: iconst_1
75: goto 79
78: iconst_0
79: invokevirtual #5 // Method java/io/PrintStream.prin
tln:(Z)V
82: sipush 1000
85: invokestatic #8 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
88: astore 5
90: getstatic #4 // Field java/lang/System.out:Ljav
a/io/PrintStream;
93: aload 4
95: aload 5
97: if_acmpne 104
100: iconst_1
101: goto 105
104: iconst_0
105: invokevirtual #5 // Method java/io/PrintStream.prin
tln:(Z)V
108: aconst_null
109: astore 6
111: getstatic #4 // Field java/lang/System.out:Ljav
a/io/PrintStream;
114: aload_1
115: aload 6
117: if_acmpne 124
120: iconst_1
121: goto 125
124: iconst_0
125: invokevirtual #5 // Method java/io/PrintStream.prin
tln:(Z)V
128: getstatic #4 // Field java/lang/System.out:Ljav
a/io/PrintStream;
131: iload_0
132: aload 6
134: invokevirtual #9 // Method java/lang/Integer.intVal
ue:()I
137: if_icmpne 144
140: iconst_1
141: goto 145
144: iconst_0
145: invokevirtual #5 // Method java/io/PrintStream.prin
tln:(Z)V
148: aconst_null
149: areturn
文章中很多内容都只是个人的一些理解,如果有什么问题可以在评论里留言。欢迎交流。