在之前的面试经历中,对于String的考察还是挺频繁的,大致考察以下几个知识点:
虽然面试中大体答对了,但是今天早上微信群里的一个问题我却答不上来,这个问题是这样的:
String str3 = "what";
String str4 = str3 + " a nice day";
//运行时, + 相当于 new,所以堆中会有 "what a nice day"对象,常量池中会有"what"," a nice day"两个对象,而不会有 "what a nice day"对象。
//这句话大佬们看看对不对啊,我怎么感觉不对啊
//常量池不会有"what a nice day" 对象吗?
public static void main(String[] args) {
//#1
String str1 = "what";
//#2
String str2 = str1 + " a nice day";
//#3
System.out.println("what a nice day".equals(str2));
//#4
System.out.println("what a nice day" == str2);
}
现在有以下几个问题,小伙伴们看看是否能答出来,即使答出来了,你知道为什么吗?
下面也不靠猜,我们直接查看生成的字节码:
localhost:test didi$ javap -verbose -p Main.class
Classfile /develop/project/string-test/out/production/classes/com/fanpan26/string/test/Main.class
Last modified --; size bytes
MD5 checksum d1f1a23bfe85c2f88d2f767e8aac314
Compiled from "Main.java"
public class com.fanpan26.string.test.Main
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #.# // java/lang/Object."<init>":()V
# = String # // what
# = Class # // java/lang/StringBuilder
# = Methodref #.# // java/lang/StringBuilder."<init>":()V
# = Methodref #.# // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
# = String # // a nice day
# = Methodref #.# // java/lang/StringBuilder.toString:()Ljava/lang/String;
# = Fieldref #.# // java/lang/System.out:Ljava/io/PrintStream;
# = String # // what a nice day
# = Methodref #.# // java/lang/String.equals:(Ljava/lang/Object;)Z
# = Methodref #.# // java/io/PrintStream.println:(Z)V
# = Class # // com/fanpan26/string/test/Main
# = Class # // java/lang/Object
# = Utf8 <init>
# = Utf8 ()V
# = Utf8 Code
# = Utf8 LineNumberTable
# = Utf8 LocalVariableTable
# = Utf8 this
# = Utf8 Lcom/fanpan26/string/test/Main;
# = Utf8 main
# = Utf8 ([Ljava/lang/String;)V
# = Utf8 args
# = Utf8 [Ljava/lang/String;
# = Utf8 str1
# = Utf8 Ljava/lang/String;
# = Utf8 str2
# = Utf8 StackMapTable
# = Class # // "[Ljava/lang/String;"
# = Class # // java/lang/String
# = Class # // java/io/PrintStream
# = Utf8 SourceFile
# = Utf8 Main.java
# = NameAndType #:# // "<init>":()V
# = Utf8 what
# = Utf8 java/lang/StringBuilder
# = NameAndType #:# // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
# = Utf8 a nice day
# = NameAndType #:# // toString:()Ljava/lang/String;
# = Class # // java/lang/System
# = NameAndType #:# // out:Ljava/io/PrintStream;
# = Utf8 what a nice day
# = Class # // java/lang/String
# = NameAndType #:# // equals:(Ljava/lang/Object;)Z
# = Class # // java/io/PrintStream
# = NameAndType #:# // println:(Z)V
# = Utf8 com/fanpan26/string/test/Main
# = Utf8 java/lang/Object
# = Utf8 java/lang/String
# = Utf8 java/io/PrintStream
# = Utf8 append
# = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
# = Utf8 toString
# = Utf8 ()Ljava/lang/String;
# = Utf8 java/lang/System
# = Utf8 out
# = Utf8 Ljava/io/PrintStream;
# = Utf8 equals
# = Utf8 (Ljava/lang/Object;)Z
# = Utf8 println
# = Utf8 (Z)V
{
public com.fanpan26.string.test.Main();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=, locals=, args_size=
: aload_0
: invokespecial # // Method java/lang/Object."<init>":()V
: return
LineNumberTable:
line :
LocalVariableTable:
Start Length Slot Name Signature
this Lcom/fanpan26/string/test/Main;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=, locals=, args_size=
: ldc # // String what
: astore_1
: new # // class java/lang/StringBuilder
: dup
: invokespecial # // Method java/lang/StringBuilder."<init>":()V
: aload_1
: invokevirtual # // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
: ldc # // String a nice day
: invokevirtual # // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
: invokevirtual # // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
: astore_2
: getstatic # // Field java/lang/System.out:Ljava/io/PrintStream;
: ldc # // String what a nice day
: aload_2
: invokevirtual # // Method java/lang/String.equals:(Ljava/lang/Object;)Z
: invokevirtual # // Method java/io/PrintStream.println:(Z)V
: getstatic # // Field java/lang/System.out:Ljava/io/PrintStream;
: ldc # // String what a nice day
: aload_2
: if_acmpne
: iconst_1
: goto
: iconst_0
: invokevirtual # // Method java/io/PrintStream.println:(Z)V
: return
LineNumberTable:
line :
line :
line :
line :
line :
LocalVariableTable:
Start Length Slot Name Signature
args [Ljava/lang/String;
str1 Ljava/lang/String;
str2 Ljava/lang/String;
StackMapTable: number_of_entries =
frame_type = /* full_frame */
offset_delta =
locals = [ class "[Ljava/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 ]
stack = [ class java/io/PrintStream, int ]
}
SourceFile: "Main.java"
从 Constant pool: 中的信息可以看到,#2 #6 #9 可以解答上文中的#1,#5两个问题。
下面我们看一下 + 操作做了什么事情,可以在Code中看到,该操作调用了 StringBuilder.append 方法
: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
: ldc #6 // String a nice day
: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
那么到这里一切都答案都出来了
所以说其实 str1 + " a nice day" 就相当于 new StringBuilder().append(str1).append(" a nice day");
//这两种写法生成的字节码是一样的。
//String str2 = str1 + " a nice day";
String str2 = new StringBuilder().append(str1).append(" a nice day").toString();
而StringBuilder 的toString 方法如下:
@Override
public String toString() {
// 所以说 str2 其实是一个 new String,是不在常量池里面的。
return new String(value, , count);
}
/**
* 以下程序输出的结果是什么?
* */
public static void main(String[] args) {
String str1 = "what";
String str2 = str1 + " a nice day";
System.out.println("what a nice day".equals(str2));
System.out.println("what a nice day" == str2);
}
/**
* 以下程序输出的结果是什么?
* */
public static void main(String[] args) {
String str1 = "what a nice day";
String str2 = new String("what a nice day");
System.out.println(str1.equals(str2));
System.out.println(str1 == str2);
}
/**
* 以下程序输出的结果是什么?
* */
public static void main(String[] args) {
String str1 = "what";
String str2 = str1.concat(" a nice day");
System.out.println("what a nice day".equals(str2));
System.out.println("what a nice day" == str2);
System.out.println("what a nice day"==str2.intern());
}