用jvm指令分析String 常量池
其他博友的不同理解方式: http://hi.baidu.com/boywell/item/d5ee5b0cc0af55c875cd3cfd
我们先来看一个类
public class javaPTest { /**常量池 * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub String i1 = "hello"; String i2="world"; String i3="helloworld"; String i4="hello"+"world"; String i5=new String("helloworld"); String i6=new String("helloworld"); System.out.println("helloworld"); System.out.println(i5==i6); System.out.println(i3==i4); } }
result: helloworld false true
why?我们可以通过javap -c javaPTest (前提是:先用javac编译通过) 来看该类的反编译结果
注意: ldc #2 是将常量池中下标为2的常量加载到栈中
astore_1 将栈顶元素存到到当前fame局部变量数组下标为1的变量中,栈顶元素出栈
invokespecial 调用超类构造方法、实例初始化方法、私有方法 aload:当前frame的局部变量数组中下标为index的引用型局部变量进栈 ldc :将int、float或String型常量值从常量池中推送至栈顶 astore i: 将栈顶数值(objectref)存入当前frame的局部变量数组中指定下标(index)处的变量中,栈顶数值出栈。 new :创建一个对象,并且其引用进栈 dup :复制栈顶数值,并且复制值进栈 F:\JAVA\javaIDE\study11.29\src\com\study\main>javap -c javaPTest Compiled from "javaPTest.java" public class com.study.main.javaPTest extends java.lang.Object{ public com.study.main.javaPTest(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: ldc #2; //String hello //将string类型常量值(hello)从常量池推送至栈顶 2: astore_1 //将 3: ldc #3; //String world 5: astore_2 6: ldc #4; //String helloworld 8: astore_3 9: ldc #4; //String helloworld 11: astore 4 13: new #5; //class java/lang/String //new了一个String对象,并将其引用进栈,
16: dup 17: ldc #4; //String helloworld 19: invokespecial #6; //Method java/lang/String."<init>":(Ljava/lang/Strin g;)V 22: astore 5 24: new #5; //class java/lang/String 27: dup 28: ldc #4; //String helloworld 30: invokespecial #6; //Method java/lang/String."<init>":(Ljava/lang/Strin g;)V 33: astore 6 35: getstatic #7; //Field java/lang/System.out:Ljava/io/PrintStream; 38: ldc #4; //String helloworld 40: invokevirtual #8; //Method java/io/PrintStream.println:(Ljava/lang/Str ing;)V 43: getstatic #7; //Field java/lang/System.out:Ljava/io/PrintStream; 46: aload 5 //将常量数组中下边为5和6的变量加载到栈中(其实两个都存放#4 17行和28行分别表示在数组的4,5下标中,存放#4)
48: aload 6 50: if_acmpne 57 //比较 如果不相等就跳转
53: iconst_1 54: goto 58 57: iconst_0 58: invokevirtual #9; //Method java/io/PrintStream.println:(Z)V 61: getstatic #7; //Field java/lang/System.out:Ljava/io/PrintStream; 64: aload_3 //将常量数组中下边为3和4的变量加载到栈中(其实两个都存放#4 8行和11行分别表示在数组的3,4下标中,存放#4)
65: aload 4 67: if_acmpne 74 70: iconst_1 71: goto 75 74: iconst_0 75: invokevirtual #9; //Method java/io/PrintStream.println:(Z)V 78: return }
我们重点看看String i5=new String("helloworld");对应的反编译代码
13: new #5; //class java/lang/String //new了一个String对象,并将其引用进栈, 16: dup 17: ldc #4; //String helloworld 19: invokespecial #6; //Method java/lang/String."<init>":(Ljava/lang/Strin g;)V 22: astore 5
执行过程中堆栈的变化
所谓的常量池就是在内存中的一个数组,这个数组中记录的都是直面量,并且在数组中,不会出现相同的直面量。