「テキスト」と新しい文字列(「テキスト」)の違いは何ですか?


195

次の2つのステートメントの違いは何ですか?

String s = "text";

String s = new String("text");


どなたでもお返事ください。文字列a = "Java"; 文字列b = "Java"; System.out.println(a == b); true //ただし、System.out.println( "a == b?" + a == b); // false ...
エネルギー

コメントを追加したときに理解できません( "a == b?)=>結果がFALSEになるのはなぜですか?
エネルギー

@Energy結果はfalse、演算の順序が+演算子を最初に実行し、「a == b?」を連結することを指示するためです。文字列「a == b?Java」を作成するa 次に、式"a==b?Java" == bはfalseと評価されます。
アリソンB

@AllisonBはそれを得た、どうもありがとう!
エネルギー

回答:


187

new String("text"); 明示的にStringオブジェクトの新しいインスタンスを明示的に作成します。文字列定数プールのString s = "text";インスタンスが利用可能な場合は、それを再利用できます。

あなたは非常にまれに、これまで使用したくないでしょうnew String(anotherString)コンストラクタを。APIから:

String(String original)新しく作成された Stringオブジェクトを初期化して、引数と同じ文字シーケンスを表すようにします。つまり、新しく作成された文字列は引数文字列のコピーです。オリジナルの明示的なコピーが必要でない限り、文字列は不変であるため、このコンストラクタの使用は不要です。

関連する質問


参照区別の意味

次のスニペットを調べます。

    String s1 = "foobar";
    String s2 = "foobar";

    System.out.println(s1 == s2);      // true

    s2 = new String("foobar");
    System.out.println(s1 == s2);      // false
    System.out.println(s1.equals(s2)); // true

==2つの参照タイプには、参照IDの比較があります。equals必ずしも必要ではない2つのオブジェクト====参照型で使用することは通常間違っています。ほとんどの場合、equals代わりに使用する必要があります。

それでも、何らかの理由で文字列ではequalsなく2つを作成する必要がある場合は、コンストラクタ使用できます。ただし、これは非常に特殊であり、意図されていることはめったにないことを再度述べる必要があります。==new String(anotherString)

参考文献

関連する問題


3
私が書いた場合:String s = new String( "abc"); そして今私は書く:String s = "abc"; ストリング= s = "abc"; 文字列プールに新しい文字列リテラルを作成しますか?
Kaveesh Kanwal

なぜ誰も前の質問に答えないのですか?
ゼッズ

2
@KaveeshKanwalいいえ、リテラルは複製されません。ご覧のとおり、2 "abc"秒あります。そのうちの1つだけが文字列プールに移動し、もう1つはそれを参照します。次に、s適切な新しいオブジェクトになるのがあります。
カヤマン

1
@Kaveesh Kanwal-String s = new String( "abc")は、値が "abc"の新しいStringオブジェクトのみを作成します。そして、2番目のステートメントは、「abc」文字列リテラルが文字列プールにすでに存在するかどうかをチェックします。すでに存在する場合、既存の参照への参照が返され、存在しない場合、新しいリテラル( "abc")が文字列プールに作成されます。それがあなたのクエリを解決することを願っています!!
user968813

それについての「可能性」はありません。コンパイラ文字列リテラルをプールする必要があります。JLS 3.10.5
ローン侯爵

119

文字列リテラル文字列定数プールに入ります。

以下のスナップショットは、それを視覚的に理解して、より長く覚えておくのに役立ちます。

ここに画像の説明を入力してください


行ごとのオブジェクト作成:

String str1 = new String("java5");

コンストラクタで文字列リテラル「java5」を使用すると、新しい文字列値が文字列定数プールに格納されます。new演算子を使用して、新しい文字列オブジェクトが値として「java5」を持つヒープに作成されます。

String str2 = "java5"

参照 "str2"は、文字列定数プールに既に格納されている値を指しています

String str3 = new String(str2);

「str2」による参照と同じ値を持つ新しい文字列オブジェクトがヒープに作成されます

String str4 = "java5";

参照 "str4"は、文字列定数プールに既に格納されている値を指しています

合計オブジェクト:ヒープ-2、プール-1

Oracleコミュニティの詳細


1
良い答えですが、今str1 = "java6"の値を変更するようになっているので、str4の値が変更されます。
CoronaPintu 2014年

2
はい、それがstr4の値を変更しないことを確認しました
CoronaPintu

@Braj Answerの主張のドキュメントを提供できますか?
バジルブルク2015

@ブラジ:テーブルの「ヒープ」と「プール」のヘッダーは逆になっているはずですか?
Rahul Kurup、2015

正しくありません。定数プールは、実行時ではなくコンパイル時に作成されます。引用されていないテキストには引用形式を使用しないでください。
ローン侯爵

15

文字列定数プールに文字列を作成します

String s = "text";

もう1つは、定数プールに文字列を作成し("text")、通常のヒープスペースに別の文字列を作成します(s)。両方の文字列は、「テキスト」と同じ値になります。

String s = new String("text");

s 後で未使用の場合は、失われます(GCの対象になります)。

一方、文字列リテラルは再利用されます。"text"クラスの複数の場所で使用する場合、実際には1つだけの文字列になります(つまり、プール内の同じ文字列への複数の参照)。


定数プール内の文字列が失われることはありません。後で使用しないと「s」が失われるということですか?
ローン侯爵

@EJP:はい、私は「s」を意味しました。お知らせいただきありがとうございます。質問を訂正します。

9

JLS

この概念は、JLSでは「インターン」と呼ばれています。

JLS 7 3.10.5からの関連箇所

さらに、文字列リテラルは常にStringクラスの同じインスタンスを参照します。これは、文字列リテラル、またはより一般的には定数式(§15.28)の値である文字列が、メソッドString.internを使用して一意のインスタンスを共有するように「インターン」されるためです。

例3.10.5-1。文字列リテラル

コンパイル単位(§7.3)で構成されるプログラム:

package testPackage;
class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.print((hello == "Hello") + " ");
        System.out.print((Other.hello == hello) + " ");
        System.out.print((other.Other.hello == hello) + " ");
        System.out.print((hello == ("Hel"+"lo")) + " ");
        System.out.print((hello == ("Hel"+lo)) + " ");
        System.out.println(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }

そしてコンパイルユニット:

package other;
public class Other { public static String hello = "Hello"; }

出力を生成します:

true true true true false true

JVMS

JVMS 7 5.1は言う

文字列リテラルは、クラスStringのインスタンスへの参照であり、クラスまたはインターフェースのバイナリ表現のCONSTANT_String_info構造(§4.4.3)から派生します。CONSTANT_String_info構造体は、文字列リテラルを構成するUnicodeコードポイントのシーケンスを提供します。

Javaプログラミング言語では、同一の文字列リテラル(つまり、同じコードポイントのシーケンスを含むリテラル)がクラスStringの同じインスタンスを参照する必要があります(JLS§3.10.5)。さらに、メソッドString.internが任意の文字列で呼び出された場合、結果は、その文字列がリテラルとして表示された場合に返されるのと同じクラスインスタンスへの参照になります。したがって、次の式の値はtrueでなければなりません。

("a" + "b" + "c").intern() == "abc"

文字列リテラルを導出するために、Java仮想マシンはCONSTANT_String_info構造体によって与えられたコードポイントのシーケンスを調べます。

  • メソッドString.internが、CONSTANT_String_info構造体によって与えられたものと同一のUnicodeコードポイントのシーケンスを含むクラスStringのインスタンスで以前に呼び出された場合、文字列リテラル導出の結果は、クラスStringの同じインスタンスへの参照になります。

  • それ以外の場合は、CONSTANT_String_info構造体で指定されたUnicodeコードポイントのシーケンスを含むStringクラスの新しいインスタンスが作成されます。そのクラスインスタンスへの参照は、文字列リテラルの派生の結果です。最後に、新しいStringインスタンスのインターンメソッドが呼び出されます。

バイトコード

OpenJDK 7でのバイトコードの実装を確認することも有益です。

逆コンパイルすると:

public class StringPool {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == c);
    }
}

定数プールにあります:

#2 = String             #32   // abc
[...]
#32 = Utf8               abc

main

 0: ldc           #2          // String abc
 2: astore_1
 3: ldc           #2          // String abc
 5: astore_2
 6: new           #3          // class java/lang/String
 9: dup
10: ldc           #2          // String abc
12: invokespecial #4          // Method java/lang/String."<init>":(Ljava/lang/String;)V
15: astore_3
16: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: aload_3
35: if_acmpne     42
38: iconst_1
39: goto          43
42: iconst_0
43: invokevirtual #7          // Method java/io/PrintStream.println:(Z)V

方法に注意してください:

  • 0and 3:同じldc #2定数がロードされます(リテラル)
  • 12:新しい文字列インスタンスが作成されます(#2引数として)
  • 35ac通常のオブジェクトとして比較されますif_acmpne

定数文字列の表現は、バイトコードでは非常に魅力的です。

  • 通常のオブジェクト(例)とは異なり、専用のCONSTANT_String_info構造を持っていますnew String
  • 構造体は、データを含むCONSTANT_Utf8_info構造体を指します。これは、文字列を表すために必要な唯一のデータです。

上記のJVMSの引用は、Utf8が同じである場合は常に、によって同一のインスタンスがロードされると述べているようですldc

私はフィールドに対して同様のテストを行いました、そして:

  • static final String s = "abc"ConstantValue属性を介して定数テーブルを指します
  • 非finalフィールドにはその属性はありませんが、 ldc

結論:文字列プールの直接バイトコードサポートがあり、メモリ表現が効率的です。

おまけ:それを、バイトコードを直接サポートしていない(アナログではない)Integerプールと比較してくださいCONSTANT_String_info


2

@ブラジ:私はあなたが逆に言及したと思います。私が間違っている場合は修正してください

行ごとのオブジェクト作成:

文字列str1 = new String( "java5")

   Pool- "java5" (1 Object)

   Heap - str1 => "java5" (1 Object)

文字列str2 = "java5"

  pool- str2 => "java5" (1 Object)

  heap - str1 => "java5" (1 Object)

文字列str3 =新しい文字列(str2)

  pool- str2 => "java5" (1 Object)

  heap- str1 => "java5", str3 => "java5" (2 Objects)

文字列str4 = "java5"

  pool - str2 => str4 => "java5" (1 Object)

  heap - str1 => "java5", str3 => "java5" (2 Objects)

str1値に関与していないstr2か、str3またはstr4どのような方法で。
ローン侯爵

1

(疑似)の"bla"ような魔法の工場であると考えてくださいStrings.createString("bla")。ファクトリは、この方法で作成されたすべての文字列のプールを保持します。

呼び出された場合、プールにこの値の文字列がすでに存在するかどうかを確認します。trueの場合、この文字列オブジェクトを返すため、この方法で取得した文字列は実際には同じオブジェクトです。

そうでない場合は、内部で新しい文字列オブジェクトを作成し、プールに保存してから返します。したがって、次回同じ文字列値がクエリされると、同じインスタンスが返されます。

手動で作成new String("")すると、文字列リテラルプールをバイパスすることでこの動作を上書きします。したがってequals()、オブジェクト参照の同等性ではなく、文字シーケンスを比較するを使用して、同等性を常に確認する必要があります。


あなたが言及する「マジックファクトリー」は、Javaコンパイラにほかなりません。このプロセスを実行時に発生したかのように記述するのは誤りです。
ローンの侯爵

1

違いを理解する簡単な方法の1つを以下に示します。

String s ="abc";
String s1= "abc";
String s2=new String("abc");

        if(s==s1){
            System.out.println("s==s1 is true");
        }else{
            System.out.println("s==s1 is false");
        }
        if(s==s2){
            System.out.println("s==s2 is true");
        }else{
            System.out.println("s==s2 is false");
        }

出力は

s==s1 is true
s==s2 is false

したがって、new String()は常に新しいインスタンスを作成します。


1

文字列リテラルは文字列リテラルプール内に作成され、プールは重複を許可しません。したがって、2つ以上の文字列オブジェクトが同じリテラル値で初期化される場合、すべてのオブジェクトは同じリテラルを指します。

String obj1 = "abc";
String obj2 = "abc";

「obj1」と「obj2」は同じ文字列リテラルを指し、文字列リテラルプールには「abc」リテラルが1つだけ含まれます。

新しいキーワードを使用してStringクラスオブジェクトを作成すると、作成された文字列はヒープメモリに格納されます。ただし、Stringクラスのコンストラクターにパラメーターとして渡される文字列リテラルは、文字列プールに格納されます。new演算子で同じ値を使用して複数のオブジェクトを作成する場合、この新しい演算子は回避する必要があるため、新しいオブジェクトは毎回ヒープに作成されます。

String obj1 = new String("abc");
String obj2 = new String("abc");

「obj1」と「obj2」はヒープ内の2つの異なるオブジェクトを指し、文字列リテラルプールには「abc」リテラルが1つだけ含まれます。

また、文字列の動作に関して注意する価値があるのは、文字列に対して行われた新しい割り当てまたは連結により、メモリ内に新しいオブジェクトが作成されることです。

String str1 = "abc";
String str2 = "abc" + "def";
str1 = "xyz";
str2 = str1 + "ghi";

ここで、上記の場合:
1行目: "abc"リテラルが文字列プールに格納されます。
2行目:「abcdef」リテラルは文字列プールに格納されます。
3行目:新しい「xyz」リテラルが文字列プールに格納され、「str1」がこのリテラルをポイントし始めます。
4行目:別の変数に追加することで値が生成されるため、結果はヒープメモリに格納され、「ghi」が追加されるリテラルは文字列プールに存在するかどうかがチェックされ、存在しないため作成されます上記の場合。


0

プログラマーの観点からは同じに見えますが、パフォーマンスに大きな影響があります。ほとんどの場合、最初の形式を使用する必要があります。


0
String str = new String("hello")

文字列定数プールに文字列「hello」がすでに含まれているかどうかを確認しますか?存在する場合、文字列定数プールにエントリを追加しません。存在しない場合は、文字列定数プールにエントリを追加します。

オブジェクトはヒープメモリ領域にstr作成され、ヒープメモリの場所に作成されたオブジェクトへの参照ポイントになります。

str文字列定数プールに含まれるポイントオブジェクトへの参照が必要な場合は、明示的に呼び出す必要がありますstr.intern();

String str = "world";

文字列定数プールに文字列「hello」がすでに含まれているかどうかを確認しますか?存在する場合、文字列定数プールにエントリを追加しません。存在しない場合は、文字列定数プールにエントリを追加します。

上記のどちらの場合も、str参照"world"は定数プールに存在する文字列を指します。


「それ」はJavaコンパイラです。文字列リテラルは、コンパイル時に定数プールに一意のエントリを作成します。それは...それは、実行時に発生したかのように、このプロセスをdeacribeする間違いです
ローン侯爵

この投稿の何が間違っているのか明確に説明していただけますか?
Jayesh、2016

この投稿で間違っているのは、すでに述べたように、文字列リテラルが完了時にプールされることです。あなたの答えのように、コードを実行するときではありません。
ローンの侯爵

@EJPご返信ありがとうございます。答えが間違っている正確な行を指摘してください。上記のすべての回答が私が書いたものと同じであることがわかります。助けてください、私は私の理解を訂正したいと思います。ありがとう。
Jayesh、2016年

プロセス全体について、コード行が実行されたときにすべてが行われるかのように書いてきました。これについては、繰り返し説明しましたが、そうではありません。あなたはそれをあなたの答えで間違っている単一の「正確な行」に減らすことはできません。
ローンの侯爵

0

文字列を次のように保存すると

String string1 = "Hello";

直接、JVMはString定数プールと呼ばれる別のメモリブロック中に、指定された価格でStringオブジェクトを作成します。

そして、次のように別の文字列を作成しようとする傾向があるときはいつでも

String string2 = "Hello";

JVMは、新しいオブジェクトを作成するのではなく、一定の価格のStringオブジェクトがString定数プール内に存在するかどうかを検証します。JVMは、既存のオブジェクトの参照を新しい変数に割り当てます。

そして、文字列を

String string = new String("Hello");

newキーワードを使用すると、文字列定数プールの内容に関係なく、指定された価格の新しいオブジェクトが作成されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.