12

ほとんどの人が知っているように、Java の文字列は不変です。最近、それが常に正しいとは限らないことを示唆するものを発見しました。このコードを試してみましょう:

System.out.println("-------- BEFORE MODIFICATIONS --------");
String beforeTest = new String("Original");
System.out.println(beforeTest);
java.lang.reflect.Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
valueField.set("Original", "Modified".toCharArray());
System.out.println("-------- AFTER MODIFICATIONS --------");
System.out.println(beforeTest);
System.out.println("Original");
String test = new String("Original");
System.out.println(test);
String test2 = new String("Original 2");
System.out.println(test2);

出力は次のようになります。

-------- BEFORE MODIFICATIONS --------
Original
-------- AFTER MODIFICATIONS --------
Original
Modified
Modified
Original 2

このトリックはどのように機能しますか? JVM はどのオブジェクトを変更する必要があり、どのオブジェクトを変更しないかをどのように判断しますか? このトリックの背後にあるメカニズムは何ですか? すでに作成されたbeforeTest文字列が変更されなかったのはなぜですか? このトリックは本当にstrings are immutable原則を軽視しているのでしょうか?

4

1 に答える 1

18

文字列リテラルはプールにインターンされます。これはあなたが書くとき

String s1 = "Foo";
String s2 = "Foo";
String s3 = new String("Foo");

s1とs2は同じStringオブジェクトを参照し、s3は別のchar配列に基づく別のオブジェクトを参照します。

コードでは、「元の」文字列リテラルインスタンスの文字を保持するプライベートchar配列を変更することにより、文字列の不変条件に違反しています。ただし、beforeTest別のStringインスタンスを参照しているため、変更されません。

不変性は、フィールドをオブジェクトに対してプライベートに保ち、このプライベート状態を変更するメソッドを提供しないことによって実現されます。リフレクションを使用すると、カプセル化のすべてのルールに違反するため、不変性に違反する可能性があります。

于 2012-06-22T22:04:39.347 に答える