次の2つのステートメントの違いは何ですか?
String s = "text";
String s = new String("text");
false
、演算の順序が+演算子を最初に実行し、「a == b?」を連結することを指示するためです。文字列「a == b?Java」を作成するa 次に、式"a==b?Java" == b
はfalseと評価されます。
次の2つのステートメントの違いは何ですか?
String s = "text";
String s = new String("text");
false
、演算の順序が+演算子を最初に実行し、「a == b?」を連結することを指示するためです。文字列「a == b?Java」を作成するa 次に、式"a==b?Java" == b
はfalseと評価されます。
回答:
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)
"abc"
秒あります。そのうちの1つだけが文字列プールに移動し、もう1つはそれを参照します。次に、s
適切な新しいオブジェクトになるのがあります。
文字列リテラルは文字列定数プールに入ります。
以下のスナップショットは、それを視覚的に理解して、より長く覚えておくのに役立ちます。
行ごとのオブジェクト作成:
String str1 = new String("java5");
コンストラクタで文字列リテラル「java5」を使用すると、新しい文字列値が文字列定数プールに格納されます。new演算子を使用して、新しい文字列オブジェクトが値として「java5」を持つヒープに作成されます。
String str2 = "java5"
参照 "str2"は、文字列定数プールに既に格納されている値を指しています
String str3 = new String(str2);
「str2」による参照と同じ値を持つ新しい文字列オブジェクトがヒープに作成されます
String str4 = "java5";
参照 "str4"は、文字列定数プールに既に格納されている値を指しています
合計オブジェクト:ヒープ-2、プール-1
文字列定数プールに文字列を作成します
String s = "text";
もう1つは、定数プールに文字列を作成し("text"
)、通常のヒープスペースに別の文字列を作成します(s
)。両方の文字列は、「テキスト」と同じ値になります。
String s = new String("text");
s
後で未使用の場合は、失われます(GCの対象になります)。
一方、文字列リテラルは再利用されます。"text"
クラスの複数の場所で使用する場合、実際には1つだけの文字列になります(つまり、プール内の同じ文字列への複数の参照)。
この概念は、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
文字列リテラルは、クラス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
方法に注意してください:
0
and 3
:同じldc #2
定数がロードされます(リテラル)12
:新しい文字列インスタンスが作成されます(#2
引数として)35
:a
とc
通常のオブジェクトとして比較されますif_acmpne
定数文字列の表現は、バイトコードでは非常に魅力的です。
new String
上記のJVMSの引用は、Utf8が同じである場合は常に、によって同一のインスタンスがロードされると述べているようですldc
。
私はフィールドに対して同様のテストを行いました、そして:
static final String s = "abc"
ConstantValue属性を介して定数テーブルを指しますldc
結論:文字列プールの直接バイトコードサポートがあり、メモリ表現が効率的です。
おまけ:それを、バイトコードを直接サポートしていない(アナログではない)Integerプールと比較してくださいCONSTANT_String_info
。
@ブラジ:私はあなたが逆に言及したと思います。私が間違っている場合は修正してください
行ごとのオブジェクト作成:
文字列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
どのような方法で。
(疑似)の"bla"
ような魔法の工場であると考えてくださいStrings.createString("bla")
。ファクトリは、この方法で作成されたすべての文字列のプールを保持します。
呼び出された場合、プールにこの値の文字列がすでに存在するかどうかを確認します。trueの場合、この文字列オブジェクトを返すため、この方法で取得した文字列は実際には同じオブジェクトです。
そうでない場合は、内部で新しい文字列オブジェクトを作成し、プールに保存してから返します。したがって、次回同じ文字列値がクエリされると、同じインスタンスが返されます。
手動で作成new String("")
すると、文字列リテラルプールをバイパスすることでこの動作を上書きします。したがってequals()
、オブジェクト参照の同等性ではなく、文字シーケンスを比較するを使用して、同等性を常に確認する必要があります。
違いを理解する簡単な方法の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()は常に新しいインスタンスを作成します。
文字列リテラルは文字列リテラルプール内に作成され、プールは重複を許可しません。したがって、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」が追加されるリテラルは文字列プールに存在するかどうかがチェックされ、存在しないため作成されます上記の場合。
プログラマーの観点からは同じに見えますが、パフォーマンスに大きな影響があります。ほとんどの場合、最初の形式を使用する必要があります。
String str = new String("hello")
文字列定数プールに文字列「hello」がすでに含まれているかどうかを確認しますか?存在する場合、文字列定数プールにエントリを追加しません。存在しない場合は、文字列定数プールにエントリを追加します。
オブジェクトはヒープメモリ領域にstr
作成され、ヒープメモリの場所に作成されたオブジェクトへの参照ポイントになります。
str
文字列定数プールに含まれるポイントオブジェクトへの参照が必要な場合は、明示的に呼び出す必要がありますstr.intern();
String str = "world";
文字列定数プールに文字列「hello」がすでに含まれているかどうかを確認しますか?存在する場合、文字列定数プールにエントリを追加しません。存在しない場合は、文字列定数プールにエントリを追加します。
上記のどちらの場合も、str
参照"world"
は定数プールに存在する文字列を指します。
文字列を次のように保存すると
String string1 = "Hello";
直接、JVMはString定数プールと呼ばれる別のメモリブロック中に、指定された価格でStringオブジェクトを作成します。
そして、次のように別の文字列を作成しようとする傾向があるときはいつでも
String string2 = "Hello";
JVMは、新しいオブジェクトを作成するのではなく、一定の価格のStringオブジェクトがString定数プール内に存在するかどうかを検証します。JVMは、既存のオブジェクトの参照を新しい変数に割り当てます。
そして、文字列を
String string = new String("Hello");
newキーワードを使用すると、文字列定数プールの内容に関係なく、指定された価格の新しいオブジェクトが作成されます。