74 Why Is String Designed to Be Immutable

74 Why Is String Designed to Be Immutable #

In this lesson, we will primarily discuss why String is designed to be immutable and the benefits of this design.

String is immutable #

Let’s first understand that “String is immutable”. In Java, a string is a constant, once we create a String object, we cannot change its value, and its content cannot be modified (excluding special behavior like reflection).

For example, let’s assign the string s the value “lagou”, and then try to assign it a new value, as shown in the code below:

String s = "lagou";

s = "la";

It may seem like we changed the value of the string, but behind the scenes, a new string “la” is actually created, and the reference of s is then pointed to this newly created string “la”, while the original string object “lagou” remains unchanged.

Similarly, if we call methods like subString() or replace() on a String and assign the reference of s to the newly created string, it does not modify the content of the original string object because these methods only create a new string. For example, consider the following code:

String lagou = "lagou";

lagou = lagou.subString(0, 4);

In the code, using lagou.subString(0, 4) will create a new string “lago” with these four letters, one less than the original, but this does not affect the original five-letter string “lagou”. In other words, in memory, both “lagou” and “lago” coexist as separate objects.

So what is the reason behind this? Let’s take a look at some important source code of the String class:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {

    /** The value is used for character storage. */
    private final char value[];

    //...
}

First, we can see that there is a very important attribute here, a private final char array named value. It stores each character of the string, and value array is also marked with final, which means once value is assigned, the reference cannot be modified. Additionally, in the source code of String, except for the constructor, there are no other methods that modify the content of the value array. Furthermore, the value’s access modifier is private, so external classes cannot access it, which ultimately makes value immutable.

Now, could there be a scenario where another class extends the String class and then overrides relevant methods to modify the value? This is a good question, but there is no need to worry about it because the String class is marked with final, which means it cannot be inherited. Therefore, no one can extend or override the behavior to break the immutability of the String class.

This is why String is immutable.

Benefits of String immutability #

Now let’s consider why the Java language designers chose to design String as immutable. Of course, we are not the designers of String and cannot know their true thoughts at the time. However, we can think about the benefits of designing String as immutable, and based on my analysis, there are primarily four advantages.

String Pool #

The first advantage of String immutability is the use of the String Pool. In Java, there is a concept of a string pool, where if the content of two string variables is the same, they will refer to the same object instead of creating a new object with the same content. For example:

String s1 = "lagou";
String s2 = "lagou";

Actually, both s1 and s2 point to the same “lagou” in the constant pool, as shown in the figure below:

并发.png

In the figure, we can see that both references on the left point to the same “lagou” in the constant pool. It is because of this mechanism, combined with the extensive use of String in programs, that we can save a lot of memory space.

If we want to take advantage of this feature of the constant pool, it requires String to have an immutable nature. Otherwise, there would be problems. Let’s look at the following example:

String s1 = "lagou";

String s2 = "lagou";

s1 = "LAGOU";

System.out.println(s2);

Let’s think about it. Suppose String objects are mutable, then after changing the object that s1 points to from the lowercase “lagou” to the uppercase “LAGOU”, s2 should also change. Then the value printed out should also be uppercase:

LAGOU

But this is not what we expect. Similarly, it would not be possible to achieve the function of string constant pool reuse. If the lowercase “lagou” object has been referenced by many variables, if any of these references changes the object value, the content pointed to by the other references should not be affected. In fact, due to the immutability of String, the program above will still print out lowercase “lagou”. Immutability ensures that different strings do not affect each other, as we expect.

Used as key for HashMap #

The second advantage of the immutability of String is that it can be conveniently used as a key for HashMap (or HashSet). It is generally recommended to use immutable objects as keys for HashMap, and String is suitable as a key for HashMap.

For a key, the most important requirement is that it is immutable, so that we can use it to retrieve the value stored in the HashMap. Since the working principle of HashMap is based on hashing, the object needs to always have the same hash value to work properly. If String is mutable, it would bring great risks because once the content of the String object changes, the hash code naturally should change as well. If we use this key to perform a lookup, we would not be able to find the previous value.

Caching HashCode #

The third advantage of the immutability of String is caching the HashCode.

In Java, we often use the HashCode of a string. In the String class, there is a hash attribute which caches the HashCode of the String. Here is the code:

/** Cache the hash code for the String */
private int hash;

This is a member variable that stores the HashCode of the String object. Because String is immutable, once the object is created, the value of the HashCode cannot change, so we can cache the HashCode. In this way, every time we want to use the HashCode, we do not need to recalculate it, but can directly return the cached value of the hash. This improves efficiency, making String very suitable as a key for HashMap.

For other ordinary class objects that do not have immutability, if we want to get their HashCode, we have to calculate it each time, which is less efficient.

Thread Safety #

The fourth advantage of the immutability of String is thread safety. Immutable objects are always thread-safe without any additional measures, naturally ensuring thread safety.

Because String is immutable, it can be safely shared by multiple threads, which is very important for multi-threaded programming, avoiding unnecessary synchronization operations.

Summary #

In this lesson, we first introduced that String is immutable, and then explained the benefits of immutability for String. These benefits are the ability to use string constant pool, suitability for being used as a key for HashMap, caching HashCode, and thread safety.