413

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Darshan Patel
  • 3,176
  • 6
  • 26
  • 49
  • 405
    You can do all kinds of stupid tricks with reflection. But you're basically breaking the "warranty void if removed" sticker on the class the instant you do it. – cHao Jan 06 '14 at 07:31
  • 16
    @DarshanPatel use a SecurityManager to disable reflection – Sean Patrick Floyd Jan 06 '14 at 08:59
  • 39
    If you really want to mess with things you can make it so that `(Integer)1+(Integer)2=42` by messing with the cached autoboxing; (Disgruntled-Bomb-Java-Edition)(http://thedailywtf.com/Articles/Disgruntled-Bomb-Java-Edition.aspx) – Richard Tingle Jan 06 '14 at 09:38
  • 5
    Oracle should rename the field `value` by appending some frequently changing part of the version string. Would be funny to see how many programs actually break. SSeriously, private fields are not part of the contract, so can change anytimes. Note that internals of the `String` class were indeed changed recently. – Ingo Jan 06 '14 at 11:13
  • 15
    You might be amused by this answer I wrote nearly 5 years ago http://stackoverflow.com/a/1232332/27423 - it's about immutable lists in C# but it's basically the same thing: how can I stop users from modifying my data? And the answer is, you can't; reflection makes it very easy. One mainstream language that doesn't have this problem is JavaScript, as it does not have a reflection system that can access local variables inside a closure, so private really does mean *private* (even though there's no keyword for it!) – Daniel Earwicker Jan 06 '14 at 12:49
  • 7
    All your program's allocated memory space is technically mutable. (-ish, I'm sure bits of it can be protected by the OS for some reason.) There's nothing preventing you from making a JNI call and writing random garbage wherever you want from the C code. It usually doesn't make sense to define immutability "immutable as long as you don't bypass the mechanisms that guarantee immutability". (Which, very frequently, is API design.) – millimoose Jan 07 '14 at 01:22
  • Is there a part of the Java specifications that prevents the VM from implementing a preventive copy of s1 before the array gets accessed? After the reflection code has stopped running, VM could then check if the value has really changed and then keep the copy, otherwise discard it and continue with just one copy of the const. string in memory. That way, the behavior of the program would be correct. – Joe Pineda Jan 07 '14 at 03:17
  • 53
    Is anybody reading the question to the end?? The question is, let me please repeat: "Why does this program operate like this? Why is the value of s1 and s2 changed and not changed for s3?" The question is NOT why are s1 and s2 changed! The question IS: WHY is s3 not changed? – Roland Pihlakas Jan 07 '14 at 03:53
  • 9
    I restored the original title of the question. I can't believe we've seen multiple 10k+ rep users making edits that completely change the question and now make a large number of the answers seem completely irrelevant. – Chris Hayes Jan 08 '14 at 05:35
  • Possible duplicate of *[Immutability of Strings in Java](http://stackoverflow.com/questions/1552301/immutability-of-strings-in-java)*. – Peter Mortensen Jan 23 '14 at 00:33
  • Sadly, nowadays this not working anymore – Dmitry Sokolov Dec 25 '18 at 21:52
  • I hope this helps [link](https://www.pushkarrajpujari.com/article/strings-in-java/) – Pushkarraj Pujari Aug 28 '19 at 12:59
  • 1
    @DmitrySokolov since Java 9, the internal array is `byte[]`, so you would have to adapt the code to make it work again, however, the modularization is steering towards a version where this will finally stop working. – Holger Feb 07 '20 at 19:35
  • @Holger Yes, true, trying this now with Java 12 has warnings: WARNING: An illegal reflective access operation has occurred WARNING: All illegal access operations will be denied in a future release – 01000001 Mar 08 '22 at 15:49

16 Answers16

414

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 Strings 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. :-)

Harald K
  • 26,314
  • 7
  • 65
  • 111
  • Note that some aspects of this (the interning, sharing/not sharing of backing arrays) are essentially dependent on the VM and should _not_ be relied upon for anything other than what it's used for: to cut down on memory used/as an optimization. Hoping for other, undocumented, functionality (ie comparing two strings with `==`) is going to get you in trouble. – Clockwork-Muse Jan 06 '14 at 08:21
  • 7
    Actually, `String` interning is part of the JLS (["a string literal always refers to the same instance of class String"](http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.5)). But I agree, it's not good practice to count on the implementation details of the `String` class. – Harald K Jan 06 '14 at 08:32
  • -1 for saying "String isn't immutable." I feel this is a misrepresentation of the situation, especially in comparison to the descriptions provided in some of the other answers to this question. – Sam Harwell Jan 06 '14 at 13:25
  • 3
    Maybe the reason why `substring` copies rather than using a "section" of the existing array, is otherwise if I had a huge string `s` and took out a tiny substring called `t` from it, and I later abandoned `s` but kept `t`, then the huge array would be kept alive (not garbage collected). So maybe it is more natural for each string value to have its own associated array? – Jeppe Stig Nielsen Jan 06 '14 at 14:14
  • @JeppeStigNielsen Yes, that's the reason. It happens often in web programming, although people used the workaround `new String(a.substring(..))`. – Aleksandr Dubinsky Jan 06 '14 at 14:21
  • 1
    @Jeppe, Alexandr The downside is that `substring` used to be an almost "free" operation due to the sharing, and now it's [potentially costly](http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7197183). :-/ – Harald K Jan 06 '14 at 14:37
  • Hey haraldk, could you please post a link if you have any regarding the mentioned "change the lookup table used in Integer autoboxing"? Thanks a lot in advance. – Yuriy Kulikov Jan 06 '14 at 15:10
  • @YuriyKulikov: I first heard about it in a talk at JavaZone, I think it was by [Heinz Kabutz](http://www.javaspecialists.eu/talks/oslo09/ReflectionMadness.pdf). But it's the same technique described by Richard Tingle in the comments section of the question. – Harald K Jan 06 '14 at 15:20
  • 10
    Sharing arrays between a string and its substrings also implied that *every* `String` instance had to carry variables for remembering the offset into the referred array and length. That’s an overhead not to ignore given the total number of strings and the typical ratio between normal strings and substrings in an application. Since they had to get evaluated for every string operation it meant slowing down *every* string operation just for the benefit of just *one* operation, a cheap substring. – Holger Jan 06 '14 at 19:32
  • 2
    @Holger - Yep, my understanding is that the offset field was dropped in recent JVMs. And even when it was present it was not used that often. – Hot Licks Jan 07 '14 at 17:18
  • @Holger: If the definition of `String` is attached to a particular runtime and compatibility with reflection-based string manipulation code is not required, then `String` could be within the runtime implemented as an abstract class with derived types `SimpleString`, `SubString`, etc. and a jinxed `getType()` which always returned `String`. Such a runtime could also do things like omit the cached `hashCode` when making strings less than 16 characters long, use a `Byte[]` rather than `Char[]` as the backing store for strings containing only ASCII, etc. – supercat Jan 07 '14 at 17:36
  • 1
    @supercat: the fact that `java.lang.String` is `final` is part of the specification. There can’t be subclasses. The implementation could do lots of things via delegation internally but this would create an overhead in a very often used class. Most implementations rely on the inlining of most `String` methods. – Holger Jan 07 '14 at 19:44
  • @Holger: The runtime cannot use subclasses *which can recognized as such by running code*, so if `s1` and `s2` are strings, then `s1.getClass()==s2.getClass()` must be true, but I don't think that means the runtime couldn't behind the scenes use multiple classes to hold "string" values *but jinx `getType()` and related methods so that all such objects would appear to be of type `String`. Does the standard mandate fields other than `length`? – supercat Jan 07 '14 at 20:17
  • 1
    @supercat: the standard does not mandate any fields in the `String` class. And the most recent version of Oracle’s implementation have no length field. They simply use the array’s length. Using a kind-of internal subclassing still opens the performance issues I described. Having different implementation code hinders inlining and turns unconditional code into conditional. And it would make it impossible to implement the class(es) using plain standard Java code anymore. – Holger Jan 08 '14 at 09:08
  • @Holger: My understanding is a packaged Java installation includes both the runtime and framework classes, so a version of `String` that ships with a particular runtime would not be expected to run with any other. If that is the case, I would think that speed loss incurred as a result of method dispatch could be offset by having many `String` natives written in optimized native code included within the runtime engine itself, which would be able to do things not possible within in Java [e.g. `substring` could read and write strings 32 bits at a time]. Personally... – supercat Jan 08 '14 at 16:15
  • ...I would have liked to have seen `string` be a "primitive" which might hold a `char[]` (which would be inaccessible to user code) but could hold anything the implementers saw fit. That would have allowed Java to enforce string immutability, allowed `==` to compare string values, etc. It would also have allowed for the possibility of strings being kept in a separate heap with a GC that could accommodate shrinking objects [so that a 500-character substring of a 5,000 character string could use the storage from the original, but if all references to the original vanished... – supercat Jan 08 '14 at 16:19
  • ...only the *portions* to which references still existed would need to be retained]. It's all academic now, but if string logic is included in the same distributable as the JVM, I wouldn't see that there would be a problem with the JVM using some "magic" to improve string handling. – supercat Jan 08 '14 at 16:21
  • 2
    @supercat: it doesn’t matter whether you have native code or not, having different implementations for strings and substring within the same JVM or having `byte[]` strings for ASCII strings and `char[]` for others implies that every operation has to check which kind of string it is before operating. This hinders inlining of the code into the methods using strings which is the first step of further optimizations using the context information of the caller. This is a big impact. – Holger Jan 08 '14 at 18:27
  • 1
    @supercat: Other things you mention *are* possible with an up-to-date JVM taking the `String` implementation as it is. E.g. `char` array copying will be transformed into a 32 bit or even 64 bit at a time copying. And the gc *can* detect when a `char[]` array is exclusively used by `String`s and do some magic (though Oracle’s developers seem to have decided against such magic). Similar things apply to the `String` equality. Equal `String`s might have a different identity due to the specification, but their internal array might be the same, making `equals` a trivial method. – Holger Jan 08 '14 at 18:32
  • 1
    @Holger the overhead of the start/length variables was not the problem, strangely it was an unexpected usage pattern. People would download an entire web page into a string then get a short sub-string out of that string and get rid of the original string assuming that it will be garbage collected, but unexpectedly their "substring" still had the entire text of the original string taking up memory. Doing this with a few dozen pages lead to instant and very confusing memory problems, so java was updated to adapt to this unexpected usage rather than force people to adapt to unexpected behavior. – Bill K Feb 07 '20 at 18:55
  • 1
    @BillK since that was the behavior for two decades, it was well understood, documented and not very surprising. Compare with this question, which is about the surprise that this array sharing did not happen anymore. The behavior surely stayed the same if it wasn’t the case that this behavior favored a small corner case (fast substrings) to the disadvantage of the majority (yes that includes surprises about the memory consumption, but not only). Nowadays, we have String Deduplication, which wouldn’t work that smoothly, if it had to atomically update three fields together. – Holger Feb 07 '20 at 19:31
  • @Holger It's funny how we developers tend to think that because we have been programming for a long time and know about bizarre behaviors, they aren't surprises. Surprises are, by definition, behaviors you had to learn about and were therefore documented, those are the most important to address. – Bill K Feb 10 '20 at 20:11
  • 1
    @BillK you don't need to convince me that this behavior was a surprising behavior. I'm just saying that this was not the main motivation to change that. The JRE developers did not suddenly realize that this behavior is surprising. They already knew for twenty years. Actual performance studies with real life applications had a much higher weight. – Holger Feb 10 '20 at 21:26
95

In Java, if two string primitive variables are initialized to the same literal, it assigns the same reference to both variables:

String Test1="Hello World";
String Test2="Hello World";
System.out.println(test1==test2); // true

initialization

That is the reason the comparison returns true. The third string is created using substring() which makes a new string instead of pointing to the same.

sub string

When you access a string using reflection, you get the actual pointer:

Field field = String.class.getDeclaredField("value");
field.setAccessible(true);

So change to this will change the string holding a pointer to it, but as s3 is created with a new string due to substring() it would not change.

change

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Zaheer Ahmed
  • 28,160
  • 11
  • 74
  • 110
  • This only works for literals and is a compile-time optimization. – SpacePrez Jan 06 '14 at 21:44
  • 2
    @Zaphod42 Not true. You can also call `intern` manually on a non-literal String and reap the benefits. – Chris Hayes Jan 08 '14 at 08:48
  • Note, though: you want to use `intern` judiciously. Interning everything doesn't gain you much, and can be the source of some head-scratching moments when you add reflection to the mix. – cHao Jan 24 '14 at 02:17
  • `Test1` and `Test1` are inconsistent with `test1==test2` and do not follow java naming conventions. – c0der Dec 25 '19 at 17:11
  • @c0der Can you write the entire java program with public static void main method to depic this – deepakl.2000 Apr 30 '22 at 07:06
  • @deepakl.2000 run it online: https://www.online-ide.com/03crhm6aOu . You'll get a compilation error. The variable names are inconsistent. – c0der Apr 30 '22 at 09:13
  • @c0der Where is the entire program which depicts Pointer[x] and Pointer[y] – deepakl.2000 Apr 30 '22 at 09:43
51

You are using reflection to circumvent the immutability of String - it's a form of "attack".

There are lots of examples you can create like this (eg you can even instantiate a Void object too), but it doesn't mean that String is not "immutable".

There are use cases where this type of code may be used to your advantage and be "good coding", such as clearing passwords from memory at the earliest possible moment (before GC).

Depending on the security manager, you may not be able to execute your code.

Community
  • 1
  • 1
Bohemian
  • 412,405
  • 93
  • 575
  • 722
30

You are using reflection to access the "implementation details" of string object. Immutability is the feature of the public interface of an object.

Ankur
  • 33,367
  • 2
  • 46
  • 72
24

Visibility modifiers and final (i.e. immutability) are not a measurement against malicious code in Java; they are merely tools to protect against mistakes and to make the code more maintainable (one of the big selling points of the system). That is why you can access internal implementation details like the backing char array for Strings via reflection.

The second effect you see is that all Strings change while it looks like you only change s1. It is a certain property of Java String literals that they are automatically interned, i.e. cached. Two String literals with the same value will actually be the same object. When you create a String with new it will not be interned automatically and you will not see this effect.

#substring until recently (Java 7u6) worked in a similar way, which would have explained the behaviour in the original version of your question. It didn't create a new backing char array but reused the one from the original String; it just created a new String object that used an offset and a length to present only a part of that array. This generally worked as Strings are immutable - unless you circumvent that. This property of #substring also meant that the whole original String couldn't be garbage collected when a shorter substring created from it still existed.

As of current Java and your current version of the question there is no strange behaviour of #substring.

Hauke Ingmar Schmidt
  • 11,559
  • 1
  • 42
  • 50
  • 2
    Actually, visibility modifiers *are* (or at least were) intended as protection againts malicious code - however, you need to set a SecurityManager (System.setSecurityManager() ) to activate the protection. How secure this actually is is another question... – sleske Jan 06 '14 at 11:36
  • 2
    Deserves an upvote because you emphasise that access modifiers **are not intended** to 'protect' code. This seems to be widely misunderstood in both Java and .NET. Although the previous comment does contradict that; I don't know much about Java, but in .NET this is certainly true. In neither language should users assume this makes their code hack-proof. – Tom W Jan 06 '14 at 12:42
  • It's not possible to violate the contract of `final` even through reflection. Also, as mentioned in another answer, since Java 7u6, `#substring` doesn't share arrays. – ntoskrnl Jan 06 '14 at 12:58
  • Actually, the behavior of `final` has changed over time... :-O According the "Reflection Madness" talk by Heinz I posted in the other thread, `final` meant final in JDK 1.1, 1.3 and 1.4, but could be modified using reflection using 1.2 always, and in 1.5 and 6 *in most cases*... – Harald K Jan 06 '14 at 17:22
  • 1
    `final` fields can be changed through `native` code as done by the Serialization framework when reading the fields of a serialized instance as well as `System.setOut(…)` which modifies the final `System.out` variable. The latter is the most interesting feature as reflection with access override cannot change `static final` fields. – Holger Jan 06 '14 at 19:45
11

String immutability is from the interface perspective. You are using reflection to bypass the interface and directly modify the internals of the String instances.

s1 and s2 are both changed because they are both assigned to the same "intern" String instance. You can find out a bit more about that part from this article about string equality and interning. You might be surprised to find out that in your sample code, s1 == s2 returns true!

Krease
  • 15,805
  • 8
  • 54
  • 86
10

Which version of Java are you using? From Java 1.7.0_06, Oracle has changed the internal representation of String, especially the substring.

Quoting from Oracle Tunes Java's Internal String Representation:

In the new paradigm, the String offset and count fields have been removed, so substrings no longer share the underlying char [] value.

With this change, it may happen without reflection (???).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
manikanta
  • 8,100
  • 5
  • 59
  • 66
  • 2
    If the OP was using an older Sun/Oracle JRE, the last statement would print "Java!" (as he accidentally posted). This only affect the sharing of the value array between strings and sub strings. You still can't change the value without tricks, like reflection. – Harald K Jan 06 '14 at 13:25
7

There are really two questions here:

  1. Are strings really immutable?
  2. Why is s3 not changed?

To point 1: Except for ROM there is no immutable memory in your computer. Nowadays even ROM is sometimes writable. There is always some code somewhere (whether it's the kernel or native code sidestepping your managed environment) that can write to your memory address. So, in "reality", no they are not absolutely immutable.

To point 2: This is because substring is probably allocating a new string instance, which is likely copying the array. It is possible to implement substring in such a way that it won't do a copy, but that doesn't mean it does. There are tradeoffs involved.

For example, should holding a reference to reallyLargeString.substring(reallyLargeString.length - 2) cause a large amount of memory to be held alive, or only a few bytes?

That depends on how substring is implemented. A deep copy will keep less memory alive, but it will run slightly slower. A shallow copy will keep more memory alive, but it will be faster. Using a deep copy can also reduce heap fragmentation, as the string object and its buffer can be allocated in one block, as opposed to 2 separate heap allocations.

In any case, it looks like your JVM chose to use deep copies for substring calls.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Scott Wisniewski
  • 24,561
  • 8
  • 60
  • 89
  • 3
    Real ROM is just as immutable as a photographic print encased in plastic. The pattern is permanently set when the wafer (or print) is chemically developed. Electrically-alterable memories, *including RAM chips*, can behave as "true" ROM if the control signals necessary to write it cannot be energized without adding additional electrical connections to the circuit wherein it is installed. It's actually not uncommon for embedded devices to include RAM which is set at the factory and maintained by a back-up battery, and whose contents would need to be reloaded by the factory if the battey failed. – supercat Jan 07 '14 at 17:41
  • 3
    @supercat: Your computer is not one of those embedded systems, though. :) True hard-wired ROMs haven't been common in PCs for a decade or two; everything's EEPROM and flash these days. Basically every user-visible address that refers to memory, refers to potentially writable memory. – cHao Jan 24 '14 at 02:34
  • @cHao: Many flash chips allow portions to be write-protected in a fashion which, if it can be undone at all, would require applying different voltages than would be required for normal operation (which motherboards would not be equipped to do). I would expect motherboards to use that feature. Further, I'm not certain about today's computers, but historically some computers have had a region of RAM which was write-protected during the boot stage and could only be unprotected by a reset (which would force execution to start from ROM). – supercat Jan 24 '14 at 15:40
  • 2
    @supercat I think you are missing the point of the topic, which is that the strings, stored in RAM, aren't going to ever be truly immutable. – Scott Wisniewski Jan 24 '14 at 19:49
5

According to the concept of pooling, all the String variables containing the same value will point to the same memory address. Therefore s1 and s2, both containing the same value of “Hello World”, will point towards the same memory location (say M1).

On the other hand, s3 contains “World”, hence it will point to a different memory allocation (say M2).

So now what's happening is that the value of S1 is being changed (by using the char [ ] value). So the value at the memory location M1 pointed both by s1 and s2 has been changed.

Hence as a result, memory location M1 has been modified which causes change in the value of s1 and s2.

But the value of location M2 remains unaltered, hence s3 contains the same original value.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
5

The reason s3 does not actually change is because in Java when you do a substring the value character array for a substring is internally copied (using Arrays.copyOfRange()).

s1 and s2 are the same because in Java they both refer to the same interned string. It's by design in Java.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Maurizio In denmark
  • 4,226
  • 2
  • 30
  • 64
  • 2
    How did this answer add anything to the answers before you? – Gray Jan 07 '14 at 20:52
  • Also note that this is a quite new behaviour, and not guaranteed by any spec. – Paŭlo Ebermann Jan 07 '14 at 21:15
  • The implementation of `String.substring(int, int)` changed with Java 7u6. Before 7u6, the JVM would just keep a pointer to the original `String`'s `char[]` together with an index and length. After 7u6, it copies the substring into a new `String` There are pros and cons. – Eric Jablow Jan 07 '14 at 22:13
5

To add to the @haraldK's answer - this is a security hack which could lead to a serious impact in the app.

First thing is a modification to a constant string stored in a String Pool. When string is declared as a String s = "Hello World";, it's being places into a special object pool for further potential reusing. The issue is that compiler will place a reference to the modified version at compile time and once the user modifies the string stored in this pool at runtime, all references in code will point to the modified version. This would result into a following bug:

System.out.println("Hello World"); 

Will print:

Hello Java!

There was another issue I experienced when I was implementing a heavy computation over such risky strings. There was a bug which happened in like 1 out of 1000000 times during the computation which made the result undeterministic. I was able to find the problem by switching off the JIT - I was always getting the same result with JIT turned off. My guess is that the reason was this String security hack which broke some of the JIT optimization contracts.

Andrey Chaschev
  • 16,160
  • 5
  • 51
  • 68
  • It might have been a thread-safety issue that was masked by slower execution time and less concurrency without JIT. – Ted Pennings Jan 07 '14 at 21:37
  • @TedPennings From my description it could, I just didn't want to go too much into details. I actually spent like a couple of days trying to localize it. It was a single-threaded algorithm which calculated a distance between two texts written in two different languages. I found two possible fixes for the issue - one was to turn off the JIT and the second one was to add literally no-op `String.format("")` inside one of the inner loops. There is a chance for it being some-other-then-JIT-failure issue, but I believe it was JIT, because this issue was never reproduced again after adding this no-op. – Andrey Chaschev Jan 07 '14 at 22:44
  • I was doing this with an early version of JDK ~7u9, so it could be it. – Andrey Chaschev Jan 07 '14 at 22:48
  • 1
    @Andrey Chaschev: “I found two possible fixes for the issue”… the third possible fix, not to hack into the `String` internals, did not come into your mind? – Holger Jan 08 '14 at 11:40
  • 1
    @Ted Pennings: thread-safety issues and JIT issues are often the very same. The JIT is allowed to generate code which relies on the `final` field thread safety guarantees which break when modifying the data after object construction. So you can view it as a JIT issue or a MT issue just as you like. The real issue is to hack into the `String` and modify data which are expected to be immutable. – Holger Jan 08 '14 at 11:45
2

String is immutable, but through reflection you're allowed to change the String class. You've just redefined the String class as mutable in real-time. You could redefine methods to be public or private or static if you wanted.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
SpacePrez
  • 1,086
  • 7
  • 15
  • 2
    If you change the visibility of fields/methods it isn't useful because at compile time they are private – Bohemian Jan 06 '14 at 23:55
  • 1
    You can change the accessibility on methods but you can't change their public/private status and you can't make them be static. – Gray Jan 07 '14 at 20:49
2

Strings are created in permanent area of the JVM heap memory. So yes, it's really immutable and cannot be changed after being created. Because in the JVM, there are three types of heap memory: 1. Young generation 2. Old generation 3. Permanent generation.

When any object are created, it goes into the young generation heap area and PermGen area reserved for String pooling.

Here is more detail you can go and grab more information from: How Garbage Collection works in Java .

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Yasir Shabbir Choudhary
  • 2,458
  • 2
  • 27
  • 31
1

[Disclaimer this is a deliberately opinionated style of answer as I feel a more "don't do this at home kids" answer is warranted]

The sin is the line field.setAccessible(true); which says to violate the public api by allowing access to a private field. Thats a giant security hole which can be locked down by configuring a security manager.

The phenomenon in the question are implementation details which you would never see when not using that dangerous line of code to violate the access modifiers via reflection. Clearly two (normally) immutable strings can share the same char array. Whether a substring shares the same array depends on whether it can and whether the developer thought to share it. Normally these are invisible implementation details which you should not have to know unless you shoot the access modifier through the head with that line of code.

It is simply not a good idea to rely upon such details which cannot be experienced without violating the access modifiers using reflection. The owner of that class only supports the normal public API and is free to make implementation changes in the future.

Having said all that the line of code is really very useful when you have a gun held you your head forcing you to do such dangerous things. Using that back door is usually a code smell that you need to upgrade to better library code where you don't have to sin. Another common use of that dangerous line of code is to write a "voodoo framework" (orm, injection container, ...). Many folks get religious about such frameworks (both for and against them) so I will avoid inviting a flame war by saying nothing other than the vast majority of programmers don't have to go there.

simbo1905
  • 6,321
  • 5
  • 58
  • 86
0

String is immutable in nature Because there is no method to modify String object. That is the reason They introduced StringBuilder and StringBuffer classes

0

This is a quick guide to everything


        // Character array
        char[] chr = {'O', 'K', '!'};

        // this is String class
        String str1 = new String(chr);
        
        // this is concat
        str1 = str1.concat("another string's ");
        
        // this is format
        System.out.println(String.format(str1 + " %s ", "string"));
        
        // this is equals
        System.out.println(str1.equals("another string"));

        //this is split
        for(String s: str1.split(" ")){
            System.out.println(s);
        }

        // this is length
        System.out.println(str1.length());

        //gives an score of the total change in the length
        System.out.println(str1.compareTo("OK!another string string's"));

        // trim
        System.out.println(str1.trim());

        // intern
        System.out.println(str1.intern());

        // character at
        System.out.println(str1.charAt(5));

        // substring
        System.out.println(str1.substring(5, 12));

        // to uppercase
        System.out.println(str1.toUpperCase());

        // to lowerCase
        System.out.println(str1.toLowerCase());

        // replace
        System.out.println(str1.replace("another", "hello"));

       //   output

        // OK!another string's  string 
        // false
        // OK!another
        // string's
        // 20
        // 7
        // OK!another string's
        // OK!another string's 
        // o
        // other s
        // OK!ANOTHER STRING'S 
        // ok!another string's 
        // OK!hello string's 


Shehan Hasintha
  • 947
  • 10
  • 10