I was busy with the logical implementation of the project on weekdays. I had some time on Saturday, so I took out a thick English version of Thinking In Java from the bookcase and read the splicing of string objects. Refer to this book as a translation, add what you think about, and write this article to record it.
Immutable String Object
In Java, String objects are immutable (Immutable). In the code, you can create multiple alias for a String object. But the references of these aliases are the same.
For example, s1 and s2 are alias for "droidyue.com" objects, and the alias are stored in references to the real object. So s1 = s2
String s1 = "droidyue.com";String s2 = s1;System.out.println("s1 and s2 has the same reference =" + (s1 == s2));The only overloaded operator in Java
In Java, the only overloaded operator is related to string splicing. +,+=. Besides that, Java designers do not allow overloading other operators.
Splicing analysis
Is there really a performance cost?
After understanding the above two points, you may have such thoughts. Since the Sting object is immutable, the splicing of multiple (three or more) strings will inevitably produce unnecessary intermediate String objects.
String userName = "Andy";String age = "24";String job = "Developer";String info = userName + age + job;
To get the above info, userName and age will be spliced to generate a temporary String object t1, with the content of Andy24, and then t1 and jobs will be spliced to generate the info object we need in the end. Among them, an intermediate t1 is generated, and after t1 is created, it will not be recycled automatically, which will inevitably occupy a certain amount of space. If it is a splicing of a lot (assuming hundreds, more common in the toString call to the object) string, then the cost will be greater and the performance will be much lower.
Compiler optimization processing
Is there really the above performance cost? String splicing is so common, is there no special processing optimization? The answer is that this optimization is performed when the compiler compiles .java to bytecode.
If a Java program wants to run, it will take two periods, compile time and run time. At compile time, the Java compiler (Compiler) converts the java file into bytecode. At runtime, the Java virtual machine (JVM) runs the compile-time generated bytecode. Through these two periods, Java has achieved the so-called compilation and running everywhere.
Let's experiment with what optimizations were made during the compilation period, and we create a piece of code that may have performance costs.
public class Concatenation { public static void main(String[] args) { String userName = "Andy"; String age = "24"; String job = "Developer"; String info = userName + age + job; System.out.println(info); }}Compile Concatenation.java. Get Concatenation.class
javac Concatenation.java
Then we use javap to decompile the compiled Concatenation.class file. javap -c Concatenation. If the javap command is not found, please consider adding the directory where the javap is located to the environment variable or using the full path of javap.
17:22:04-androidyue~/workspace_adt/strings/src$ javap -c ConcatenationCompiled from "Concatenation.java"public class Concatenation { public Concatenation(); 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 Andy 2: store_1 3: ldc #3 // String 24 5: store_2 6: ldc #4 // String Developer 8: store_3 9: new #5 // class java/lang/StringBuilder 12: dup 13: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V 16: aload_1 17: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 20: aload_2 21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: aload_3 25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 28: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 31: store 4 33: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream; 36: aload 4 38: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 41: return}Among them, ldc, store, etc. are java bytecode instructions, similar to assembly instructions. The following comments use Java-related content for explanation. We can see that there are many StringBuilders on it, but we call them without display in the Java code. This is the optimization made by the Java compiler. When the Java compiler encounters string splicing, it will create a StringBuilder object. The splicing behind it is actually calling the append method of the StringBuilder object. In this way, there will be no problems we are worried about.
Compiler optimization alone?
Since the compiler has helped us optimize, is it enough to rely solely on the compiler's optimization? Of course not.
Let's look at a piece of code that has not been optimized for low performance
public void implicitUseStringBuilder(String[] values) { String result = ""; for (int i = 0 ; i < values.length; i ++) { result += values[i]; } System.out.println(result);}Compile with javac and view with javap
11: new #5 // class java/lang/StringBuilder 14: dup 15: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V 18: aload_2 19: invokevirtual #7 // Method 31: store_2 32: iinc 3, 1 35: goto 5 38: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream; 41: aload_2 42: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 45: return
Among them 8: if_icmpge 38 and 35: goto 5 form a loop. 8: if_icmpge 38 means that if the integer comparison of the JVM operand stack is greater than or equal to (the opposite result of i < values.length) is true, then jump to line 38 (System.out). 35: goto 5 means jumping directly to line 5.
But there is a very important thing here that StringBuilder object creation occurs between loops, which means that how many times the loops will create StringBuilder objects, which is obviously not good. Naked low-level code.
Optimize it slightly to instantly improve the quality.
public void explicitUseStringBuilder(String[] values) { StringBuilder result = new StringBuilder(); for (int i = 0; i < values.length; i ++) { result.append(values[i]); }}Corresponding compiled information
public void explicitUseStringBuider(java.lang.String[]); Code: 0: new #5 // class java/lang/StringBuilder 3: dup 4: invokespecial #6 // 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 #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 23: pop 24: iinc 3, 1 27: goto 10 30: return
As can be seen from above, 13: if_icmpge 30 and 27: goto 10 form a loop loop, while 0: new #5 is outside the loop, so StringBuilder is not created multiple times.
In general, we need to try to avoid implicit or explicit creation of StringBuilders in the loop body. Therefore, those who understand how the code is compiled and how it is executed internally have relatively high code levels.
If there are any errors in the above article, please criticize and correct them.
The above is to sort out the information about splicing Java strings, and we will continue to add relevant information in the future. Thank you for your support for this website!