Interview question: why should String be modified with final? What are the benefits

1. First answer why

To ensure the immutability of String objects

2. How does final ensure the immutability of String objects?

To answer this question, you must first know the function of final:

  1. The class decorated by final cannot be inherited
  2. The reference of final modification cannot be re assigned after initialization
  3. The method of final modification cannot be overridden

Next, we need to know what the bottom layer of String is. In jdk8, the bottom layer of String is a char array. In the source code of String class, there is a member variable value, which is a char array reference, pointing to the char array storing the content of Sting object. The value reference uses the final modification:

The final reference modification is used so that the char array object pointed to by the value reference will not be changed, but this does not guarantee the immutability of the String object. Although the object remains unchanged, the elements of the array can still be changed. Changing the elements of the char array will also change the String object. We can test it with a piece of code

class Main{
    public static void main(String[] args) {
        //First declare a reference to the final modifier and initialize it
        final char[] value = {'1','2','3','4'};
        //Before change
        System.out.println(value[0]);
        //Change the character with subscript 0
        value[0] = 'a';
        //After change
        System.out.println(value[0]);
        System.out.println();

    }
}

The results are as follows:

You can see that although value is modified by final, the array elements can still be changed

Therefore, in order to ensure the immutability of the content of the String object, the designers of the String class avoid changing the value array elements when implementing various methods provided by String. For example, the following is the implementation source code of the substring method

 public String substring(int beginIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        int subLen = value.length - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        //If beginIndex is not equal to 0, a new object is created and returned
        return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
    }

At the end of the method, a new object return is created to avoid changing the value array of the original object. Let's look at the replace method

public String replace(char oldChar, char newChar) {
        if (oldChar != newChar) {
            int len = value.length;
            int i = -1;
            char[] val = value; /* avoid getfield opcode */

            while (++i < len) {
                if (val[i] == oldChar) {
                    break;
                }
            }
            if (i < len) {
                char buf[] = new char[len];
                for (int j = 0; j < i; j++) {
                    buf[j] = val[j];
                }
                while (i < len) {
                    char c = val[i];
                    buf[i] = (c == oldChar) ? newChar : c;
                    i++;
                }
                //If the original object needs to be changed, a new object is also created
                return new String(buf, true);
            }
        }
        return this;
    }

It can be seen that in order to keep the value array unchanged, String designers take great pains, but there is another risk, that is, inheritance

java supports inheritance. If I write a new class to inherit the String class and rewrite the underlying implementation of its various methods, won't the efforts of designers be in vain?

Therefore, in order to ensure that this happens, the String class is decorated with the final keyword, which can ensure that the String class is not inheritable, so as to ensure that its various methods will not be overridden

Here, we can answer why final can make the String object immutable. The modification of final makes the implementation logic of the String class unchanged, and the implementation logic ensures that the underlying value array of the String object does not change, thus realizing the immutability of the String object

Having said so much, what are the benefits of String immutability?

3. The advantage of designing string to be immutable

  1. increase of efficiency
    String class is often used as the key in the hash table and its hash value is often used. Based on the immutability of string, its hash value can be cached to reduce repeated operations. String class has a member variable hash, which caches the hash value

  2. Improve resource utilization
    String is the most commonly used object. In order to save memory, string constant pool can be realized based on the immutability of string.
    Before creating a String object, the jvm will first check whether the object exists in the String constant pool. If it exists, it will directly return its reference. Otherwise, it will create a new object and cache it into the constant pool, and then return the reference.
    The String constant pool avoids the creation of repeated String objects and saves memory resources. At the same time, it also improves the execution efficiency of the program by reducing the number of object creation

  3. Ensure thread safety
    String is immutable, so there is no need to worry that the string object will be changed by other threads, which is inherently thread safe

Keywords: Java

Added by daneth1712 on Sun, 06 Mar 2022 16:44:21 +0200