Is that a clickbait? Doesn’t count.

This article looks at one of the most recently discovered boring oddities – string links

How to concatenate strings

We all know that there are several ways to link strings in the Java world:

  1. +++

We can concatenate strings with the plus sign to form a new string.

  1. StringBuilder

We can also do this by creating the StringBuilder class and then calling the Append method.

  1. StringBuffer

Thread-safe string concatenation method, similar to StringBuilder.

  1. Has a novelty technology

Some little known tricks.

String concatenation problem

As we all know, strings are immutable in the Java world.

Check it out at the Nuggets if you don’t know. This article will not elaborate.

So the Java compiler is secretly doing something for you when you’re doing string concatenation.

g.

As we all know, StringBuilder is a class that buffers strings, so it can concatenate strings gracefully without wasting memory by creating lots of strings.

The JVM secretly does this for you by replacing the concatenation of strings with a StringBuilder. As shown in the following code:

public class SimpleStringConnect {
    public static void main(String[] args) {
        String a = "abcdefg";
        String b = "qweqwfqwf"; System.out.println(a + b); }}Copy the code

This is a simple string concatenation, so let’s see what the compiler does for us. Here is the bytecode file for this code:

/ / class version 52.0 (52) / / access flags 0 x21 public class com/lot/mattison/SimpleStringConnect {/ / compiled from:  SimpleStringConnect.java // access flags 0x1 public <init>()V L0 LINENUMBER 3 L0 ALOAD 0 INVOKESPECIAL java/lang/Object.<init> ()V RETURN L1 LOCALVARIABLE this Lcom/github/mattison/SimpleStringConnect; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1 // access flags 0x9 public static main([Ljava/lang/String;)V L0 LINENUMBER 5 L0 LDC "abcdefg" ASTORE 1 L1 // Save the second string. LINENUMBER 6 L1 LDC "qweqwfqwf" ASTORE 2 L2 LINENUMBER 7 L2 GETSTATIC java/lang/System.out : Ljava/ IO /PrintStream; NEW Java /lang/StringBuilder // Create StringBuilder DUP INVOKESPECIAL Java /lang/StringBuilder.<init> () 1 V ALOAD INVOKEVIRTUAL Java/lang/StringBuilder append (Ljava/lang/String;) Ljava/lang/StringBuilder; / / calling append Methods to connect the first String ALOAD 2 INVOKEVIRTUAL Java/lang/StringBuilder. Append (Ljava/lang/String;) Ljava/lang/StringBuilder; / / calls Append method to connect the second String INVOKEVIRTUAL Java/lang/StringBuilder toString () Ljava/lang/String; INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V L3 LINENUMBER 8 L3 RETURN L4 LOCALVARIABLE args [Ljava/lang/String; L0  L4 0 LOCALVARIABLE a Ljava/lang/String; L1 L4 1 LOCALVARIABLE b Ljava/lang/String; L2 L4 2 MAXSTACK = 3 MAXLOCALS = 3 }Copy the code

The comments in the code explain that the compiled bytecode becomes creating the StringBuilder class and calling the append method instead of creating links to multiple String classes that implement strings.

Existing problems and how to solve them

The problem

However, the compiler is not a human being, and when we concatenate strings in loops, it has another problem. The code is as follows:

public class LoopStringConnect {
    public static void main(String[] args) {
        String a = "a";
        for (int i = 0; i < 24; i++) { a += i; }}}Copy the code

The bytecode is as follows:

// class version 52.0 (52)
// access flags 0x21
public class com/github/mattison/LoopStringConnect {

  // compiled from: LoopStringConnect.java

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 3 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/github/mattison/LoopStringConnect; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x9
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 5 L0
    LDC "a"
    ASTORE 1
   L1
    LINENUMBER 6 L1
    ICONST_0
    ISTORE 2
   L2
   FRAME APPEND [java/lang/String I]
    ILOAD 2
    BIPUSH 24
    IF_ICMPGE L3
   L4
    LINENUMBER 7 L4
    NEW java/lang/StringBuilder  / / create the StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> (a)V
    ALOAD 1
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;  // String before concatenation
    ILOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String; // store the StringBuilder as a string
    ASTORE 1
   L5
    LINENUMBER 6 L5
    IINC 2 1
    GOTO L2 // Continue the loop
   L3
    LINENUMBER 9 L3
   FRAME CHOP 1
    RETURN
   L6
    LOCALVARIABLE i I L2 L3 2
    LOCALVARIABLE args [Ljava/lang/String; L0 L6 0
    LOCALVARIABLE a Ljava/lang/String; L1 L6 1
    MAXSTACK = 2
    MAXLOCALS = 3
}
Copy the code

Reading the bytecode, we see that although the JVM does not create the String class, it is compiled by the compiler. Each loop creates a StringBuilder class. The memory waste caused by the repeated creation of previous classes still exists.

To solve

If you are using IntelliJ IDEA, it will prompt you to use StringBuilder instead.

The modified code is as follows:

public class LoopStringConnect {
    public static void main(String[] args) {
        StringBuilder a = new StringBuilder("a");
        for (int i = 0; i < 24; i++) { a.append(i); }}}Copy the code

Its bytecode is as follows:

// class version 52.0 (52)
// access flags 0x21
public class com/github/mattison/LoopStringBuilderConnect {

  // compiled from: LoopStringBuilderConnect.java

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 3 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/github/mattison/LoopStringBuilderConnect; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x9
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 5 L0
    NEW java/lang/StringBuilder  / / create the StringBuilder
    DUP
    LDC "a"
    INVOKESPECIAL java/lang/StringBuilder.<init> (Ljava/lang/String;)V
    ASTORE 1
   L1
    LINENUMBER 6 L1
    ICONST_0
    ISTORE 2
   L2
   FRAME APPEND [java/lang/StringBuilder I]
    ILOAD 2
    BIPUSH 24
    IF_ICMPGE L3
   L4
    LINENUMBER 7 L4
    ALOAD 1
    ILOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder; // Call the append method
    POP
   L5
    LINENUMBER 6 L5
    IINC 2 1
    GOTO L2
   L3
    LINENUMBER 9 L3
   FRAME CHOP 1
    RETURN
   L6
    LOCALVARIABLE i I L2 L3 2
    LOCALVARIABLE args [Ljava/lang/String; L0 L6 0
    LOCALVARIABLE a Ljava/lang/StringBuilder; L1 L6 1
    MAXSTACK = 3
    MAXLOCALS = 3
}
Copy the code

Subsequent optimization

I already know all this stuff. Did I see a lonely? – net friend XXXX

No, no, no, no, no, no, no, no, no.

Although we can rely on StringBuilder to solve the problem of string loop concatenation. But compiler writers have “good for humanity” in mind. Eventually when j9 StringConcatFactory InvokeDynamic calls. MakeConcatWithConstants method to optimize string concatenation.

Specific principle can refer to the end of some good reference materials.

conclusion

Boring strange knowledge.

A link to the

Code base for this article

JAVA9 String new feature