Immutability Of String

Java defines some objects as immutable objects; developers cannot modify those objects once they are created. String objects and wrapper class objects are the best examples for immutable objects. This article covers the immutable objects and the reason for having immutable objects using String as an example.

Before getting into the actual content, it is required to have a basic knowledge about the equality operator (==). Usually equality operator is used to compare two primitive data types, but it has a different meaning when it is used with references. To compare the content of two objects, equals method is used and the equality operator is used to check whether two references are referring same object.
public class EqualityOperator {
    public static void main(String[] args) {
        Student stu1 = new Student(100);
        Student stu2 = new Student(100);
        Student stu3 = stu1;
        if (stu1 == stu2) { // false
            System.out.println("stu1 == stu2");
        }
        if (stu1.equals(stu2)) { // true
            System.out.println("stu1 equals stu2");
        }
        if (stu1 == stu3) { // true
            System.out.println("stu1 == stu3");
        }
    }
}
For the complete project click on this link.

In the above example stu1 == stu2 returns false, because they refer two different objects. stu1 == stu3 returns true because both references are referring the same object.

In Java, String is one of the commonly used data type other than the eight primitive data types. However, compared to primitive data types, String consumes more memory since it is an object. Java engineers use a technique of reusing, to create String objects efficiently in the heap. To create String objects Java uses a memory location heap (which is common for all objects) and additionally it has a special memory location called String pool. String pool is used to store internal references to String objects. When a new String object is created, Java checks the String pool for that object. If String pool has a reference to that object already, then Java uses the same object instead of creating a new object. If there is no reference for that object, Java creates a new String object in the heap and add a reference to it from the String pool.


Let's see what happens, when Java executes the following code.
public class StringPool {
    public static void main(String[] args) {
        String x = "Java";
        String y = "Java";

        System.out.println(x == y); //true
        System.out.println(x.toLowerCase());    // java
        System.out.println(x);  // Java
        System.out.println(y);  // Java
        x = x.toLowerCase();    // Update the reference
        System.out.println(x);  // java
        System.out.println(y);  // Java
    }
}
Line 3:


When an object is created using String x = “Java”, Java checks the String pool for any reference for "Java" object. Since this is the first time, there is no reference for an existing "Java" object in the String pool. Therefore Java just creates a new “Java” object in the heap and add the reference “x” to the object. In addition to the reference “x” which is created by us, an invisible reference is added to the object from the String pool as shown here.


After executing this line, there is a single object with two references.

Line 4:
Next line is String y = “Java”;
Again Java checks the String pool, but this time there is a reference for an existing “Java” object. To save the memory, Java does not create a new object; instead Java creates a new reference “y” to the same object.


Line 6:
Then the references “x” and “y” are compared using == operator. Since both references are referring the same object, the output must be true.

Line 7:
Now you might have a question. What will happen, if we change the object using the reference “x”? Even though Java keeps the same object to serve those two references, developers do not care it. From developers' perspective what they need is two separate Strings. Even if one object is modified, other object should remain unchanged. In Java, toLowerCase method is used to convert all the characters of a String to simple letters. What if the toLowerCase method modify the object as shown here?


If there is a way to modify a String object, then the other references which are referring the same object also will get affected by the modification. Therefore, Java does not allow modifying any String objects once they have been created. The objects which cannot be modified once created are called as immutable objects.


Actually, the toLowerCase method creates a new object and return the reference. In this code that reference is used to print the object, so the output is “java”. Notice that still both “x” and “y” are referring the same object. Therefore, printing “x” and “y”, produces the same output “Java”.

Line 10:
In this line, the reference “x” is updated to the returned reference of toLowerCase method. So in the RAM, “x” refers the new “java” object and y refers the same old “Java” object. The next two lines print “java” and “Java”, because of the updated reference.


Even though immutability reduces the memory usage and object creation, there is a disadvantage of having immutable objects. Every modification made on a String object cause to new String object. It increases the memory usage and introduces new garbage objects. Therefore, modifying a String object several times is a bad practice in Java. Consider an example as shown below.
public class GarbageGenerator {
    public static void main(String[] args) {
        String result = "";

        for (int i = 0; i < 10; i++) {
            // Every time creates a new object
            result += i;
        }
        System.out.println(result);
    }
}
In this code String object is modified in a loop ten times. If the object is modified ten times, it means ten new objects were created in background. It increases the load of Garbage Collector and invisibly reduces the performance of the application. Best-practice on modifying text content is using either StringBuffer or StringBuilder as shown below. StringBuffer/StringBuilder objects are mutable (changeable) objects. So all the modifications are applied to the same object.
public class BestPractice {
    public static void main(String[] args) {
        StringBuilder builder = new StringBuilder();

        for (int i = 0; i < 10; i++) {
            builder.append(i);
        }
        String result = builder.toString();
        System.out.println(result);
    }
}

Conclusively, immutable objects are used to reduce memory usage and object creation. It increases the performance by saving the time needed to create a new object. However, improper usage of immutable objects (so many modifications) can cause to performance reduction. Therefore, always try to use StringBuffer/StringBuilder whenever there is a need for large number of modifications.

Find the source codes at Git Hub.


Previous
Next Post »

Contact Form

Name

Email *

Message *