15

As we all know, String is immutable in java. however, one can change it using reflection, by getting the Field and setting access level. (I know it is unadvised, I am not planning to do so, this question is pure theoretical).

my question: assuming I know what I am doing (and modify all fields as needed), will the program run properly? or does the jvm makes some optimizations that rely on String being immutable? will I suffer performance loss? if so, what assumption does it make? what will go wrong in the program

p.s. String is just an example, I am interested actually in a general answer, in addition to the example.

thanks!

amit
  • 175,853
  • 27
  • 231
  • 333
  • Performance loss, compared to what? Doing something normally takes more time than doing nothing. – user unknown Aug 03 '11 at 20:29
  • Related: [What is the purpose of modifying a string using reflection?](http://stackoverflow.com/q/6905181/600500). – Paŭlo Ebermann Aug 03 '11 at 20:36
  • @user: by performance loss I meant for other oporations including this String object (which was modified), will they suffer performance loss, comparing to Strings that were not modified? – amit Aug 03 '11 at 20:40
  • How should it affect the performance? Do you believe, there is a `mutated`-flag, which will invoke some extra checking, if the string was modified? If you assume, there is a test, whether the Strings length is somehow different from the length of the internal char-array, or whether the hashcode is still alright - that would be a check performed on every String. So what schould then be the reason to use a Stringpool at all? Did you look into the source of the String class? Or debug the String class, and see the source in your IDE? – user unknown Aug 04 '11 at 01:07

7 Answers7

7

After compilation some strings may refer to the one instance, so, you will edit more than you want and never know what else are you editing.

public static void main(String args[]) throws Exception {
    String s1 = "Hello"; // I want to edit it
    String s2 = "Hello"; // It may be anywhere and must not be edited
    Field f = String.class.getDeclaredField("value");
    f.setAccessible(true);
    f.set(s1, "Doesn't say hello".toCharArray());
    System.out.println(s2);
}

Output:

Doesn't say hello
Oliver Spryn
  • 16,871
  • 33
  • 101
  • 195
Sergey Fedorov
  • 2,169
  • 1
  • 15
  • 26
6

You are definitely asking for trouble if you do this. Does that mean you will definitely see bugs right away? No. You might get away with it in a lot of cases, depending on what you're doing.

Here are a couple of cases where it would bite you:

  • You modify a string that happens to have been declared as literal somewhere within the code. For example you have a function and somewhere it is being called like function("Bob"); in this scenario the string "Bob" is changed throughout your app (this will also be true of string constants declared as final).
  • You modify a string which is used in substring operations, or which is the result of a substring operation. In Java, taking a substring of a string actually uses the same underlying character array as the source string, which means modifications to the source string will affect substrings (and vice versa).
  • You modify a string that happens to be used as a key in a map somewhere. It will no longer compare equal to its original value, so lookups will fail.

I know this question is about Java, but I wrote a blog post a while back illustrating just how insane your program may behave if you mutate a string in .NET. The situations are really quite similar.

Dan Tao
  • 125,917
  • 54
  • 300
  • 447
  • I know many things can go wrong, my question is NOT what can go wrong, it's about the jvm behavior, where will it be undefined (or will fail), not because of program bugs, but because it assumes String is immutable – amit Aug 03 '11 at 20:52
  • @amit: String interning is an example of JVM behavior that assumes strings are immutable. The behavior of substring operations is an example of Java behavior that assumes strings are immutable. I guess what I'm trying to say is that yes, there are both runtime- and language-level assumptions that will be broken and lead to undefined behavior if you mutate a `String` instance. If you're looking for other JVM-specific examples *besides* string interning, I can't think of any off the top of my head. But I didn't write the JVM. – Dan Tao Aug 03 '11 at 20:57
3

The thing that jumps to mind for me is string interning - literals, anything in the constant pool and anything manually intern()ed points to the same string object. If you start messing around with the contents of an interned string literal, you may well see the exact same alterations on all the other literals using the same underlying object.

I'm not sure whether the above actually happens since I've never tried (in theory it will, I don't know if something happens under the scene to stop it but I doubt it) but it's things like that that could throw up potential problems. Of course, it could also throw up problems at the Java level through just passing multiple references to the same string around and then using a reflection attack to alter the object from one of the references. Most people (me included!) won't explicitly guard against that sort of thing in code, so using that attack with any code that's not your own, or your own code if you haven't guarded against that either, could cause all sorts of bizarre, horrible bugs.

It's an interesting area theoretically, but the more you dig around the more you see why anything along these lines is a bad idea!

Speaking outside of string, there's no performance enhancements I know of for an object being immutable (indeed I don't think the JVM can even tell at the moment whether an object is immutable, reflection attacks aside.) It could throw things like the checker-framework off though or anything that tries to statically analyse the code to guarantee it's immutable.

Michael Berry
  • 70,193
  • 21
  • 157
  • 216
2

I'm pretty sure The JVM itself makes no assumptions about the immutability of Strings, as "immutability" in Java is not a language-level construct; it's a trait implied by a class's implementation, but cannot, as you note, be actually guaranteed in the presence of reflection. Thus, it also shouldn't be relevant to performance.

However, pretty much all Java code in existence (including the Standard API implementation) relies on Strings being immutable, and if you break that expectation, you'll see all kinds of bugs.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
0
public static void main(String args[]){
    String a = "test213";
    String s = new String("test213");
    try {
        System.out.println(s);
        System.out.println(a);
        char[] value = (char[])getFieldValue(s, "value");
        value[1] = 'a';
        System.out.println(s);
        System.out.println(a);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

static Object getFieldValue(String s,String fieldName) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
    Object chars = null;
    Field innerCharArray = String.class.getDeclaredField(fieldName);
    innerCharArray.setAccessible(true);
    chars = innerCharArray.get(s);
    return chars;
}

Changing value of S will change the literal of a as mentioned in all answers.

Mani
  • 3,274
  • 2
  • 17
  • 27
0

To demonstrate how can it screw up a program:

System.out.print("Initial: "); System.out.println(addr);
editIntStr("ADDR_PLACEH", "192.168.1.1");
System.out.print("From var: "); System.out.println(addr);//
System.out.print("Hardcoded: "); System.out.println("ADDR_PLACEH");
System.out.print("Substring: "); System.out.println("ADDR_PLACE" + "H".substring(0));
System.out.print("Equals test: "); System.out.println("ADDR_PLACEH".equals("192.168.1.1"));
System.out.print("Equals test with substring: ");  System.out.println(("ADDR_PLACE" + "H".substring(0)).equals("192.168.1.1"));

Output:

Initial: ADDR_PLACEH
From var: 192.168.1.1
Hardcoded: 192.168.1.1
Substring: ADDR_PLACEH
Equals test: true
Equals test with substring: false

The result of the first Equals test is weird, isn't it? You can't expect your fellow programmers to figure out why is Java thinking they are equal...
Full test code: http://pastebin.com/vbstfWX1

szgal
  • 66
  • 1
  • 4
0

The private fields in the String class are the char[], the offset and length. Changing any of these should not have any adverse effect on any other object. But if you can somehow change the contents of the char[], then you could probably see some surprising side effects.

arun_suresh
  • 2,875
  • 20
  • 20
  • I think that's what the OP is asking about: changing the *contents* of the internal `char[]` array. – Dan Tao Aug 03 '11 at 20:38
  • 1
    If the char[] is changed then the result of `hashCode()` will be different. Any attempt to recover the string from a Hash based collection will probably fail. – rossum Aug 03 '11 at 20:58
  • @rossum: Actually, a `String` instance caches its own hash code so, for example, a call to `containsKey` with the same string object would actually succeed; the bizarre part is that a call with an *identical* but separate *instance* would fail. – Dan Tao Aug 03 '11 at 22:06
  • @Dan Tao: some implementations may cache, others may not. It is dangerous to rely on specific implementation details. – rossum Aug 04 '11 at 00:14
  • @rossum: Preaching to the choir, my friend. I was just trying to make an interesting (albeit implementation-specific) observation. – Dan Tao Aug 04 '11 at 00:27