Fundamentally, it's not really a useful question (the book's, I mean, not yours) because it deals with the internal details of both the Java compiler and various JDK methods. But...
The book is probably referring to Java 8 or earlier (even though it's for Java 11 certification — my guess is they didn't update this example). In Java 8 and earlier, that code creates six strings (well, one of them — the one assigned to hello
at the outset — is created when the class is loaded, then five created dynamically). But it also creates and throws away StringBuilder
objects, one per loop iteration. Since there are five loop iterations, there are five StringBuilder
objects.
6 + 5 = 11. :-)
That's no longer true in Java 9 and above, thankfully. More on that below.
You can see the StringBuilder
s if you compile the class (with JDK 8 or earlier), then use javap -c StringCreations
to look at a rendering of the bytecode:
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String hello
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: iconst_5
7: if_icmpge 35
10: new #3 // class java/lang/StringBuilder
13: dup
14: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
17: aload_1
18: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: iload_2
22: invokevirtual #6 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
25: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
28: astore_1
29: iinc 2, 1
32: goto 5
35: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
38: aload_1
39: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
42: return
The loop is offset 5 through 32. At offset 14 you can see a StringBuilder
being created, then at offset 25 its toString
is called (creating a new string); the loop.
The first string isn't really created by that code, it's created by loading the class (and thus its constants pool), but the five in the loop are, and of course the five StringBuilder
s in the loop.
Compare that with the bytecode produced by Java 13:
public static void main(java.lang.String[]);
Code:
0: ldc #7 // String hello
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: iconst_5
7: if_icmpge 24
10: aload_1
11: iload_2
12: invokedynamic #9, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;I)Ljava/lang/String;
17: astore_1
18: iinc 2, 1
21: goto 5
24: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
27: aload_1
28: invokevirtual #19 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
The loop is offset 5 through offset 21, but there are no StringBuilder
s in sight anymore; instead, there's a call to makeConcatWithConstants
. So you end up with just the six strings (the one from the constants pool, then the five created dynamically via makeConcatWithConstants
).
As kaya3 points out in a comment, though, we don't know (in both cases) whether StringBuilder.append
or makeConcatWithConstants
converts i
to a string in its implementation before returning the new string. That would mean in Java 8 it would be 16 objects (11 strings and 5 StringBuilders
), and in Java 9+ 11 strings. But given that the point of makeConcatWithConstants
is to "...the creation of optimized String concatenation methods...", I think we can probably assume that it doesn't create a string for i
separately from creating the new string that will be its result. But really at this point we're well into the details of the Java compiler, the JVM and its JIT, etc.