Foreword
The interview may often ask this question: Tell me about the difference between String, StringBuilder, StringBuffer, and many people may say that if the String by +
the time (which is a good plus, followed by the same token) to concatenate strings, creates a lot of temporary variable, performance is relatively low (so many online posts also wrote), but really so?
String splicing
In fact, in order to know String through +
time to splice the string, in the end there is no create temporary variables? In fact, this problem is very simple, just need to javap
decompile the generated class file to see if the class file String operations done on it. Here we are in the "java programming ideas" string section an example to explain.
First we look at the following code:
public class Test {
public static void main(String[] args){
String mango = "mango";
String s = "abc" + mango + "def" + 47;
System.out.println(s);
}
}
复制代码
This code is more typical through +
to the code string concatenation, by then we javac Test.java
compile this code, then javap -c Test.class
decompile the generated Test.class
files. Weed out unrelated part, it shows the main()
code bytecode, so with the following bytecode. You will find very interesting thing happened.
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: ldc #2 // String mango
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: ldc #5 // String abc
12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_1
16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: ldc #7 // String def
21: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: bipush 47
26: invokevirtual #8 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
29: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
32: astore_2
33: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
36: aload_2
37: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: return
}
复制代码
Here it comes to assembly language, readers can search online bytecode instruction list there will be many, I offer here a reader can look at the table to understand the meaning of each instruction, I am not here to expand in detail. After each instruction may be //
, //
the contents of the instruction code representing the objects behind operation. Careful readers will find: the compiler automatically introduces java.lang.StringBuilder (where the front of java L
is a reference type, you want to learn more about the reader can look at "in-depth understanding of the Java Virtual Machine" in the class file structure chapter) . Although we did not use StringBuilder in the source code, but the compiler has given a free hand to use it, because it is more efficient.
After watching the above bytecode you will find that the compiler creates a StringBuilder object, +
each connected to a string using a number of append()
methods to splice, a total of four calls, last call toString () method to generate results. (Note: if the interested reader can be used to replace the above code StringBuilder by javac
javap
recompiling, then you will find main()
generated in the process is the same bytecode).
in conclusion
By the above example we found that when we pass +
a string to splice, the compiler will automatically optimize for us to splice into the StringBuilder, and will not cause the Internet said the creation of temporary variables, slow down these shortcomings. (Note: Since StringBuidler is introduced after jdk5.0, jdk5.0 before it is spliced to StringBuffer by the interested reader can then authenticate itself).
extend
Now, we will certainly be very happy, since compilers are optimized for us, that we are not free to use String
of it (think of all happy). Haha, do not get too excited, because sometimes optimizing compiler may not be the results you want. Let's look at the following code:
The following program to generate a String in two ways: using a plurality of methods String object; two code method used StringBuidler.
public class Test {
public String testString(String[] fields) {
String result = "";
for (int i = 0; i < fields.length; i++) {
result += fields[i];
}
return result;
}
public String testStringBuilder(String[] fields){
StringBuilder result = new StringBuilder();
for (int i = 0; i<fields.length; i++){
result.append(fields[i]);
}
return result.toString();
}
}
复制代码
The above two methods perform similar code, are passed string array, then for
loop stitching together the array of strings, a first difference method is used String
to splice the second method used StringBuilder
to splice. Then we pass javap
to decompile the code, eliminate independent part, will see the bytecode two methods.
First, testString()
the bytecode method:
public java.lang.String testString(java.lang.String[]);
descriptor: ([Ljava/lang/String;)Ljava/lang/String;
flags: ACC_PUBLIC
Code:
stack=3, locals=4, args_size=2
0: ldc #2 // String
2: astore_2
3: iconst_0
4: istore_3
5: iload_3
6: aload_1
7: arraylength
8: if_icmpge 38
11: new #3 // class java/lang/StringBuilder
14: dup
15: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
18: aload_2
19: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: aload_1
23: iload_3
24: aaload
25: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: astore_2
32: iinc 3, 1
35: goto 5
38: aload_2
39: areturn
复制代码
Here readers look at the line 8 if_icmpge
, the control table is found bytecode instructions for the instruction loop is to compare i
the value equal to a certain time into the circulation, back out of the loop 38 represents the line 38, where the first loop 8
row to 35
row. It means line 35: return loop start point (first 5
row). Then we see (the first loop 8
line to the first 35
line 11 lines), is a new
command, this too familiar, is to create objects. But it is actually inside the loop, which means that each time through the loop, it is necessary to create a new StringBuilder
object. This is clearly unacceptable.
Then we look at what testStringBuilder()
bytecode:
public java.lang.String testStringBuilder(java.lang.String[]);
descriptor: ([Ljava/lang/String;)Ljava/lang/String;
flags: ACC_PUBLIC
Code:
stack=3, locals=4, args_size=2
0: new #3 // class java/lang/StringBuilder
3: dup
4: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
7: astore_2
8: iconst_0
9: istore_3
10: iload_3
11: aload_1
12: arraylength
13: if_icmpge 30
16: aload_2
17: aload_1
18: iload_3
19: aaload
20: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: pop
24: iinc 3, 1
27: goto 10
30: aload_2
31: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
34: areturn
复制代码
We can see that the code is not only part of the body of the loop shorter, simpler, and it new
is only the beginning of a call, indicating only generated a StringBuilder object.
Conclusions extension
So, when you write a class toString()
when the method, if the string operation is relatively simple, it can rely on the compiler, it would be reasonable final result string configuration for you. But if you want to toString()
use cyclic process, then you need to create a StringBuilder object. Of course, when you can not decide, then you can always analyze your program by javap.
Remain a problem: enumeration we should all know, but you know in the end is how it performed in the jvm it? (Thinking: in fact, want to know how it works is very simple, you can also write a enumeration code, and then by javap
anti-compile the code, you will have a clear insight into the feeling.)
references
- "java programming ideas"