1. What is an immutable object?
As we all know, in Java, the string class is immutable. So what exactly is an immutable object?
It can be said that if an object cannot change its state after it has been created, then the object is immutable.
the inability to change the state means that the member variables within the object cannot be changed, that the value of the underlying data type cannot be changed, that a variable of the reference type cannot point to other objects, and that the state of the object to which the reference type points cannot be changed.
2. references to sub-objects and objects
For Java beginners, there is always doubt that a string is an immutable object. Look at the following code:
1 String s = "Abcabc"; 2 System.out.println ("s =" + s); 3 4 s = "123456"; 5 System.out.println ("s =" + s);
Printing results are:
1 s = abcabc2 s = 123456
First create a String object s, then the value of S to "Abcabc", and then let s the value "123456". As you can see from the printout, the value of S has indeed changed.
So how do you say the string object is immutable?
In fact, there is a misunderstanding: S is just a reference to a string object, not the object itself . An object is an area of memory in memory, and the more member variables, the greater the amount of space that the memory area occupies. A reference is just a 4-byte data that contains the address of the object it points to, and the object can be accessed by this address.
That is: s is just a reference, it points to a specific object when s="123456"; After this code has been executed, a new object "123456"is created, The reference s again points to this new object, and the original object "abcabc" still exists in memory and does not change. The memory structure looks like this:
A different point of Java and C + + is that in Java it is not possible to manipulate the object itself directly, all objects are pointed to by a reference, which must be used to access the object itself, including getting the value of the member variable, changing the object's member variables, invoking the object's methods, and so on. In C + + There are references, objects and pointers to three things, and these three things can access the object. In fact, the references in Java are conceptually similar to the pointers in C + +, they are the address values of the stored object in memory, but in Java, the reference loses some flexibility, such as a reference in Java cannot be added and reduced as a pointer in C + +.
3. Why is the string object immutable?
To understand the immutability of a string, first look at what member variables are in the string class. In JDK1.6, there are several member variables for string:
1 Public Final classString2 ImplementsJava.io.Serializable, comparable<string>, Charsequence3 {4 /**The value is used for character storage.*/5 Private Final Charvalue[];6 7 /**The offset is the first index of the storage, which is used.*/8 Private Final intoffset;9 Ten /**The count is the number of characters in the String.*/ One Private Final intcount; A - /**Cache The hash code for the string*/ - Private intHash//Default to 0
In JDK1.7, the string class made some changes, mainly changing the behavior of the substring method when it was executed, which is irrelevant to the topic in this article. The main member variables of the string class in JDK1.7 are two:
1 Public Final classString2 ImplementsJava.io.Serializable, comparable<string>, charsequence {3 /**The value is used for character storage.*/ 4 Private Final Charvalue[]; 5 6 /**Cache The hash code for the string*/ 7 Private intHash//Default to 0
As can be seen from the above code, the String class in Java is actually the encapsulation of the character array . In JDK6, value is an array of string wrappers , offset is the starting position of string in this value array, and count is the number of characters that the string occupies. In JDK7, there is only one value variable, that is, all characters in value are of the object of string. This change does not affect the discussion in this article. In addition to this, there is a hash member variable, which is the cache of the hash value of the string object, and this member variable is not related to the discussion in this article. in Java, arrays are also objects .
So value is just a reference, it points to a real array object. Actually executes the string s = "ABCABC"; After this code, the actual memory layout should look like this:
The three variables of Value,offset and count are private, and no public methods such as SetValue, SetOffset, and SetCount are available to modify the values, so the string cannot be modified outside the string class. This means that once the initialization is not modified, and the three members cannot be accessed outside the string class. In addition, the three variables of Value,offset and count are final, that is, within the string class, once the three values are initialized, they cannot be changed. So you can think of a string object as immutable.
so in string, there are some methods that can be called to get the changed values. These methods include substring, replace, ReplaceAll, toLowerCase and so on. For example, the following code:
1 String a = "Abcabc"; 2 System.out.println ("a =" + a); 3 a = A.replace (' A ', ' a '); 4 System.out.println ("a =" + a);
The result of the above code decompile is as follows:
1 String s = "Abcabc"; 2 System.out.println ((new StringBuilder ()). Append ("A="). Append (s). ToString ()); 3 s = s.replace (' A ', ' a '); 4 System.out.println ((new StringBuilder ()). Append ("A="). Append (s). ToString ());
Printing results are:
A == abcabc
Then the value of a seems to change, in fact, it is the same misunderstanding. Again, A is just a reference, not a real string object, when calling A.replace (' a ', ' a '), a new string object is created inside the method and the new object is re-assigned to reference a. the source code of the Replace method in string can explain the problem:
1 /**2 * Copies This string replacing occurrences of the specified character with3 * another character.4 *5 * @paramOldChar6 * The character to replace.7 * @paramNewchar8 * The replacement character.9 * @returna new string with occurrences of OldChar replaced by Newchar.Ten */ One PublicString Replace (CharOldChar,CharNewchar) { A Char[] buffer =value; - int_offset =offset; - int_count =count; the - intIDX =_offset; - intLast = _offset +_count; - Booleancopied =false; + while(IDX <Last ) { - if(Buffer[idx] = =OldChar) { + if(!copied) { A Char[] Newbuffer =New Char[_count]; atSystem.arraycopy (buffer, _offset, Newbuffer, 0, _count); -Buffer =Newbuffer; -IDX-=_offset; -Last-=_offset; -copied =true; - } inBUFFER[IDX] =Newchar; - } toidx++; + } - the returnCopied?NewString (0, Count, buffer): This; *}
The reader can view the other methods by re-creating the new string object inside the method, and returning the new object, the original object will not be changed. This is why there are return values for methods such as replace, Substring,tolowercase, and so on. It is also why the call does not change the value of the object as follows:
1 String ss = "123456"; 2 System.out.println ("ss =" + ss); 3 ss.replace (' 1 ', ' 0 '); 4 System.out.println ("ss =" + ss);
Printing results:
1 ss = 1234562 ss = 123456
4. Is the string object really immutable?
from the above, it is known that the member variable of the string is private final, which is not changed after initialization. So in these few members, value is more special because he is a reference variable, not a real object. Value is final decorated, that is, final cannot point to other array objects, can I change the array that value points to? For example, change a character in an array to an underscore "_". At least not in the ordinary code we write ourselves, because we simply cannot access this value reference, nor can we modify the array by this reference.
So what is the way to access a private member? Yes, with reflection, you can reflect the Value property in a String object and change the structure of the array by the value reference obtained . Here is the instance code:
1 Public Static voidTestreflection ()throwsException {2 //Create the string "Hello world" and assign it to the reference s3String s = "Hello World"; 4System.out.println ("s =" + s);//Hello World5 //Get the Value field in the String class6Field valuefieldofstring =7String.class. Getdeclaredfield ("value");8 9 //changing the access rights of the Value propertyTenValuefieldofstring.setaccessible (true); One //gets the value of the Value property on the S object A Char[] Value = (Char[]) valuefieldofstring.get (s); - //Change the 5th character in the array referenced by value -Value[5] = ' _ '; theSystem.out.println ("s =" + s);//Hello_world -}
The printing results are as follows:
1 s = HelloWorld2 s = Hello_world
In this process,s always refers to the same string object , but before and after the reflection, the string object has changed, that is to say, by reflection, the so-called immutable object can be modified . But generally we do not do so.
This example of reflection can also illustrate a problem: If an object, the state of his group of other objects can be changed, then this object is probably not immutable object. For example, a car object, which combines a wheel object, although the wheel object is declared as private final, but the internal state of the wheel object can be changed, it is not very good to ensure that the car object is immutable.
Is the string object in the Java basics hardening 101:java really immutable?