86

Java 文字列プールをリフレクションと組み合わせると、Java で想像を絶する結果が生じる可能性があります。

import java.lang.reflect.Field;

class MessingWithString {
    public static void main (String[] args) {
        String str = "Mario";
        toLuigi(str);
        System.out.println(str + " " + "Mario");
    }

    public static void toLuigi(String original) {
        try {
            Field stringValue = String.class.getDeclaredField("value");
            stringValue.setAccessible(true);
            stringValue.set(original, "Luigi".toCharArray());
        } catch (Exception ex) {
            // Ignore exceptions
        }
    }
}

上記のコードは以下を出力します:

"Luigi Luigi" 

マリオはどうした?

4

7 に答える 7

98

マリオどうした??

基本的に、あなたはそれを変更しました。はい、リフレクションを使用すると、文字列の不変性に違反する可能性があります...文字列のインターンにより、「マリオ」の使用はすべて終了します(コンパイル時に解決されるより大きな文字列定数式を除く)。プログラムの残りの部分で「ルイージ」としてアップします。

この種のことは、リフレクションがセキュリティ権限を必要とする理由です...

str + " " + "Mario"の左結合性により、式はコンパイル時の連結を実行しないことに注意してください+。それは事実上(str + " ") + "Mario"、それがあなたがまだ見る理由ですLuigi Luigi。コードを次のように変更した場合:

System.out.println(str + (" " + "Mario"));

Luigi Mario...その後、コンパイラが" Mario"別の文字列にインターンしたことがわかります"Mario"

于 2015-09-17T06:34:28.500 に答える
24

ルイージにセットしました。Java の文字列は不変です。したがって、コンパイラはすべての言及を"Mario"同じ文字列定数プール項目 (大まかに言えば「メモリの場所」) への参照として解釈できます。リフレクションを使用してそのアイテムを変更しました。あなたのコードのすべて"Mario"は、あなたが書いたかのようになりました"Luigi"

于 2015-09-17T06:35:07.800 に答える
9

文字列リテラルは文字列プールに格納され、その正規値が使用されます。両方の"Mario"リテラルは、同じ値を持つ単なる文字列ではなく、同じオブジェクトです。それらの 1 つを (リフレクションを使用して) 操作すると、それらは同じオブジェクトへの 2 つの参照にすぎないため、「両方」が変更されます。

于 2015-09-17T06:35:30.580 に答える
8

複数の によって参照されていたString 定数プールStringのを変更したので、すべての参照リテラルは になりました。 MarioLuigiString MarioLuigi

Field stringValue = String.class.getDeclaredField("value");

char[]名前付きvalueフィールドをクラスから取得しましたString

stringValue.setAccessible(true);

アクセスできるようにします。

stringValue.set(original, "Luigi".toCharArray());

original String フィールドを に変更しましたLuigi。しかし、オリジナルはリテラルMarioであり、リテラルはプールに属し、すべてがインターンされています。これは、同じ内容を持つすべてのリテラルが同じメモリ アドレスを参照することを意味します。String String

String a = "Mario";//Created in String pool
String b = "Mario";//Refers to the same Mario of String pool
a == b//TRUE
//You changed 'a' to Luigi and 'b' don't know that
//'a' has been internally changed and 
//'b' still refers to the same address.

基本的に、すべての参照フィールドに反映さStringれたプールのマリオを変更しました。リテラルの代わりに(ie )を作成すると、2 つの異なるsがあるため、この動作に直面することはありません。String Objectnew String("Mario")Mario

于 2015-09-17T06:50:30.577 に答える
5

他の答えは、何が起こっているのかを適切に説明しています。これは、セキュリティ マネージャがインストールされていない場合にのみ機能するという点を追加したかっただけです。コマンドラインからコードを実行する場合、デフォルトでは存在せず、このようなことができます。ただし、本番環境のアプリケーション サーバーやブラウザーのアプレット サンドボックスなど、信頼できるコードと信頼できないコードが混在する環境では、通常、セキュリティ マネージャーが存在し、このような悪ふざけは許されません。これは見た目ほどひどいセキュリティ ホールではありません。

于 2015-09-17T10:33:23.573 に答える