2016-07-04 7 views
-4

下のクラスの出力は本当に私に衝撃を与えましたが、どうなるか理解できません。なぜ、下のクラスの出力が正常でなかったのですか?

public class SampleTest { 

    public static void main(String[] args) throws SecurityException, NoSuchFieldException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException { 

     String someString = "IMMUTABLE"; 
     Field field = Class.forName("java.lang.String").getDeclaredField("value"); 
     field.setAccessible(true); 
     char[] value = (char []) field.get(someString); 
     String anotherString = "NOTREALLY"; 
     for (int i=0; i<value.length; i++){ 
      char c = anotherString.toCharArray()[i]; 
      value[i]=c; 
     } 

     System.out.println(someString); // prints NOTREALLY 
     System.out.println("IMMUTABLE"); // Why it prints NOTREALLY here..!!! 

    } 

} 
+0

あなたは不変性を壊すために反省を払っています - あなたはどんな結果を期待していますか? 'setAccessible(true)'を使うと、アクセスできないプライベートフィールドにアクセス可能になります。配列内の値を変更することで、文字列を変更することができます。 –

答えて

0

TL; DR:はそれをしないでください。そのコードは、(ab)リフレクションを使用して仕様に違反しています。 JLSによると、String values cannot change。そのコードは、文書化されていない私的な内部構造にアクセスすることによって文字列の値を変更します。悪いアイデア。何が起こっている

:実行時に、それらはすべてメモリ内の同じ文字列オブジェクトを参照するようにクラスの

等価文字列リテラルは、すべての結合され;クラスの「定数プール」に書き込まれます。したがって、そのクラスの"IMMUTABLE"のすべての出現は、実際には同じインスタンスStringを参照します。これは、クラスがロードされるとリテラルと変数が同じオブジェクトを参照するので効果で、最後の行

System.out.println("IMMUTABLE"); 

ことを... ...

System.out.println(someString); 

と同等であることを意味しています。

コード(ab)はリフレクションを使用して文字列オブジェクトの非公開の非公開値を上書きするため、オブジェクトが使用されているすべての場所でその更新された状態が見えます。あなたが別のクラスにあなたのSystem.out.println("IMMUTABLE");ラインを移動し、あなたのコードはsomeStringを修正前にそのクラスがロードされることを保証した場合にも起こる

。これは、クラスがロードされると、定数プール内の文字列がinternedになるため、異なるクラスの同等の文字列定数が同じStringオブジェクトを参照するようになります。これはあなたが頼りにしたいことではありません。ストリングは(最近)インターンプールから逃げ出すことができるからですが、単純なケースではおそらくそれを観察するでしょう。例えばので

、あなたはこの別のクラスだったとします

public class Separate { 
    public static void show() { 
     System.out.println("IMMUTABLE"); 
    } 
} 

をそして、あなたはそれを使用するようにコードを変更し、だけsomeStringを変更:

public static void main(String[] args) throws SecurityException, NoSuchFieldException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException { 
    String someString = "IMMUTABLE"; 
    // ... 

    System.out.println(someString); // prints NOTREALLY 
    Separate.show(); 
} 

あなたが取得したいです

 
NOTREALLY 
IMMUTABLE 

...によってSeparateがロードされている場合は、インテルプールにある文字列のバージョンを既に変更しているので、Separateをロードすると一致しないため、別のStringオブジェクトを参照してしまいます。

しかし、あなたはSeparateは、あなたがそれを変更前にロードされていることを確認した場合:

public static void main(String[] args) throws SecurityException, NoSuchFieldException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException { 
    Separate.show(); // <================ Note 

    String someString = "IMMUTABLE"; 
    // ... 

    System.out.println(someString); // prints NOTREALLY 
    Separate.show(); 
} 

あなたが楽しいのこのビットを取得:

 
IMMUTABLE 
NOTREALLY 
NOTREALLY 

...Separateがロードされている時点で、その"IMMUTABLE"はインタープールのものと同等なので、Separatemainのコードが変更されるStringインスタンス  —を使用します。

関連する問題