We all know that String
is immutable in Java, but check the following code:
String s1 = "Hello World"; String s2 = "Hello World"; String s3 = s1.substring(6); System.out.println(s1); // Hello World System.out.println(s2); // Hello World System.out.println(s3); // World Field field = String.class.getDeclaredField("value"); field.setAccessible(true); char[] value = (char[])field.get(s1); value[6] = 'J'; value[7] = 'a'; value[8] = 'v'; value[9] = 'a'; value[10] = '!'; System.out.println(s1); // Hello Java! System.out.println(s2); // Hello Java! System.out.println(s3); // World
Why does this program operate like this? And why is the value of s1
and s2
changed, but not s3
?
The String is immutable in Java because of the security, synchronization and concurrency, caching, and class loading. The reason of making string final is to destroy the immutability and to not allow others to extend it. The String objects are cached in the String pool, and it makes the String immutable.
String is immutable ( once created can not be changed ) object . The object created as a String is stored in the Constant String Pool. Every immutable object in Java is thread safe ,that implies String is also thread safe . String can not be used by two threads simultaneously.
Being immutable automatically makes the String thread safe since they won't be changed when accessed from multiple threads. Hence immutable objects, in general, can be shared across multiple threads running simultaneously.
Because String is immutable so the value one string object is holding will never get changed which means its hashcode will also not change which gives String class an opportunity to cache its hashcode during object creation.
String
is immutable* but this only means you cannot change it using its public API.
What you are doing here is circumventing the normal API, using reflection. The same way, you can change the values of enums, change the lookup table used in Integer autoboxing etc.
Now, the reason s1
and s2
change value, is that they both refer to the same interned string. The compiler does this (as mentioned by other answers).
The reason s3
does not was actually a bit surprising to me, as I thought it would share the value
array (it did in earlier version of Java, before Java 7u6). However, looking at the source code of String
, we can see that the value
character array for a substring is actually copied (using Arrays.copyOfRange(..)
). This is why it goes unchanged.
You can install a SecurityManager
, to avoid malicious code to do such things. But keep in mind that some libraries depend on using these kind of reflection tricks (typically ORM tools, AOP libraries etc).
*) I initially wrote that String
s aren't really immutable, just "effective immutable". This might be misleading in the current implementation of String
, where the value
array is indeed marked private final
. It's still worth noting, though, that there is no way to declare an array in Java as immutable, so care must be taken not to expose it outside its class, even with the proper access modifiers.
As this topic seems overwhelmingly popular, here's some suggested further reading: Heinz Kabutz's Reflection Madness talk from JavaZone 2009, which covers a lot of the issues in the OP, along with other reflection... well... madness.
It covers why this is sometimes useful. And why, most of the time, you should avoid it. :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With