2012-06-22 4 views
11

ほとんど誰もがJavaの文字列を知っているので、変更できません。最近、私はそれが常に真実ではないことを示唆するかもしれない何かを発見しました。のは、このコードを試してみましょう: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の原則を逸脱していますか?

+6

反射は黒い魔法のブードーです。 –

+1

@HovercraftFullOfEels、Reflectionは完全に定義されています。これは、 'setAccessible'を呼び出すことによって' private'に違反したときにのみ、コアクラスの不変条件がウィンドウ外に出ます。 –

+3

@MikeSamuel Reflection *自体はよく定義されています。 *使用していないので、一度unmuckable開始moodingブードー。私はこれのための全体的な枠組みを持っています(Muckito)。 –

答えて

17

文字列リテラルは、プールに収容されています。これは

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

S1およびS2同じ文字列オブジェクトを参照を書くときことを意味し、S3は別のchar配列によって裏付け別のものを指します。

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

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

+0

3番目のインスタンス(テスト参照)も新しいインスタンスを指していませんか? "Original"のリテラルcharが "Modified"に置き換えられた場合、beforeTest値は更新されません(同じリテラルが渡されるため)。 – kosa

+0

'test'変数は、「元の」文字列リテラルのコピーに初期化されます。しかし、コピーが作成されたときに、その内容を「変更済み」に変更しました。それは "Modified"のコピーです。 'beforeTest'も" Original "Stringリテラルのコピーですが、その内容を変更する前にコピーが作成されています。 –