[解決済み] Java の文字列は本当に不変なのですか?
質問
私たちは皆、次のことを知っています。
String
はJavaでは不変ですが、次のコードを確認してください。
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
?
解決方法は?
String
は immutable* ですが、これは public API を使って変更できないことを意味するだけです。
ここでやっていることは、リフレクションを使って、通常のAPIを回避しているのです。同じように、enumの値を変更したり、Integer autoboxingで使用されるルックアップテーブルを変更したりすることができます。
では、その理由
s1
と
s2
は、どちらも同じ内部文字列を参照しているということです。コンパイラはこれを行います(他の回答でも言及されています)。
その理由は
s3
が行います。
ない
を共有すると思っていたので、実はちょっと意外でした。
value
配列(
以前のバージョンのJavaでは
Java 7u6 より前のバージョン)。しかし
String
を使用すると
value
の文字配列が実際にコピーされます。
Arrays.copyOfRange(..)
). そのため、変更されないのです。
をインストールすることができます。
SecurityManager
のように、悪意のあるコードがそのようなことをしないようにするためです。しかし、いくつかのライブラリは、この種の反射トリックの使用に依存していることに留意してください(典型的なORMツール、AOPライブラリなど)。
*) 私は最初
String
は実際にはイミュータブルではなく、単に"effective immutable"というだけです。の現在の実装では、これは誤解を招くかもしれません。
String
というのは
value
の配列は、確かに
private final
. しかし、Javaでは配列を不変と宣言する方法がないため、適切なアクセス修飾子をつけても、クラスの外に公開しないように注意する必要があることは知っておく価値があります。
このトピックは圧倒的に人気があるようなので、さらにお勧めの記事を紹介します。 ハインツ・カブツの「リフレクション・マッドネス」トーク JavaZone 2009の記事で、OPの問題の多くと、他のリフレクションの...その...狂気を取り上げています。
なぜこの方法が役に立つのか、その理由を説明しています。そして、ほとんどの場合、それを避けるべき理由も。)
関連
-
java 例外。Javaツールの初期化
-
[解決済み] JavaScriptで文字列が部分文字列を含むかどうかを確認する方法は?
-
[解決済み] C#のStringとstringの違いは何ですか?
-
[解決済み] JavaでInputStreamを読み込んでStringに変換するにはどうすればよいですか?
-
[解決済み] JavaでStringをintに変換するにはどうしたらいいですか?
-
[解決済み] バイトを文字列に変換する
-
[解決済み] Javaはパラメータのデフォルト値をサポートしていますか?
-
[解決済み] 特定のUnicode文字を含むコメントでのJavaコードの実行が許可されているのはなぜですか?
-
[解決済み】JavaScriptで文字列の出現箇所をすべて置換する方法
-
[解決済み】大文字・小文字を区別しない「Contains(string)
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
springboot project MIMEタイプ text/htmlで転送された静的ファイルを読み込む。
-
アクセス制限です。タイプ 'Application' は API ではありません。
-
eclipse アクセス制限です。タイプ 'xxx' は API ではありません(必須ライブラリ '' の制限)。
-
Dateが型に解決できない問題を解決する
-
をインスタンス化することができません。
-
Javaクラスローダーにソースコードから潜り込む
-
spring-boot 401 このリソースにアクセスするには完全な認証が必要です エラー解決
-
javax.net.ssl.SSLException: 読み取りエラー: ssl=0xdeae5100: システムコール中の I/O エラー、接続 res
-
IDEA パッケージステートメントの欠落
-
[解決済み】Javaにおける文字列の不変性