其他
运行时常量池的一道面试题(JDK8环境)
事先说明环境 在jdk8下,高版本的jdk可能找不到对应的Version类
public class TestDemo {
@Test
public void test01() {
//
String str1 = new StringBuilder("hello").append("World").toString();
System.out.println(str1.intern());
System.out.println(str1 == str1.intern());
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern());
System.out.println(str2 == str2.intern());
String str3 = new StringBuilder("hello").toString();
System.out.println(str3.intern());
System.out.println(str3 == str3.intern());
}
}
helloWorld
true
java
false
hello
false
public class GCTest {
public static void main(String[] args) {
String str1 = new StringBuilder("ja").append("va").toString();
String str2 = str1.intern();
System.out.println(str1==str2);
System.out.println(System.identityHashCode(str1)); // 通过System提供的方法得到hash值,打印hash值相当于内存地址,hashCode方法不完全等于
System.out.println(System.identityHashCode(str2)); // 打印地址
String str3 = new StringBuilder("hello").append("world").toString();
String str4 = str3.intern();
System.out.println(str3==str4);
System.out.println(System.identityHashCode(str3));
System.out.println(System.identityHashCode(str4));
}
}
false // str1和str2不是同一个对象
460141958 // 堆空间创建的那个对象 ”java“
1163157884 // 字符串常量池中的字符串对象
true // 说明str3和str4是同一个对象
1956725890 // 堆中创建的字符串对象 ”helloworld“
1956725890 // 堆中创建的字符串对象 ”helloworld“
helloWorld
true
字节码中分常量池、方法test01是我们重点关注的地方,首先注意到常量池中已经有了hello、World、ja、va字符串。
public class com.example.demo.test.TestDemo
minor version: 0
major version: 58
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #47 // com/example/demo/test/TestDemo
super_class: #2 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
#3 = NameAndType #5:#6 // "<init>":()V
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Class #8 // java/lang/StringBuilder
#8 = Utf8 java/lang/StringBuilder
#9 = String #10 // 常量"hello"字符串
#10 = Utf8 hello
#11 = Methodref #7.#12 // java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
#12 = NameAndType #5:#13 // "<init>":(Ljava/lang/String;)V
#13 = Utf8 (Ljava/lang/String;)V
#14 = String #15 // 常量"World"字符串
#15 = Utf8 World
#16 = Methodref #7.#17 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#17 = NameAndType #18:#19 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#18 = Utf8 append
#19 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#20 = Methodref #7.#21 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#21 = NameAndType #22:#23 // toString:()Ljava/lang/String;
#22 = Utf8 toString
#23 = Utf8 ()Ljava/lang/String;
#24 = Fieldref #25.#26 // java/lang/System.out:Ljava/io/PrintStream;
#25 = Class #27 // java/lang/System
#26 = NameAndType #28:#29 // out:Ljava/io/PrintStream;
#27 = Utf8 java/lang/System
#28 = Utf8 out
#29 = Utf8 Ljava/io/PrintStream;
#30 = Methodref #31.#32 // java/lang/String.intern:()Ljava/lang/String;
#31 = Class #33 // java/lang/String
#32 = NameAndType #34:#23 // intern:()Ljava/lang/String;
#33 = Utf8 java/lang/String
#34 = Utf8 intern
#35 = Methodref #36.#37 // java/io/PrintStream.println:(Ljava/lang/String;)V
#36 = Class #38 // java/io/PrintStream
#37 = NameAndType #39:#13 // println:(Ljava/lang/String;)V
#38 = Utf8 java/io/PrintStream
#39 = Utf8 println
#40 = Methodref #36.#41 // java/io/PrintStream.println:(Z)V
#41 = NameAndType #39:#42 // println:(Z)V
#42 = Utf8 (Z)V
#43 = String #44 // 常量"ja"字符串
#44 = Utf8 ja
#45 = String #46 // 常量"va"字符串
#46 = Utf8 va
#47 = Class #48 // com/example/demo/test/TestDemo
#48 = Utf8 com/example/demo/test/TestDemo
#49 = Utf8 Code
#50 = Utf8 LineNumberTable
#51 = Utf8 LocalVariableTable
#52 = Utf8 this
#53 = Utf8 Lcom/example/demo/test/TestDemo;
#54 = Utf8 test01
#55 = Utf8 str1
#56 = Utf8 Ljava/lang/String;
#57 = Utf8 str2
#58 = Utf8 str3
#59 = Utf8 StackMapTable
#60 = Utf8 RuntimeVisibleAnnotations
#61 = Utf8 Lorg/junit/Test;
#62 = Utf8 SourceFile
#63 = Utf8 TestDemo.java
{
public com.example.demo.test.TestDemo();
descriptor: ()V
flags: (0x0001) 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 7: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/example/demo/test/TestDemo;
public void test01();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=4, args_size=1
0: new #7 // 创建 java/lang/StringBuilder 对象
3: dup
4: ldc #9 // 入栈常量池(#9)中的字符串常量hello
6: invokespecial #11 // 实例初始化将hello传入
9: ldc #14 // 入栈常量池(在#14)中的字符串常量World
11: invokevirtual #16 // StringBuilder.append方法调用传入World
14: invokevirtual #20 // 调用StringBuilder.toString方法
17: astore_1 // 将返回的值的地址引用存入到局部变量1
18: getstatic #24 // 获取打印流
21: aload_1 // 将局部变量1装载成引用类型,也就是"helloWorld"
22: invokevirtual #30 // "helloWorld"调用String.intern:()方法
25: invokevirtual #35 // 调用打印流,打印"helloWorld"
28: getstatic #24 // 获取打印流
31: aload_1 // 加载局部变量1 也就是"helloWorld"
32: aload_1 // 加载局部变量1 "helloWorld"
33: invokevirtual #30 // "helloWorld"调用intern方法
36: if_acmpne 43 // 如果条件满足就转执行43 的iconst_0也就是将0入栈
39: iconst_1 // 将int类型常量值1压入栈
40: goto 44 // 无条件转移到44
43: iconst_0 // 将0入栈
44: invokevirtual #40 // 调用方法传入也就是“ja"(对应常量池的#44)
47: new #7 // 创建StringBuilder对象
50: dup
51: ldc #43 // 加载字符串"ja"
53: invokespecial #11 // 实例化StringBuilder
56: ldc #45 // 加载字符串"va"
58: invokevirtual #16 // 调用append方法
61: invokevirtual #20 // 调用toString方法
64: astore_2 // 将toString的结果存入局部变量2中
65: getstatic #24 // 获取打印流
68: aload_2 // 加载局部变量2的值也就是加载"java"
69: invokevirtual #30 // 调用intern方法
72: invokevirtual #35 // 打印"java"
75: getstatic #24 // 获取打印流
78: aload_2 // 加载局部变量2"java"
79: aload_2 // 加载局部变量2"java"
80: invokevirtual #30 // 调用intern方法
83: if_acmpne 90 // 如果条件成立跳转到90也就是将0入栈
86: iconst_1 // 将常量值1入栈
87: goto 91 // 无条件跳转到91
90: iconst_0 // 将常量值0入栈
91: invokevirtual #40 // 打印
94: new #7 // class java/lang/StringBuilder
97: dup
98: ldc #9 // String hello
100: invokespecial #11 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
103: invokevirtual #20 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
106: astore_3
107: getstatic #24 // Field java/lang/System.out:Ljava/io/PrintStream;
110: aload_3
111: invokevirtual #30 // Method java/lang/String.intern:()Ljava/lang/String;
114: invokevirtual #35 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
117: getstatic #24 // Field java/lang/System.out:Ljava/io/PrintStream;
120: aload_3
121: aload_3
122: invokevirtual #30 // Method java/lang/String.intern:()Ljava/lang/String;
125: if_acmpne 132
128: iconst_1
129: goto 133
132: iconst_0
133: invokevirtual #40 // Method java/io/PrintStream.println:(Z)V
136: return
LineNumberTable:
line 13: 0
line 14: 18
line 15: 28
line 17: 47
line 18: 65
line 19: 75
line 21: 94
line 22: 107
line 23: 117
line 24: 136
LocalVariableTable:
Start Length Slot Name Signature
0 137 0 this Lcom/example/demo/test/TestDemo;
18 119 1 str1 Ljava/lang/String;
65 72 2 str2 Ljava/lang/String;
107 30 3 str3 Ljava/lang/String;
StackMapTable: number_of_entries = 6
frame_type = 255 /* full_frame */
offset_delta = 43
locals = [ class com/example/demo/test/TestDemo, class java/lang/String ]
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ class com/example/demo/test/TestDemo, class java/lang/String ]
stack = [ class java/io/PrintStream, int ]
frame_type = 255 /* full_frame */
offset_delta = 45
locals = [ class com/example/demo/test/TestDemo, class java/lang/String, class java/lang/String ]
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ class com/example/demo/test/TestDemo, class java/lang/String, class java/lang/String ]
stack = [ class java/io/PrintStream, int ]
frame_type = 255 /* full_frame */
offset_delta = 40
locals = [ class com/example/demo/test/TestDemo, class java/lang/String, class java/lang/String, class java/lang/String ]
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ class com/example/demo/test/TestDemo, class java/lang/String, class java/lang/String, class java/lang/String ]
stack = [ class java/io/PrintStream, int ]
RuntimeVisibleAnnotations:
0: #61()
org.junit.Test
}
SourceFile: "TestDemo.java"
java
false
总结:这道题目的前面两问区别在于"java"是在程序运行时就已经在运行时常量了,而其它字符串则没有,因此出现不同的结果,同理类似于"java"这类字符串常量的应该还有一些在某些类中有定义。
hello
false
点个在看你最好看