JavaのStringオブジェクトは可変ですか?
友人が StackOverflow に質問を投稿して、こう尋ねました:
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 をバイパスします。この方法では、列挙型の値を変更したり、整数型がオートボックス化されるときに使用されるルックアップ テーブルを変更したりすることもできます。
ここで、s1とs2は同じ内部文字列オブジェクトを指しているため、両方の値が変更されます。他の回答で述べたように、これはコンパイラによって実装されます。
s3 が変更されない理由には、私は本当に驚きました。s3 と s1 は同じ値の配列を共有していると考えていました (Java 7u6 より前のバージョンでは実際にそうでした)。ただし、String クラスのソース コードを見ると、部分文字列オブジェクトの値配列が元の文字列オブジェクトから (Arrays.copyOfRange(..) メソッドを使用して) コピーされていることがわかります。これが、s3 が変更されない理由です。
SecurityManager をインストールすると、悪意のあるコードがこの種の操作を実行するのを防ぐことができます。ただし、一部のライブラリ (ORM ツール、AOP ライブラリなど) の実装はこのリフレクション手法に依存していることに注意してください。
返信の冒頭で、String オブジェクトは実際には不変ではなく、「不変に見える」だけであると書きました。これにより、読者は String クラスの現在のバージョンがアクセス制限に関して無視されていると誤解する可能性がありますが、実際には、値の配列は private 修飾子と Final 修飾子を使用しています。したがって、開発者は注意する必要があります。Java では配列を不変として宣言することはできません。また、正しいアクセス修飾子が使用されている場合でも、配列をクラスの外部に公開することはできません。
このトピックは非常に話題になっているため、いくつかの高度な読書をお勧めします。2009 年の JavaZone カンファレンスでの Heinz Kabutz のリフレクション テクノロジに関するクレイジーなスピーチ。この記事では、リフレクション操作における一般的な問題と、リフレクション テクノロジに関するその他の内容について説明します。この記事は非常に優れており、非常にクレイジーです。
この記事では、特定のシナリオではリフレクション手法が役立つ理由を説明しますが、ほとんどの場合は使用を避けるべきです。
回答 #2:
Java では、String 型の 2 つの変数が同じ文字列に初期化されると、両方の変数に同じオブジェクト参照が割り当てられます。これが、式「Test1==Test2」が true を返す理由です。
String Test1="Hello World"; String Test2="Hello World"; System.out.println(test1==test2); // true
Test3 は、substring() メソッドによって作成された新しい String オブジェクトであり、Test1 と同じ値の配列を共有しません。 (注: オリジナル作成者の事務ミスにより、下の図の変数 test1 と test3 の最初の文字が大文字になっていません。読者の皆様はご注意ください。)
リフレクション技術を通じて String オブジェクトにアクセスし、値配列のポインタ:
Field field = String.class.getDeclaredField("value"); field.setAccessible(true);
Change この値配列の値は、配列ポインタを保持するすべての String オブジェクトの値を変更できるため、Test1 と Test2 の値が変更されています。ただし、Test3 は substring() メソッドによって作成された新しい String オブジェクトであるため、その値は変更されていません。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









Java の乱数ジェネレーターのガイド。ここでは、Java の関数について例を挙げて説明し、2 つの異なるジェネレーターについて例を挙げて説明します。

Java の Weka へのガイド。ここでは、weka java の概要、使い方、プラットフォームの種類、利点について例を交えて説明します。

この記事では、Java Spring の面接で最もよく聞かれる質問とその詳細な回答をまとめました。面接を突破できるように。

Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

Java での日付までのタイムスタンプに関するガイド。ここでは、Java でタイムスタンプを日付に変換する方法とその概要について、例とともに説明します。

カプセルは3次元の幾何学的図形で、両端にシリンダーと半球で構成されています。カプセルの体積は、シリンダーの体積と両端に半球の体積を追加することで計算できます。このチュートリアルでは、さまざまな方法を使用して、Javaの特定のカプセルの体積を計算する方法について説明します。 カプセルボリュームフォーミュラ カプセルボリュームの式は次のとおりです。 カプセル体積=円筒形の体積2つの半球体積 で、 R:半球の半径。 H:シリンダーの高さ(半球を除く)。 例1 入力 RADIUS = 5ユニット 高さ= 10単位 出力 ボリューム= 1570.8立方ユニット 説明する 式を使用してボリュームを計算します。 ボリューム=π×R2×H(4
