1. ホーム

[解決済み】文字列は不変です。具体的にはどういうこと?[重複しています。]

2022-04-18 19:06:11

質問内容

イミュータブルなStringについて、次のようなコードを書いてみました。

public class ImmutableStrings {

    public static void main(String[] args) {
        testmethod();
    }

    private static void testmethod() {
        String a = "a";
        System.out.println("a 1-->" + a);
        a = "ty";
        System.out.println("a 2-->" + a);
    }
}

出力します。

a 1-->a  
a 2-->ty

ここでは、変数 a が変更されました(一方、多くの人は、immutableオブジェクトの内容は変更できないと言います)。しかし、次のような言い方は正確には何を意味するのでしょうか? String は不変である ? このトピックを明確にしていただけませんか?

ソース https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

解決方法は?

の大騒ぎで先に進む前に 不変性 について見てみましょう。 String クラスとその機能について、結論を出す前に少し説明します。

このように String が動作します。

String str = "knowledge";

これは、いつものように、以下を含む文字列が作成されます。 "knowledge" を参照し、それに str . 簡単でしょう?では、さらにいくつかの関数を実行してみましょう。

 String s = str;     // assigns a new reference to the same string "knowledge"

以下の文がどのように機能するか見てみましょう。

  str = str.concat(" base");

これは文字列を追加します " base"str . しかし、ちょっと待ってください、なぜこんなことが可能なのでしょうか? String オブジェクトは不変なのでしょうか?驚いたことに、そうなのです。

上記のステートメントが実行されると、VMはその値を String str すなわち "knowledge" を追加し、さらに " base" という値が得られます。 "knowledge base" . ここで String は不変なので、VM はこの値を str であるため、新しい String オブジェクトを作成し、そのオブジェクトに値 "knowledge base" という参照を与える。 str .

ここで重要な点は、このように String オブジェクトはイミュータブルです。 その参照変数はそうではありません。 そのため、上記の例では、参照先が新たに形成された String オブジェクトを作成します。

上の例では、この時点で2つの String オブジェクトを作成しました。 "knowledge" で指される s と、2つ目の "knowledge base" で指される str . しかし、厳密には、3つの String オブジェクトで、3つ目はリテラルの "base" の中に concat ステートメントを使用します。

文字列とメモリ使用量に関する重要な事実

もし、別の参照がなかったらどうなるか s から "knowledge" ? を失うことになる。 String . しかし、それはまだ存在していたはずですが、参照がないため失われたとみなされます。 もう一つ、次の例を見てください。

String s1 = "java";
s1.concat(" rules");
System.out.println("s1 refers to "+s1);  // Yes, s1 still refers to "java"

何が起きているのか

  1. 最初の行はとても簡単で、新しい String "java" を参照し s1 を追加してください。
  2. 次に、VM は別の新しい String "java rules" しかし、何も を参照しています。そこで、2番目の String は瞬時に失われる。到達できないのです となります。

参照変数 s1 はまだ元の String "java" .

ほとんどすべてのメソッドは String オブジェクトを変更するために、新しい String オブジェクトを作成します。では、これらの String オブジェクトはどこに行くのでしょうか?まあね。 プログラミング言語の重要な目標の1つは、メモリを効率的に使用することです。

アプリケーションが大きくなると というのはよくあることです。 String リテラルはメモリの大きな領域を占め、冗長性をもたらすことさえあります。 そこで、Javaをより効率的にするために JVMは、quot;String constant pool"と呼ばれる特別なメモリ領域を確保します。

コンパイラは String というリテラルがある場合、それは String をプール内に置く。一致するものが見つかった場合、新しいリテラルへの参照は、既存の String となり、新しい String オブジェクトが作成されます。既存の String は単に参照が1つ増えただけです。ここで String オブジェクトは不変である。

において String 定数プールに String オブジェクトは、1つまたは複数の参照を持つ可能性があります。 複数の参照先が同じ String を知らないうちに、そのリファレンスの1つがその String の値を指定します。そのため String オブジェクトはイミュータブルです。

さて、今ならこう言えるでしょう。 の機能を上書きしてしまったらどうなるのでしょうか? String クラスはどうでしょうか? そのため String クラスがマークされています。 final のように、誰もそのメソッドの動作をオーバーライドできないようにします。