Java 文字列は本当に不変ですか? 質問する

Java 文字列は本当に不変ですか? 質問する

Java では不変であることは誰もが知っていますStringが、次のコードを確認してください。

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  

s1このプログラムはなぜこのように動作するのでしょうか? また、との値s2は変更されるのに、 の値は変更されないのはなぜでしょうかs3?

ベストアンサー1

String不変*ですが、これはパブリック API を使用して変更できないことを意味するだけです。

ここで行っているのは、リフレクションを使用して通常の API を回避することです。同様に、列挙型の値を変更したり、整数オートボクシングで使用されるルックアップ テーブルを変更したりすることもできます。

さて、理由s1s2値の変更は、両方とも同じインターンされた文字列を参照することです。コンパイラはこれを実行します(他の回答で言及されているように)。

理由は、配列を共有すると思っていたので、私にとってはちょっと意外s3でしvalue以前のバージョンのJavaではそうだった(Java 7u6 より前)ただし、 のソース コードを見ると、部分文字列の文字配列が実際にはコピーされているStringことがわかります( を使用)。これが変更されない理由です。valueArrays.copyOfRange(..)

をインストールすることで、悪意のあるコードがこのようなことを行うのを防ぐことができますSecurityManager。ただし、一部のライブラリは、このようなリフレクション トリックの使用に依存していることに注意してください (通常は ORM ツール、AOP ライブラリなど)。

*) 当初、 はString実際には不変ではなく、「実質的に不変」であるだけだと書きました。これは、配列が実際に とマークされているStringの現在の実装では誤解を招く可能性があります。ただし、Java では配列を不変として宣言する方法がないため、適切なアクセス修飾子を使用しても、配列をそのクラスの外部に公開しないように注意する必要があることに注意してください。valueprivate final


このトピックは圧倒的な人気があるようなので、さらに読むべき文献をいくつか紹介します。ハインツ・カブツのリフレクション・マッドネス講演JavaZone 2009 から、OP の多くの問題とその他の反省点...まあ...狂気について取り上げています。

これは、なぜ時々役に立つのか、そしてなぜほとんどの場合、これを避けるべきなのかを説明します。:-)

おすすめ記事