The Immutability of Strings – APCS A Deep Dive

Emphasizing the difference in how Java handles memory addressing for primitive vs. class types can really help students understand a lot of the tricky parts of how Java works. Should you use == or .equals()? How will parameters behave? What happens when we create an array of this type? Understanding this basic difference makes all of these areas clear.



String Art

One of the major advantages of Java for beginning coders is it is very consistent when it comes to the behavior of primitive vs. class types. Except of course when it isn’t.

The String class can cause a lot of confusion for students as they first start working with code. We often introduce it early in the year, indeed it is hard to do many meaningful things without it. The problem is, as an example of a class data type, it can behave in some unusual ways.

Let’s look at:

 String w1 = “dog”;
String w2 = “dog”;

if (w1 == w2)
System.out.println(“These objects are equal”);
System.out.println(“Except they shouldn’t be”);

Since the w1 and w2 are both object types, testing using == should test the primitive value – the memory address. These are two separate variables and in theory have two different memory addresses. So based on how Java tests Object equality they should not be equal.

Except, they are.

This has to do with the unique way Java handles Strings in memory. They are what is called an immutable data type. Immutable classes mean that once an object of that type is created it cannot be changed. But of course Strings in Java can change, so how does that work?

Looking at the documentation for the String class we see the following:

public final class String

Let’s back up a minute and think about constant variables – they hold values that cannot change, and are defined using the final keyword. Constants are, in fact, immutable. But what does it mean for the final keyword to be applied to a class?

This word final creates some unique behavior. For example, you cannot extend final classes, and trying to extend String throws the error:  cannot inherit from final java.lang.String.

This also impacts how the data in a String object is stored. According to the Java Documentation:

The String class is immutable, so that once it is created a String object cannot be changed. The String class has a number of methods, … that appear to modify strings. Since strings are immutable, what these methods really do is create and return a new string that contains the result of the operation.

When Java defines the w1 String above it stores the memory address just like any other object type. What is different is when it defines w2. Instead of creating a new address for w2, Java assigns w2 the same address as w1.

What? Confusing, right?

Java does this as a way of managing memory allocation. (more on that here)

By defining both variables to point at the same spot in memory it reduces the amount of memory Java is using.

This changes if we use the new keyword:

 String w1 = “dog”;
String w2 = new String (“dog”);

if (w1 == w2)
System.out.println(“These objects are equal”);
System.out.println(“Except they shouldn’t be”);

Now the two variables point to unique spots in memory and the boolean test returns a false.

Looking through the methods included in the String class hint at this immutability. None of the methods have the ability to change the current data stored by the String object. Several return a new String with the changed data, but unlike an array you cannot directly change the values stored.

Some other Java classes that are immutable include the wrapper classes – Integer, Double, etc. In fact, Java allows the user to create immutable classes from scratch when needed.

Want to know more:

Rebecca Dovi
As CEO (chief educational officer) of CodeVA Rebecca is responsible for training teachers and developing computer science curriculum as CodeVA works to bring computer science education to all Virginia students.