Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Learn › Java › Commonly used classes

String, StringBuilder

Why is the original unchanged after you call a string method?

A String is an object whose contents do not change once it has been created. Methods such as concat, replace, and toUpperCase do not modify the original. They create a new string and return it, so if you ignore the return value, it looks as if nothing happened. A StringBuilder, in contrast, modifies the contents of a single object directly. Look at the difference while using == to check whether two variables refer to the same object.

Checked against real execution · The 18 combinations were run on JDK 21 and checked against the results

Concepts in this lab

String immutability
This is the property that the characters inside a String object cannot be changed once the object has been created. Methods such as concat, replace, toUpperCase, and trim do not modify the original. They return a String that holds the result. So if you only call the method, as in s.toUpperCase(), and ignore the return value, s stays as it was, and you have to assign the return value back, as in s = s.toUpperCase(), to use the result. What changes in that case is not the string object but the object that the variable s refers to. Because the contents do not change, it is safe for several places to refer to the same string, and a String is well suited for use as a key in a HashMap.
Immutable object
This is an object whose state does not change after it is created. Because its value never changes, it is safe for several threads to use at the same time, you can pass it to other code without worrying that it will be changed, and it is well suited for caching or for use as a key in a hash collection. Declare the fields private final, provide no methods that change the state, and make the class final so that a subclass cannot change its behavior. If a field refers to a mutable object (such as a List or a Date), the constructor and the getters pass copies in and out (defensive copying) so that outside code cannot change the internals.
StringBuilder and StringBuffer
These are mutable string classes whose contents can be changed. String is immutable, so every concatenation creates a new object, but these two append to an internal buffer and so create fewer objects. StringBuffer has synchronized methods and is safe to use from several threads, while StringBuilder has no synchronization and is faster in a single thread.
Identity and equality
Identity means that two references point to the same object in memory, and you compare it with the == operator. Equality means that two objects logically have the same value, and you compare it with the equals() method. If you use == to compare strings or value objects, the result can be false even when the contents are the same.

Try these

Running with the default settings

  1. String s = "java": There is a string object with the contents "java", and the variable s refers to that object.
  2. String first = s: first is given the same reference that s holds. The contents are not copied. Instead, the two variables now refer to the same object. first is kept so that you can see later whether the first object changes.
  3. s.toUpperCase(): The original object "java" is left as it is, and a new string object "JAVA" is created and returned. But there is no variable to receive the return value. The new string is not kept in any variable.
  4. System.out.println(s): The contents of the object that s refers to are printed as one line: java. A method was called, but s still refers to the first object, so it looks as if nothing happened.
  5. System.out.println(first): The contents of the first object, which first refers to, are printed as one line: java. The contents of the first object did not change.
  6. s == first: The two variables refer to the same object, so the result is true.

Result of this run

Code

String s = "java";
String first = s;                 // remember the first object

s.toUpperCase();                 // the return value is ignored

System.out.println(s);
System.out.println(first);
System.out.println(s == first);   // same object?

Assumptions of this model

The output of the 18 combinations (10 distinct cases when the hidden setting is left out) was checked against the results of running the same code on JDK 21 on this PC. Saying that "a new object was created" means that a == comparison confirmed that the object is different from the previous one. How many objects are actually created in memory at run time can vary with the optimizations of the JVM, so no claim is made about it. The methods and values chosen here are all cases in which the contents change. For a call that does not change the contents (for example, replace when there is no character to replace), String sometimes returns itself without creating a new object. How a StringBuilder grows the internal space that holds its characters is not covered by this model.

Previous lab: equals and hashCode · Next lab: Dates and times

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these