Java developers are familiar with the
performance best practice of using a StringBuffer in a loop instead
of concatenating String objects. However, most developers have
never seen the difference in terms of bytecode for one approach vs.
the other. There is a tool included with the Java Development Kit
(JDK) called javap that can show you why you would want to adopt
this best practice.

Javap takes a class and dumps information about
its methods to standard out. It doesn’t decompile the code into
Java source code, but it will disassemble the bytecode into the
bytecode instructions defined by the Java Virtual Machine

Javap is useful when you want to see what your
compiler is doing for (or to) you, or when you want to see what
effect a code change will have on the compiled class file.

Let’s use the StringBuffer vs. String scenario
mentioned above as an example. Below is a contrived class that has
two methods that return a String consisting of the numbers 0 to n,
where n is supplied by the caller. The only difference between the
two methods is that one uses a String to build the result, and the
other uses a StringBuffer.

public class JavapTip {
    public static void main(String []args)

    private static String withStrings(int
count) {
        String s =
        for (int i = 0; i
< count; i++) {
+= i;

        return s;

    private static String withStringBuffer(int
count) {
        StringBuffer sb =
new StringBuffer();
        for (int i = 0; i
< count; i++) {



Now let’s take a look at what javap outputs
when it’s run against the class with the -c option. The -c option
tells javap to disassemble the bytecode found in the class.

Running it looks like this:

>javap -c JavapTip

The output of the command relative to this tip

Method java.lang.String withStrings(int)
   0 ldc #2 <String “”>
   2 astore_1
   3 iconst_0
   4 istore_2
   5 goto 30
   8 new #3 <Class
  11 dup
  12 invokespecial #4 <Method
  15 aload_1
  16 invokevirtual #5 <Method java.lang.StringBuffer
  19 iload_2
  20 invokevirtual #6 <Method java.lang.StringBuffer
  23 invokevirtual #7 <Method java.lang.String
  26 astore_1
  27 iinc 2 1
  30 iload_2
  31 iload_0
  32 if_icmplt 8
  35 aload_1
  36 areturn

Method java.lang.String withStringBuffer(int)
   0 new #3 <Class
   3 dup
   4 invokespecial #4 <Method
   7 astore_1
   8 iconst_0
   9 istore_2
  10 goto 22
  13 aload_1
  14 iload_2
  15 invokevirtual #6 <Method java.lang.StringBuffer
  18 pop
  19 iinc 2 1
  22 iload_2
  23 iload_0
  24 if_icmplt 13
  27 aload_1
  28 invokevirtual #7 <Method java.lang.String
  31 areturn

The output is a little cryptic if you’ve never
seen Java assembler before, but hopefully you can see that the
withString method creates a new StringBuffer instance each time
through the loop. Then, it appends the current value of the
existing String to the StringBuffer and appends the current value
of the loop. Finally, it calls toString on the buffer and assigns
the results to the existing String reference.

This is in contrast to the withStringBuffer
method, which only calls the existing StringBuffer’s append method
each time through the loop. There’s no object creation and no new
String references.

In this case, we already knew that using
StringBuffer instead of String was a good idea; if we didn’t, then
javap would have helped us find the answer.

You won’t often find yourself in circumstances
that require a Java disassembler, but when you do, it’s nice to
know that you already have one on your machine and that it’s simple
to use. If you’re interested, take a look at javap’s other
options—you might find features that will come in handy in your

Delivered each Thursday, our free Java newsletter provides insight and hands-on tips you need to unlock the full potential of this programming language. Automatically sign up today!