2013-07-08 9 views
73

私はいくつかの奇妙な文字列プールの動作の質問を持っています。 私は==を使用して、等しい文字列を比較して、プール内にあるかどうかを調べます。奇妙な文字列プールの振る舞い

public class StringPoolTest { 
    public static void main(String[] args) { 
    new StringPoolTest().run(); 
    } 

    String giveLiteralString() { 
    return "555"; 
    } 

    void run() { 
    String s1 = giveLiteralString() + ""; 
    System.out.println("555" == "555" + ""); 
    System.out.println(giveLiteralString() == giveLiteralString() + ""); 
    } 
} 

出力は次のようになります。私にとって大きな驚きである

true 
false 

。誰でもこのことを説明できますか? コンパイル時にこれについて何か考えていると思います。しかし、なぜ文字列に""を追加すると違いがあるのでしょうか?

+0

@MarkoTopolnik私にも同じように見えます。 – johnchen902

+5

@ MarkoTopolnik私は質問が少し異なっていることを知っていた。しかし、答えは常に "XXXはコンパイル時定数ですが、YYYはそうではありません"のようです。しかし、間違った質問を選んだのかもしれません。 – johnchen902

+1

@ johnchen902私は同意しますが、間違った質問を重複として投稿しました: – Thihara

答えて

111
"555" + "" 

giveLiteralString() + "" 

ではないのに対し、compile-time constantあります。したがって、前者は文字列定数 "555"にコンパイルされ、後者は実際のメソッドの呼び出しと連結にコンパイルされ、結果として新しいStringインスタンスが生成されます。


また JLS §3.10.5 (String Literals)参照:実行時に連結することにより計算

文字列は、新しく作成されたと ため、別個のものです。

+0

メソッド呼び出しの新鮮な文字列のインスタンスを引用してください。任意のJLSリンク? – sanbhat

+0

また、コンパイルの前/実行中に実行されるコードオプティマイザは、すでに単一の文字列オブジェクト "555" ' に" 555 "+" "'を組み合わせている可能性があります。 'method()+" method()+ ""をコンパイルしてください。 – Korashen

+3

@sanbhat私の答えで "コンパイル時定数"をクリックしてください。 –

31

は、逆コンパイル、このライン

System.out.println("555" == "555" + ""); 

後、私は表現"555" == "555" + ""trueブールにコンパイル意味

System.out.println(true); 

に相当し、このバイトコード

LINENUMBER 8 L0 
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 
    ICONST_1 
    INVOKEVIRTUAL java/io/PrintStream.println(Z)V 
    ... 

を得ました。 giveLiteralString() == giveLiteralString() + "" javacのために

は、我々は2つのdisctinctオブジェクトを比較しているので、ここでは常にfalseを生成します

if (giveLiteralString() == new StringBuilder(giveLiteralString()).append("").toString()) { 
... 

に相当し、このバイトコード

LINENUMBER 8 L0 
    INVOKESTATIC Test1.giveLiteralString()Ljava/lang/String; 
    NEW java/lang/StringBuilder 
    DUP 
    INVOKESTATIC Test1.giveLiteralString()Ljava/lang/String; 
    INVOKESTATIC java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String; 
    INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V 
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String; 
    IF_ACMPNE L1 
    ... 

を構築しました。

+2

"常にfalseを生成します" - 技術的には、StringBuilderは、非内部文字列を生成するために*必須*ではありません。それは、単にそれが拘束力のあるものを生産しようとする正当な理由はないということです。 –

+1

StringBuilder.toString API - 新しいStringオブジェクトが割り当てられ、このオブジェクトによって現在表されている文字シーケンスを含むように初期化されます。このStringが返されます。 –

+0

しかし、何もそれは抑えられないと言います。 –

4

""は長さがゼロであることが分かっているので、後者の場合、コンパイラは+ ""がソートしていないことを認識しています。しかし、コンパイラは、giveLiteralStringの結果をヌルにチェックする必要があります(最適化されていないケースでは+オペレーションの結果としてヌルチェックが行われるため)。最適化を試みないのが最も簡単です。

結果として、コンパイラは連結を実行するコードを生成し、新しい文字列が作成されます。

+5

コンパイラは、実行時連結の結果が新鮮な文字列であることを明示するJLSに準拠する必要があります。 –

0

定数式によって計算コンパイル時間連結 文字列は、コンパイル時に行われ、定数またはリテラルとして扱われる文字列または式の値がコンパイル時に知られているか、または評価されることを意味従ってコンパイラは、同じ値を確認することができ文字列プールを返し、同じ文字列オブジェクト参照を返します。

、その値は、コンパイラは、文字列の値を知ることができませんので、いつも使って土地が知られているか、コンパイル時に評価することはできませんが、実行時の入力や状態によって異なりランタイム連結 文字列式StringBuilderは文字列を追加し、常に新しい文字列を返します。 私はこの例がそれをより明確にすると思います。

public static void main(String[] args) { 
    new StringPoolTest().run(); 
    } 
    String giveLiteralString() { 
    return "555"; 
    } 

    void run() { 
    System.out.println("555" + 9 == "555" + 9); 
    System.out.println("555"+Integer.valueOf(9) == "555" + Integer.valueOf(9)); 
    System.out.println(giveLiteralString() == giveLiteralString()); 
    // The result of runtime concatenation is a fresh string. 
    System.out.println(giveLiteralString() == giveLiteralString() + ""); 
    }