文字列を比較する次のコード行があります。 str1はstr2と等しくありません。これは、オブジェクト参照を比較するので理解できます。しかし、なぜs1がs2に等しいのでしょうか。
String s1 = "abc";
String s2 = "abc";
String str1 = new String("abc");
String str2 = new String("abc");
if (s1==s2)
System.out.println("s1==s2");
else
System.out.println("s1!=s2");
if (str1==str2)
System.out.println("str1==str2");
else
System.out.println("str1!=str2");
if (s1==str1)
System.out.println("str1==s1");
else
System.out.println("str1!=s1");
出力:
s1==s2
str1!=str2
str1!=s1
文字列定数プールは基本的にすべての文字列リテラルをキャッシュするので、それらはその下にある同じオブジェクトです。そのため、s1==s2
の出力が表示されます。これは、本質的にVMの最適化であり、リテラルが宣言されるたびに新しい文字列オブジェクトを作成することを回避します。これは非常にすぐに高価になる可能性があります!str1==str2
の例では、明示的に= VM新しい文字列オブジェクトを作成するため、なぜそれが偽であるか。
余談ですが、任意の文字列でintern()
メソッドを呼び出すと、定数プールに追加されます(そして、プールに追加された文字列が返されます)。必ず定数として使用される文字列を処理していることを確認してください。そうしないと、メモリリークを追跡するのが困難になる可能性があります。
s1とs2は文字列リテラルです。新しい文字列リテラルを作成すると、コンパイラは最初に、それを表すリテラルが文字列プールに存在するかどうかをチェックします。存在する場合、コンパイラはそのリテラルを返します。それ以外の場合、コンパイラは新しいリテラルを作成します。
String s2
を作成すると、以前に作成されたとおり、コンパイラーはプールからString s1
を返します。これが、s1
とs2
が同じである理由です。この動作はinterningと呼ばれます。
この現象は Stringinterning によるものです。
基本的に、すべての文字列リテラルは「キャッシュ」されて再利用されます。
これは、文字列リテラルがインターンされているためです。この問題について、 Javaドキュメント は次のように述べています。
すべてのリテラル文字列と文字列値の定数式がインターンされます
そして、これがs1
およびs2
は同じです(これらの2つの変数は同じインターンされた文字列を指します)
Javaでは、同じ定数文字列が再利用されます。つまり、_s1
_と_s2
_は、同じ "abc"オブジェクトと_s1==s2
_を指します。ただし、new String("abc")
を使用すると、別のオブジェクトが作成されます。つまり、_s1 != str1
_です。
string
はJavaでは不変なので、すべてのstring literals
再利用のためにキャッシュされました。
New()演算子を使用してStringオブジェクトを作成すると、常にヒープメモリに新しいオブジェクトが作成されます。一方、文字列リテラル構文を使用してオブジェクトを作成するとします。 「Java」の場合、文字列プールから既存のオブジェクトを返します(Perm genスペースの文字列オブジェクトのキャッシュで、最近のJavaリリースでヒープ領域に移動されました)。それ以外の場合は、新しい文字列オブジェクトを作成し、将来の再利用のために文字列プールに入れます。
String s1 = new String("Java");
String s2 = new String("Java");
String s3 = "Java";
String s4 = "Java";