Let's consider a question about String in Java: the difference between "abc" + '/' and "abc" + "/". Through this example, we can practice the usage of Javap in JDK tools. The original question is as follows:
What is the difference between treating slashes/as characters or strings?
One is used as the basic data type char, and the other is the object String. What is the specific difference?
Will it be more efficient to treat it as a character?
String str = "abc" + '/';
and
String str = "abc" + "/";
1. Compiler optimization
First of all, you should know that the above two sentences have the same effect, because the compiler will optimize the above two sentences into the following:
String str = "abc/";
We can prove this through Javap. For Javap, we can refer to "5 JDK tools that every Java developer should know". We first create a class: StringOne, and fill in the following code in the main method:
StringOne.javaString str1 = "abc" + '/';String str2 = "abc" + "/";System.out.println(str1 == str2);
Compile and run, the output result is true. Next, our Javap is launched, enter the following command in the command line:
javap -v -l StringOne.class > StringOne.s
Then check the generated StringOne.s file. You will find that there are several lines in it
StringOne.s#2 = String #20 // abc/...#20 = Utf8 abc/...0: ldc #2 // String abc/2: store_13: ldc #2 // String abc/5: store_2
Explanation: str1 and str2 both refer to the string "abc/".
2. Use Javap to analyze differences
Now let’s change the question. What is the difference between stringAddString and stringAddChar methods in the following code?
StringTwopublic static String stringAddString(String str1, String str2){ return str1 + str2;}public static String stringAddChar(String str, char ch){ return str + ch;}This time, Javap is used for decompilation, and some of the contents of the generated file are as follows
StringTwo.spublic java.lang.String stringAddString(java.lang.String, java.lang.String); descriptor: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=3 0: new #2 // class java/lang/StringBuilder 3: dup 4: invokespecial #3 // Method java/lang/StringBuilder."< init>":()V 7: aload_1 8: invokevirtual #4 // Method java/lang/StringBuilder. append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 11: aload_2 12: invokevirtual #4 // Method java/lang/StringBuilder. append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15: invokevirtual #5 // Method java/lang/StringBuilder. toString:()Ljava/lang/String; 18: areturnpublic java.lang.String stringAddChar(java.lang.String, char); descriptor: (Ljava/lang/String;C)Ljava/lang/String; flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=3 0: new #2 // class java/lang/StringBuilder 3: dup 4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V 7: aload_1 8: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 11: iload_2 12: invokevirtual #6 // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder; 15: invokevirtual #5 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 18: areturn
Now, we can clearly see the process of executing these two methods:
stringAddString
The procedure of stringAddChar is the same as stringAddString, except that when the append method is called the second time, the parameter of stringAddString is of String type, while the parameter of stringAddChar is of char type.
3. Append(char) method and append(String) method of StringBuilder class
Here, we just need to check the source code directly (mine is the source code that comes with jdk1.8.0_60). Note that although the document shows that StringBuilder inherits from Object, from the source code, it is inherited from the abstract class AbstractStringBuilder. And the append method is implemented by AbstractStringBuilder.
AbstractStringBuilder.java
public AbstractStringBuilder append(char c) { ensureCapacityInternal(count + 1); // Ensure that the array can accommodate count+1 characters value[count++] = c; return this;}public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); // Copy the character array in the string into the character array of this object count += len; return this;}The rest will not be posted anymore. String.getChars(int, int, char[], int) ultimately depends on public static native void arraycopy(Object, int, Object, int, int). In other words, it may be written in C language, and the efficiency should be better when copying large arrays than programs written in Java. So, now let me tell you what I understand:
In terms of direct memory, since String contains char arrays, the array should have length fields. At the same time, the String class also has an int hash property, and the object itself will occupy additional memory to store other information, so the string will occupy more memory. However, if the string is very long, then these memory overheads can be almost ignored; and if the string is (very) short, then there are many shared references to share these memory overheads, so the excess memory overhead can still be ignored.
From the call stack, since String only has one or two more function calls than char here, if the function call overhead (including time and space) is not considered, it should be similar; if the function call overhead is considered, "abc" + '/' should be better; but when several characters need to be connected (I think this situation should be more common, right?), since using char requires many loops to complete the connection, the number of functions called will only be more called than using String. At the same time, copying will not be faster than String directly copying an array. So at this time, it becomes "abc" + "/" with a larger throughput.
Now I feel like this question is asking: Is it more efficient to use system calls when reading and writing files, or is it more efficient to use the IO library in the standard function library. I personally feel that although the standard IO library has to call system calls in the end, and some temporary variables and deeper call stacks will be generated, due to the buffering mechanisms of the IO library, the throughput of the IO library will be larger, and the real-time performance of system calls will be better. Similarly, although the String class will have more fields and a deeper function stack, the throughput should be better due to cache and more direct copying.
New questions
Judging from the above Javap decompilation code, when the two Strings are added together, they will become append strings in StringBuilder. So, in theory, which of the following code is more efficient?
String str1 = "abc" + "123"; // 1StringBuilder stringBuilder = new StringBuilder(); // 2stringBuilder.append("abc");stringBuilder.append("123");String str2 = stringBuilder.toString();Let this question be left for everyone to think about!
The above is all the content of this article, which helps you better distinguish String+String and String+char in Java. I hope it will be helpful to everyone's learning.