Why String is Immutable in Java?
Overview
In Java, the string is an object that represents a sequence of characters. The java.lang.String class is used to create a string object. Java String class provides a lot of methods to perform operations on strings.
A string is an immutable object which means we cannot change them after creating the objects. Whenever we change any string, a new instance is created. In this article, we will explore Why Strings are immutable in Java along with its benefits.
What is an Immutable Object?
Immutable refers to something that cannot be changed or modified. Hence, immutable objects are ones that cannot be modified after they have been created. This is an object whose internal state does not change after it has been created completely. It assures us that it will behave in the same manner for the rest of its life. It can be safely shared among different threads because its internal state remains consistent.
The concept of immutability is required in certain situations such as when you require the attributes ( properties ) of an object to be dynamically set during its creation and then automatically be set as permanent, or for several other security-related situations.
What is Immutable String in Java?
A String is defined as a sequence or an array of characters. Strings are treated as objects in the Java programming language. The term "immutable string" in Java refers to a string object that cannot be altered, but the reference to the object can be changed. Every time we make a modification, a new instance of that string is created and the previous value is copied to the new String with the change. The String class is marked final to prevent overriding the functionality of its methods.
In Java, the String class and all wrapper classes which include Boolean, Character, Byte, Short, Integer, Long, Float, and Double are immutable. A user is free to create immutable classes of their own.
Why String Objects are Immutable in Java and Its Benefits?
As we have discussed, Strings are immutable which means it's value can't be changed. Now, let's look into the below example to understand why String objects are immutable in Java:
Strings in Java are specified as immutable, as seen above because strings with the same content share storage in a single pool to minimize creating a copy of the same value. That is to say, once a String is generated, its content cannot be changed and hence changing content will lead to the creation of a new String. Otherwise, the update will affect the heap value, and then all other String references that share the same storage location will be changed. This change has the potential to be unpredictable, which is why it is not desirable.
A new String instance will be generated if in case it previously doesn't have any instance in the pool and returned to that same caller if any modifications are made to the String or if any operation is performed using String methods. When there are no more references to the original String object in the pool, it will be unallocated and the global garbage collector will remove it.
As shown in the previous example Strings s1, s2, and s3 are created. Now we are doing further operations as below:
Output:
As shown above, considering the example:
- String s1 is updated with a new value and that's why a new instance is created. Hence, s1 reference changes to that newly created instance "Java".
- String s2 and s3 remain unchanged as their references were not changed to a new instance created after performing concat() operation.
- "Hello World" remains unreferenced to any object and lost in the pool as s2.concat() operation (in line number 5) is not assigned to any object. That's why there is a reference to its result.
- String newS3 refers to the instance of s3.concat() operation that is "Hello Scaler" as it is referenced to new object newS3.
Hence, Strings are immutable and whenever we change the string only its reference is changed to the new instance.
String Pool
In java, a String pool refers to a storage area in heap memory. Memory allocation to a String object is a costly process in terms of both time as well as memory. The JVM (Java Virtual Machine) performs a sequence of steps while initializing string literals to reduce memory overhead and increase performance efficiency.
To reduce the number of String objects produced and hence the overall memory consumption, the String class keeps a pool of strings. Every time a string literal is generated, the JVM checks for its existence in the string pool first. A reference to the instance of the String is returned if the string already exists in the string pool, otherwise a new String object is instantiated and added to the pool.
Thread Safety
Thread-safety is a property that ensures that multiple threads can work safely in coordination on a shared resource or object. The work done by one thread does not override the work done by any other thread.
Whenever a thread attempts to change the value of a String object, the thread simply ends up producing a new object. Now even that very same thread would not be able to make any changes to the original String object.
But in the case of the usage of references, thread safety is not maintained. Thus, Immutable objects are always thread-safe, but their references are not.
Benefits of Immutability of String in Java
-
Security As the String is commonly utilized in Java applications. Immutable String objects are used to prevent making any more mistakes because they load the right class. Strings are used to hold sensitive data such as connection URLs, usernames, and passwords. Consider banking software: because String objects are immutable, any attacker will be unable to change the username and password. If Strings were mutable, we couldn't be sure whether the String we got was safe or not when running the update, even after performing security checks. Because of the immutability of strings in Java, the application software can be more secure.
As an example, consider the following code snippet:
In the above block of code, let us assume that we have received a String object from a nonreliable source. We're doing a security check to check if the String object is empty. After that, we will perform some database-related operations.
The untrustworthy source caller function still has reference to this name object. If String class objects were not immutable, then by the time the database query is executed, we can not be sure if the String our function crucial method received, remains secure even after performing security checks.
The unreliable caller method still possesses a reference to the String object and can change its value between security checks. This makes our query prone to SQL injections.
It is also a possibility that the String name is visible to another thread, which could change the String object's value after the security checks.
-
Synchronization The String object is immutable, so there is no need to worry about synchronization while sharing an object across several threads. They will not be modified if they are accessed from numerous threads. As Strings are immutable objects, they can be shared across several threads that are running at the same time. They're also thread-safe because whenever a thread modifies the value, the String pool creates a new String instead of modifying the existing one. As a result, multi-threading is safe with Strings.
-
Hashcode Caching String objects are used in hash implementations like as HashTable, HashMap, HashSet, and so on. In these hash implementations, the hashCode() method is utilized for the bucketing system. Immutable strings in Java guarantee that their value will not change. So the hashCode() method in the String class is overridden to support caching. During the initial hashCode() call the hash is calculated and cached, and the same value is returned every time after that. As a result, while working with String objects, the performance of collections that use hash implementations will increase.
-
Performance Strings are immutable, hence there is a String pool. Because the String pool improves efficiency by reducing heap memory, hash implementations can be accessed more quickly when using Strings. The JVM verifies if the value already exists inside the String pool when we want to declare a new String object. If it already exists, the new object is given the same value. Because String seems to be the most extensively used, enhancing its efficiency has a significant impact on the overall performance of the application.
Demerits of Using String
Along with the benefits of Immutable String in Java, there are some demerits as well which we will discuss in this section. We have seen Immutability of String being an advantage but this immutability is somehow a disadvantage as well because it doesn't allow manipulation of the String. And if we try to manipulate then it will create a new object. So in real-world applications, if we need to change the String value many times then it will take a lot of heap memory by creating many objects. Hence, the execution efficiency will decrease. So it is suitable to use only with a small amount of data only.
However, for a large amount of data and frequent changes in String, or to create mutable String we use StringBuilder and StringBuffer.StringBuffer and StringBuilder are not the same thing. StringBuffer is String variables and String Builder is mutable objects. When we use them to manipulate a string, we're actually manipulating an object, so it's not the same as a String. Create some outer items to operate, and the speed will be increased. JVM cannot guarantee that the operation of StringBuilder is secure when multiple**** threads use StringBuffer. Although it is the fastest, it can ensure that StringBuffer is correctly operated.
Conclusion
As per the article, we can conclude that:
- Strings are immutable, therefore their references can be provided in methods or threads without fear of the actual String object changing.
- Strings are stored in String pools as it enhances the performance by saving heap memory.
- Immutable String in Java has benefits such as Security, Synchronization, Hashcode Caching and improving performance.
- In java, a String pool refers to a storage area in heap memory used by JVM for caching String literals.
- Thread-safety is a property that ensures that multiple threads can work safely in coordination on a shared resource or object.