最近小伙伴面试的时候经常被问到关于string的面试题,题目变化层出不穷。
这里我们来列举一道面试题来深入的解析一下。
下面来看面试题目:
String st1 = "a";
String st2 = "b";
String st3 = "a"+"b";
String st4 = "ab";
String st5 = new String("ab");
String st6 = new String("a");
System.out.println((st1+st2)==st3);
System.out.println((st1+st2)==st4);
System.out.println(st3==st4);
System.out.println(st3==st5);
System.out.println(st1==st6);
是不是很让人头疼,别着急,我们来慢慢的拨开面纱。
我们先执行一下看一下结果,再来慢慢的分析一下。
最后的结果为
1,false
2,false
3,true
4,false
5,false
是不是和自己分析的不一样,来我们用Java自带的命令来分析一下
我们到工程目录下面的classes文件下面找到编译后的class文件
在执行Javap -v xxxx.class
这里我来解释一下这个命令
-v 的意思就是打印详细的所以的附加信息,一些本地方法栈针的引用,出栈
入栈等信息。一些版本号啥的。
将其导入一个txt 文件便于观看。打开TXT文件
Classfile /home/pc/eclipse-workspace/springboot-captcha/target/classes/com/example/demo/HootlTest.class
Last modified 2020-5-30; size 1380 bytes
MD5 checksum 308e16797d8574803753206744655fec
Compiled from "HootlTest.java"
public class com.example.demo.HootlTest
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Class #2 // com/example/demo/HootlTest
#2 = Utf8 com/example/demo/HootlTest
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Methodref #3.#9 // java/lang/Object."<init>":()V
#9 = NameAndType #5:#6 // "<init>":()V
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/example/demo/HootlTest;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = String #17 // a
#17 = Utf8 a
#18 = String #19 // b
#19 = Utf8 b
#20 = String #21 // ab
#21 = Utf8 ab
#22 = Class #23 // java/lang/String
#23 = Utf8 java/lang/String
#24 = Methodref #22.#25 // java/lang/String."<init>":(Ljava/lang/String;)V
#25 = NameAndType #5:#26 // "<init>":(Ljava/lang/String;)V
#26 = Utf8 (Ljava/lang/String;)V
#27 = Fieldref #28.#30 // java/lang/System.out:Ljava/io/PrintStream;
#28 = Class #29 // java/lang/System
#29 = Utf8 java/lang/System
#30 = NameAndType #31:#32 // out:Ljava/io/PrintStream;
#31 = Utf8 out
#32 = Utf8 Ljava/io/PrintStream;
#33 = Class #34 // java/lang/StringBuilder
#34 = Utf8 java/lang/StringBuilder
#35 = Methodref #22.#36 // java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
#36 = NameAndType #37:#38 // valueOf:(Ljava/lang/Object;)Ljava/lang/String;
#37 = Utf8 valueOf
#38 = Utf8 (Ljava/lang/Object;)Ljava/lang/String;
#39 = Methodref #33.#25 // java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
#40 = Methodref #33.#41 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#41 = NameAndType #42:#43 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#42 = Utf8 append
#43 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#44 = Methodref #33.#45 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#45 = NameAndType #46:#47 // toString:()Ljava/lang/String;
#46 = Utf8 toString
#47 = Utf8 ()Ljava/lang/String;
#48 = Methodref #49.#51 // java/io/PrintStream.println:(Z)V
#49 = Class #50 // java/io/PrintStream
#50 = Utf8 java/io/PrintStream
#51 = NameAndType #52:#53 // println:(Z)V
#52 = Utf8 println
#53 = Utf8 (Z)V
#54 = Utf8 args
#55 = Utf8 [Ljava/lang/String;
#56 = Utf8 st1
#57 = Utf8 Ljava/lang/String;
#58 = Utf8 st2
#59 = Utf8 st3
#60 = Utf8 st4
#61 = Utf8 st5
#62 = Utf8 st6
#63 = Utf8 StackMapTable
#64 = Class #55 // "[Ljava/lang/String;"
#65 = Utf8 MethodParameters
#66 = Utf8 SourceFile
#67 = Utf8 HootlTest.java
{
public com.example.demo.HootlTest();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/example/demo/HootlTest;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=7, args_size=1
0: ldc #16 // String a
2: astore_1
3: ldc #18 // String b
5: astore_2
6: ldc #20 // String ab
8: astore_3
9: ldc #20 // String ab
11: astore 4
13: new #22 // class java/lang/String
16: dup
17: ldc #20 // String ab
19: invokespecial #24 // Method java/lang/String."<init>":(Ljava/lang/String;)V
22: astore 5
24: new #22 // class java/lang/String
27: dup
28: ldc #16 // String a
30: invokespecial #24 // Method java/lang/String."<init>":(Ljava/lang/String;)V
33: astore 6
35: getstatic #27 // Field java/lang/System.out:Ljava/io/PrintStream;
38: new #33 // class java/lang/StringBuilder
41: dup
42: aload_1
43: invokestatic #35 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
46: invokespecial #39 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
49: aload_2
50: invokevirtual #40 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
53: invokevirtual #44 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
56: aload_3
57: if_acmpne 64
60: iconst_1
61: goto 65
64: iconst_0
65: invokevirtual #48 // Method java/io/PrintStream.println:(Z)V
68: getstatic #27 // Field java/lang/System.out:Ljava/io/PrintStream;
71: new #33 // class java/lang/StringBuilder
74: dup
75: aload_1
76: invokestatic #35 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
79: invokespecial #39 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
82: aload_2
83: invokevirtual #40 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
86: invokevirtual #44 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
89: aload 4
91: if_acmpne 98
94: iconst_1
95: goto 99
98: iconst_0
99: invokevirtual #48 // Method java/io/PrintStream.println:(Z)V
102: getstatic #27 // Field java/lang/System.out:Ljava/io/PrintStream;
105: aload_3
106: aload 4
108: if_acmpne 115
111: iconst_1
112: goto 116
115: iconst_0
116: invokevirtual #48 // Method java/io/PrintStream.println:(Z)V
119: getstatic #27 // Field java/lang/System.out:Ljava/io/PrintStream;
122: aload_3
123: aload 5
125: if_acmpne 132
128: iconst_1
129: goto 133
132: iconst_0
133: invokevirtual #48 // Method java/io/PrintStream.println:(Z)V
136: getstatic #27 // Field java/lang/System.out:Ljava/io/PrintStream;
139: aload_1
140: aload 6
142: if_acmpne 149
145: iconst_1
146: goto 150
149: iconst_0
150: invokevirtual #48 // Method java/io/PrintStream.println:(Z)V
153: return
LineNumberTable:
line 7: 0
line 8: 3
line 9: 6
line 10: 9
line 11: 13
line 12: 24
line 13: 35
line 14: 68
line 15: 102
line 16: 119
line 17: 136
line 18: 153
LocalVariableTable:
Start Length Slot Name Signature
0 154 0 args [Ljava/lang/String;
3 151 1 st1 Ljava/lang/String;
6 148 2 st2 Ljava/lang/String;
9 145 3 st3 Ljava/lang/String;
13 141 4 st4 Ljava/lang/String;
24 130 5 st5 Ljava/lang/String;
35 119 6 st6 Ljava/lang/String;
StackMapTable: number_of_entries = 10
frame_type = 255 /* full_frame */
offset_delta = 64
locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, 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 "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
stack = [ class java/io/PrintStream, int ]
frame_type = 96 /* same_locals_1_stack_item */
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
stack = [ class java/io/PrintStream, int ]
frame_type = 79 /* same_locals_1_stack_item */
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
stack = [ class java/io/PrintStream, int ]
frame_type = 79 /* same_locals_1_stack_item */
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
stack = [ class java/io/PrintStream, int ]
frame_type = 79 /* same_locals_1_stack_item */
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
stack = [ class java/io/PrintStream, int ]
MethodParameters:
Name Flags
args
}
SourceFile: "HootlTest.java"
是不是看不懂没关系,我也看不懂。并且这种命令行模式的更加的不方便,这里我使用的是第三方的开源工具jclasslib,官方链接 https://bintray.com/ingokegel/generic/jclasslib/view 我下载的是Linux 版本的,大家可以根据自己的系统版本来下载对呀的版本,安装就是傻瓜式的安装。我们打开刚才的那个class文件。
我们先来看第一个信息,版本号就不说,jdk1,8 的,常量池有68个。访问修饰符是public 的,父类是object的。接口没有,属性没有,方法两个(这里有一个疑问,方法为啥是两个啊,只看见一个一个main 方法啊),我们点进去方法看一下
this 当前对象,默认的无参构造器,在本地方法栈,并且还是load_0 ,先入栈的。也就是类初始化的时候,是先加载无参构造器的。
常量池先不讲,太多了。接口和属性也没有,直接跳过。我们开始讲方法一。
方法名称main 方法, 返回类型string , 访问修饰符public static 的。那么有同学问了那上面的cp 是啥啊,cp 就是 constant pool 的简写。我们根据提示来找第十四个和第十五个。
没什么东西,但是这给了我们提示。下面的东西我们就会自己查找自己看了。
这里我们看一下前三个的结果,false ,false,true
看一下左边 st1 在常量池16 号,st2 在常量池 18 号 st3 在20 号,st4 也在20号,所以 (st1+st2) ==st3 结果为false。(st1+st2) ==st4 结果为false。st3==st4 为true .再看下面两个为啥为false
我们看一下st5 ,有重新 new 了一下string 但是引用的内存地址变了,虽然指向的常量池都是20,同理st6 也是一样的。再往下看着一断,system.out 的时候 st1+st2的底层是stringbuilder.append() 也就是说system.out.print() 也不是线程安全的,打印顺序 有可能不一致。
剩下的也没啥东西,大家要掌握学习的方法,这样才能举一反三。提示一下integer 的自动拆箱装箱 这也能看见啊。