Immutability
Once you have created it, you cannot change it. This is what 'Immutability' is all about. Speaking in a layman's language, if something is immutable, you simply cannot change it.
Same applies in case of Java, where you talk in terms of classes and objects, in which case, we associate the term immutability with the name class. When we say that a class is immutable, then it means that, once an instance of that class is created, it cannot be modified (to be more precise - attributes for that instance cannot be modified).
Ok enough with this boring generic explanation that of course you all know better than me (this I assume). Let us quickly move to our topic of interest - String class. By now you must have understood that we cannot modify an instance of String class once created. Of course you know this, but the point is why?
Probably you are most interested in that last question. I will come to that after describing the basics of Immutability.Why String class is designed this way? What all it takes to make a class immutable? And if String class is immutable, then how the hell have we been able to modify our String instance value till now? Is there any benefit of Immutability? and What can be the effect of Immutability on String Comparison?
So, we will cover these questions one by one.
So let's do the real talking...
String objects are Immutable!!!
Let's start straight with a simple code to explain this: -
public class Demo {
public static void main(String[] args) {String str1 = "abc"; // Line # 1String str2 = "abc"; // Line # 2String str3 = new String("abc"); // Line # 3str1 = str1 + "de"; // Line # 4}
}
Before we try to understand what is happening internally in the above code, we need to understand some buzz words : "String Literals" and "String Literal Pool".
String Literals are simple sequence of characters written within a pair of double-quotes. They are objects that are created on Heap.String Literal pool is a pool of constants containing references to these String literals in the heap.
In the above code, we have created a String literal on the heap: - "abc" and made a reference "str1" pointing it, or wait. Is it really happening like this? Probably not. Let us first understand what JVM does in background when we compile a .java file.
When a Java code is compiled into a class file, the JVM scans the .java file for all the String literals used. For each literal, it checks whether that literal is already created on the heap or not. If not, then it creates one, and stores a reference to it in the String Literal Pool. Now any further reference to this string literal will be assigned the earlier created reference to this string literal.
So, in the above code, if literal "abc" is already on the heap, it is not created again. Rather the string literal pool is searched for a reference to this literal and is assigned to str1. Similar is the case with str2.
Now, consider the case of str3. Does the above logic apply to this string also. Precisely not. So, here we are introduced with two ways of declaring and initializing a String.
Now, consider the case of str3. Does the above logic apply to this string also. Precisely not. So, here we are introduced with two ways of declaring and initializing a String.
In the first two cases, just a single object is created on the Heap, and both str1 and str2 are assigned a reference to that object. In the 3rd case, when we create a string with a new keyword, a new object will be created on the heap, regardless of whether a matching literal is already on heap or not. So, in this case, a different object will be created and str3 will directly point to this object. (So, 2 objects are created on the heap for the above code, both with value "abc")
Now, let's move further. And, OH MY GOD!! At Line # 4, What did you just see?? Modification of a String variable, which few paragraphs ago as we said, is immutable!! What is all this heck?
Ok, before you go and crack your head hitting on the wall, let me tell you that we're not.
We are not modifying our string object str1 at line#4. When we append a string literal to an existing object earlier pointed by reference str1, a new object is created on the heap containing the modified string, and the string reference str1 now is made to point to this new object. And what about the previously stored value? Where did it go? Well, that object is still on the heap, but with just a single reference, that of str2. And str1 is now detached from that object.
So, now that we got a glimpse of what happens when we try to modify our String object, its time for a new question.
Why Such a Design???
Security:
The main reason why String was made
immutable was security.
Lets take a look at this example: We have a method to reads a file with the filename passed to it as a parameter. If it is successful on locating that file, it returns a file object to that file. Now, suppose if String was mutable, then it was possible to change the filename, in the duration the request is sent back to the caller. Then imagine what will happen. The caller will have a file object for a file that does not exists.
Lets take a look at this example: We have a method to reads a file with the filename passed to it as a parameter. If it is successful on locating that file, it returns a file object to that file. Now, suppose if String was mutable, then it was possible to change the filename, in the duration the request is sent back to the caller. Then imagine what will happen. The caller will have a file object for a file that does not exists.
So its time to see the Benefits of Immutability!!
Well, there are many reasons for why immutability is a better design approach:
- First, an immutable class, as you can guess, is easier to design and implement. How? we will see later.
- Thread Secure: - Imagine you have an object that multiple threads will operate on, and you want to add concurrency to your problem, by allowing multiple threads to operate simultaneously on the object. Now, suppose while a Thread A is using your object, a different Thread B modifies the same object (possibly passed to it by Thread A). Now, we have an inconsistent state of object. Thread A and Thread B both are actually operating on two states of same object.
- Now, consider the case if your object was Immutable, then in that case, the Thread B will not be able to modify your object. And even if it tries to modify, it will create a new object for itself. Thus you will never have two states of the same object. Thus, immutable objects can be shared securely between Multiple Threads (Concurrent Access).
- Also, immutable objects make great building blocks for other immutable or mutable objects.
- E.g: - You can use immutable objects as a Key in your Map
How do you make One?
OK, so now you are eager to know how do you exactly make your class Immutable (So, that no one can change its state). Don't worry, Java allows you to protect your data. (Of course, everyone has a right to protect his/her property).
- To make your class Immutable, simply make your fields invisible to the outside, and remove all the setters from your class.
- Make sure that none of the fields in your class is mutable, else your class will not longer be immutable. Consider the below code: -
public class JustLooksImmutable {
private List<Integer> mutableList = new ArrayList<Integer>();private String name;public JustLooksImmutable() {/ ***Initializer code ***/}}
- The above class is mutable, even though we have all private fields, and have no setter methods. But, notice that we have a List<Integer>, which we can modify even by calling getter function of list.
/****So we can modify list like this: ****/
getMutableList().add(2);
- Here's the smallest and simplest Immutable class. You didn't expect it to be so easy, right? You can bet on that, Immutability is that easy..
/**** Simplest possible Immutable Class ****/
public class Immutable {
}
- And here's a full-blown Immutable class: -
public class Immutable {
/******** Private Fields *********/private int intVar;private String strVar;
/************* Constructors ************/// Zero-Arg constructor setting fields to default valuepublic Immutable() {}// One-Arg constructor to set integer field, String field set to its defaultpublic Immutable(int intVar) {this.intVar = intVar;}//One-Arg constructor to set String field, integer field set to its defaultpublic Immutable(String strVar) {this.strVar = strVar;}// Two-Arg constructor to set both the fieldspublic Immutable(int intVar, String strVar) {this(intVar);this(strVar);}/********** Add Getters for the fields *************/public int getIntVar() {return this.intVar;}public String getStrVar() {return this.strVar;}}
So, that was just one complete page of Java Code (Not large though right?). And now that you have got an idea about what Immutability is - I assume, it's time to move to our last but the most important question - How are String Comparison handled in JVM? and, What is the general practice we should follow while we compare Strings, or any other Object?
OK, without wasting even a few minutes lets quickly jump to the topic.
No comments:
Post a Comment