230

次の例を考えてみましょう。

String str = new String();

str  = "Hello";
System.out.println(str);  //Prints Hello

str = "Help!";
System.out.println(str);  //Prints Help!

現在、Java では、String オブジェクトは不変です。では、どうしてオブジェクトstrに「ヘルプ!」という値を割り当てることができるのでしょうか。これは、Java における文字列の不変性に矛盾していませんか? 誰でも不変性の正確な概念を説明してもらえますか?

編集:

Ok。私は今それを手に入れていますが、フォローアップの質問は1つだけです。次のコードはどうでしょうか。

String str = "Mississippi"; 
System.out.println(str); // prints Mississippi 

str = str.replace("i", "!"); 
System.out.println(str); // prints M!ss!ss!pp! 

これは、2 つのオブジェクト ("Mississippi" と "M!ss!ss!pp!") が再度作成され、参照がメソッドstrの後に別のオブジェクトを指していることを意味しますか?replace()

4

26 に答える 26

336

strオブジェクトではなく、オブジェクトへの参照です。"Hello""Help!"は 2 つの異なるStringオブジェクトです。したがって、文字列str を指します。それが指すものは変更できますが、それが指すもの変更できません。

たとえば、次のコードを使用します。

String s1 = "Hello";
String s2 = s1;
// s1 and s2 now point at the same string - "Hello"

さて、の値に影響を与える1に対してできることは何もありません。これらは同じオブジェクト (文字列) を参照しますが、そのオブジェクトは不変であるため、変更することはできません。s1s2"Hello"

次のようにすると:

s1 = "Help!";
System.out.println(s2); // still prints "Hello"

ここで、オブジェクトの変更と参照の変更の違いがわかります。s2最初に指し示すように設定したものと同じオブジェクトをs1指しています。に設定s1すると、参照"Help!"のみが変更され、最初に参照されていたオブジェクトは変更されません。String

文字列変更可能な場合、次のようなことができます。

String s1 = "Hello";
String s2 = s1;
s1.setCharAt(1, 'a'); // Fictional method that sets character at a given pos in string
System.out.println(s2); // Prints "Hallo"

OPの編集に対応するための編集:

String.replace(char,char)のソース コード ( JDK インストール ディレクトリの src.zip にもあります。プロのヒントは、何かが実際にどのように機能するのか疑問に思うときはいつでもそこを見ることです) のソース コードを見ると、何がわかるかがわかります。それは次のとおりです。

  • 現在の文字列に が 1 つ以上出現する場合はoldChar、現在の文字列のコピーを作成し、すべての出現箇所をoldCharに置き換えnewCharます。
  • が現在の文字列に存在しない場合はoldChar、現在の文字列を返します。

はい、"Mississippi".replace('i', '!')新しいStringオブジェクトを作成します。ここでも、次のことが成り立ちます。

String s1 = "Mississippi";
String s2 = s1;
s1 = s1.replace('i', '!');
System.out.println(s1); // Prints "M!ss!ss!pp!"
System.out.println(s2); // Prints "Mississippi"
System.out.println(s1 == s2); // Prints "false" as s1 and s2 are two different objects

ここでの宿題は、次のように変更した場合に上記のコードがどのように動作するかを確認することs1 = s1.replace('i', '!');ですs1 = s1.replace('Q', '!');:)


1実際には、文字列 (およびその他の不変オブジェクト) を変更することが可能です。これにはリフレクションが必要であり、非常に危険であり、実際にプログラムを破壊することに興味がない限り、絶対に使用しないでください。

于 2009-10-12T07:04:59.453 に答える
25

参照するオブジェクトstrは変更できますが、実際のStringオブジェクト自体は変更できません。

文字列をString含むオブジェクトは値を変更できないため、不変です。"Hello""Help!"

オブジェクトの不変性は、Stringオブジェクトを指す参照が変更できないという意味ではありません。

参照が変更されないようにする方法の 1 つは、次のstrように宣言することfinalです。

final String STR = "Hello";

Stringここで、別のを割り当てようとSTRすると、コンパイル エラーが発生します。

于 2009-10-12T01:24:27.847 に答える
10

Light_handle Cup Size を読むことをお勧めします-- 変数Pass-by-Value についての話 Please (Cup Size 続き) . これは、上記の投稿を読むときに非常に役立ちます。

それらを読みましたか?はい。良い。

String str = new String();

これにより、「 」という新しい「リモコン」が作成strされ、値new String()(または"") が設定されます。

たとえば、メモリ内に次のものが作成されます。

str --- > ""

str  = "Hello";

これにより、リモコン " str" が変更されますが、元の文字列は変更されません""

たとえば、メモリ内に次のものが作成されます。

str -+   ""
     +-> "Hello"

str = "Help!";

これにより、リモコン " " が変更されますが、リモコンが現在指しているstr元の文字列またはオブジェクトは変更されません。""

たとえば、メモリ内に次のものが作成されます。

str -+   ""
     |   "Hello"
     +-> "Help!"
于 2009-10-12T10:48:16.250 に答える
6

最初に参照された文字列オブジェクトstrは変更されておらずstr、新しい文字列オブジェクトを参照するようにしただけです。

于 2009-10-12T01:25:23.807 に答える
5

質問の置換部分については、これを試してください:

String str = "Mississippi"; 
System.out.println(str); //Prints Mississippi 

String other = str.replace("i", "!"); 
System.out.println(str); //still prints Mississippi 
System.out.println(other);  // prints M!ss!ss!pp!
于 2009-10-12T19:48:58.923 に答える
5

文字列は変更されませんが、それへの参照は変更されます。不変性とフィールドの概念を混同していfinalます。フィールドが として宣言されfinalている場合、一度割り当てられると、再割り当てできません。

于 2009-10-12T01:26:09.217 に答える
4

Java はそれを無視しようとしますstrが、ポインターにすぎません。これは、最初に を記述するときに、 を指すstr = "Hello";オブジェクトを作成することを意味します。を記述しstrて再割り当てすると、新しいオブジェクトが作成され、古いオブジェクトは Java が必要に応じていつでもガベージ コレクションを取得します。strstr = "Help!";"Hello"

于 2009-10-12T01:28:50.697 に答える
3

String クラスは不変であり、不変オブジェクトの値を変更することはできません。ただし、文字列の場合、文字列の値を変更すると、文字列プールに新しい文字列が作成され、古い値ではなくその値への文​​字列参照が作成されます。したがって、文字列は不変です。あなたの例を見てみましょう。

String str = "Mississippi";  
System.out.println(str); // prints Mississippi 

1 つの文字列"Mississippi"が作成され、それが String プールに追加されるため、str は Mississippi を指しています。

str = str.replace("i", "!");  
System.out.println(str); // prints M!ss!ss!pp! 

しかし、上記の操作の後、別の文字列「M!ss!ss!pp!」が作成されます。 そしてそれは文字列プールに追加されます。そして今、str はミシシッピではなく M!ss!ss!pp! を指しています。

このようにして、文字列オブジェクトの値を変更すると、もう 1 つのオブジェクトが作成され、文字列プールに追加されます。

もう1つ例を挙げましょう

String s1 = "Hello"; 
String s2 = "World"; 
String s = s1 + s2;

上記の 3 行は、文字列の 3 つのオブジェクトを文字列プールに追加します。
1) ハロー
2) ワールド
3 ) ハローワールド

于 2016-06-23T12:15:35.953 に答える
3

不変性とは、インスタンス化されたオブジェクトの値が変更できないことを意味します。「Hello」を「Help!」に変えることはできません。

変数 str はオブジェクトへの参照です。新しい値を str に割り当てると、それが参照するオブジェクトの値を変更するのではなく、別のオブジェクトを参照しています。

于 2009-10-12T01:28:45.280 に答える
2

ライナス・トルバーズが言ったように:

口で言うだけなら簡単です。コードを見せて

これを見てください:

public class Test{
    public static void main(String[] args){

        String a = "Mississippi";
        String b = "Mississippi";//String immutable property (same chars sequence), then same object

        String c = a.replace('i','I').replace('I','i');//This method creates a new String, then new object
        String d = b.replace('i','I').replace('I','i');//At this moment we have 3 String objects, a/b, c and d

        String e = a.replace('i','i');//If the arguments are the same, the object is not affected, then returns same object

        System.out.println( "a==b? " + (a==b) ); // Prints true, they are pointing to the same String object

        System.out.println( "a: " + a );
        System.out.println( "b: " + b );

        System.out.println( "c==d? " + (c==d) ); // Prints false, a new object was created on each one

        System.out.println( "c: " + c ); // Even the sequence of chars are the same, the object is different
        System.out.println( "d: " + d );

        System.out.println( "a==e? " + (a==e) ); // Same object, immutable property
    }
}

出力は

a==b? true
a: Mississippi
b: Mississippi
c==d? false
c: Mississippi
d: Mississippi
a==e? true

したがって、次の 2 つのことを覚えておいてください。

  • 新しい文字列を操作して作成するメソッドを適用するまで、文字列は不変です (c & d ケース)。
  • 両方のパラメーターが同じ場合、Replace メソッドは同じ String オブジェクトを返します。
于 2016-09-14T03:46:52.613 に答える
2

使用する:

String s = new String("New String");
s.concat(" Added String");
System.out.println("String reference -----> "+s); // Output: String reference -----> New String

ここに表示されている場合、concatメソッドを使用して元の文字列、つまり「新しい文字列」を「追加された文字列」という文字列で変更しますが、それでも以前と同じように出力されているため、オブジェクトの参照を変更できないことがわかりますString クラスのことですが、StringBuilder クラスでこれを行うとうまくいきます。以下に記載します。

StringBuilder sb = new StringBuilder("New String");
sb.append(" Added String");
System.out.println("StringBuilder reference -----> "+sb);// Output: StringBuilder reference -----> New String Added String
于 2016-02-06T12:06:10.917 に答える
1

Javaで文字列の不変性を破る方法を知りたい人のために...

コード

import java.lang.reflect.Field;

public class StringImmutability {
    public static void main(String[] args) {
        String str1 = "I am immutable";
        String str2 = str1;

        try {
            Class str1Class = str1.getClass();
            Field str1Field = str1Class.getDeclaredField("value");

            str1Field.setAccessible(true);
            char[] valueChars = (char[]) str1Field.get(str1);

            valueChars[5] = ' ';
            valueChars[6] = ' ';

            System.out.println(str1 == str2);
            System.out.println(str1);
            System.out.println(str2);           
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}

出力

true
I am   mutable
I am   mutable
于 2015-04-24T11:12:31.667 に答える
0

文字列不変です。つまり、参照のみを変更できます。


String a = "a";
System.out.println("String a is referencing to "+a); // Output: a

a.concat("b");
System.out.println("String a is referencing to "+a); // Output: a

a = a.concat("b");
System.out.println("String a has created a new reference and is now referencing to "+a); // Output: ab
于 2016-01-05T09:23:54.367 に答える
0

または、次を試すことができます。

public class Tester
{
public static void main(String[] args)
{
 String str = "Mississippi"; 
 System.out.println(str); // prints Mississippi 
 System.out.println(str.hashCode());

 str = str.replace("i", "!"); 
 System.out.println(str); // prints M!ss!ss!pp! 
 System.out.println(str.hashCode());
 }
 }

これにより、ハッシュコードがどのように変化するかが示されます。

于 2016-08-22T13:33:44.643 に答える
0

Java では、通常、オブジェクトは参照によってアクセスされます。あなたのコードでは、 str は最初に "Hello" (自動作成されたオブジェクトまたは定数プールからフェッチされたオブジェクト) に割り当てられた参照であり、次に別のオブジェクト "Help!" を割り当てました。同じ参照に。注意点として、参照は同じで変更されていますが、オブジェクトは異なります。コードでもう 1 つ、3 つのオブジェクトにアクセスしました。

  1. new String() を呼び出したとき。
  2. 「こんにちは」を割り当てたとき。
  3. 「助けて!」を割り当てたとき。

new String() を呼び出すと、文字列プールに存在する場合でも新しいオブジェクトが作成されるため、通常は使用しないでください。new String () から作成された文字列を文字列プールに入れるには、intern()メソッドを試すことができます。

これが役立つことを願っています。

于 2015-08-31T14:48:55.700 に答える
0

が String の場合、 に変更するHELLOことはできません。この性質を不変性といいます。HELLOHILLO

HELLO 文字列を指す複数のポインタ文字列変数を持つことができます。

ただし、HELLO が char 配列の場合、HELLO を HILLO に変更できます。例えば、

char[] charArr = 'HELLO';
char[1] = 'I'; //you can do this

プログラミング言語には、キーと値のペアのキーとして使用できるように、不変のデータ変数があります。

于 2018-07-20T20:53:26.673 に答える
-1

Immutable および Final の Java の文字列は、変更または修正できないことを意味します。

ケース 1:

class TestClass{  
 public static void main(String args[]){  
   String str = "ABC";  
   str.concat("DEF");  
   System.out.println(str);  
 }  
} 

出力: ABC

理由: オブジェクト参照 str は変更されません。実際には、新しいオブジェクト "DEF" がプール内に作成され、参照がまったくありません (つまり、失われます)。

ケース 2:

class TestClass{  
 public static void main(String args[]){  
   String str="ABC";  
   str=str.concat("DEF");  
   System.out.println(str);  
 }  
}  

出力: ABCDEF

理由: この場合、str は現在、新しいオブジェクト "ABCDEF" を参照しているため、ABCDEF が出力されます。つまり、以前の str オブジェクト "ABC" がプール内で参照なしで失われています。

于 2016-09-23T06:35:22.987 に答える