4
public class Demo {  

    public static void main(String[] args) {

        String s1 = "Hello";
        String s2 = "Hello";
        System.out.println("s1 == s2 " + (s1 == s2));

        String s5 = "Hel" + "lo";
        String s6 = "He" + "llo";
        System.out.println("s5 == s6 " + (s5 == s6));

        String s7 = "He";
        String s8 = "Hello";
        s7 = s7.concat("llo");
        System.out.println("s7 == s8 " + (s7 == s8));

        String s10 = "He";
        s10 = s10 + "llo";
        System.out.println("s1 == s10 "+(s1 == s10));
    }
}

上記のコードでは、s7 == s8 および s1 == s10 は false を返します。誰かが私に説明してくれませんか、ここで実際に何が起こっているのか s7 = s7.concat ("llo"); そして s10 = s10 + "llo"; == 演算子は参照をチェックし、 equal() はオブジェクトの内容をチェックすることを理解しています。しかし、s7 と s10 の参照変数のビット パターンが s8 と s1 と異なる理由を知る必要があります。これらがコンパイル時に生成された文字列と実行時に生成された文字列に関連している場合、それがコンパイル時または実行時の文字列であるかどうかをどのように識別できますか?

4

7 に答える 7

11

これが発生する理由は、Java がコンパイラで最適化を行っているためです。リテラル文字列"Hello"を s1 に割り当てていることがわかると、s2 に同じ「Hello」を使用します。これは、すべての Java String 操作が非破壊的であるため (たとえば、オリジナルを変更するのではなくクローンを返すため)、安全です。やるべき事。

"Hel" + "lo"vsにも同じことが言え"He" + "llo"ます。それらが同じものであることを理解するのに十分賢いです。

他のものは複雑すぎて最適化できないため、別々のオブジェクトになってしまいます。

于 2010-09-26T19:34:55.090 に答える
4

Clintの答えは問題ありませんが、さらに詳しく説明し、コンパイラレベルで説明します。

ご存知のように、同じ文字列インスタンスへの参照になりs1ます。s2"Hello"

s5およびの場合s6、コンパイラは定数式を参照します。つまり、2つの定数(文字列リテラル)間の演算を確認します。コンパイラは、文字列を追加する方法と結果がどうなるかを知っています。値はコンパイル時にすぐにわかるため、加算が行われ、リテラル文字列になります"Hello"。したがって、値はと同じs1s2あるため、それぞれが同じインスタンスを参照します。

s7同じように単純化することはできません。 もちろん、s7最初はから始まります。"He"ここでの違いは、関数呼び出しの結果にs7 = s7.concat("llo");再割り当てすることです。s7これをそのまま単純化することはできませんでした。Javaコンパイラに関する限り、すべての関数呼び出しの結果はコンパイル時にわかりません。結果の値がわからないため、簡略化できず、そのままにしておきます。結果の呼び出しは、コンパイル時のインスタンス(共有する)"Hello"と同じインスタンスではない文字列の新しいインスタンスを返します。s8

s10同様に単純化することはできません。 もちろん、s10最初はから始まります。"He"その後、再割り当てされs10 = s10 + "llo"; ますこれは単純化できませんでした。なぜあなたは尋ねるかもしれませんか?さてs10、非最終的な変数式です。技術的には、定数ではないため、コンパイラはその値を認識しません。s10が宣言された場合final String、これは定数畳み込みである可能性があります(別の変数に割り当てられた場合)。

したがって、このバージョンのテストコードを検討してください。

public static void main(String[] args)
{
    String s1 = "Hello";
    String s2 = "Hello";
    System.out.println("1: s1 == s2 " + (s1 == s2));    // 1

    String s3 = "Hel" + "lo";
    String s4 = "Hel".concat("lo");
    System.out.println("2: s1 == s3 " + (s1 == s3));    // 2
    System.out.println("3: s1 == s4 " + (s1 == s4));    // 3

    String he = "He";
    String s5 = he + "llo";
    String s6 = he.concat("llo");
    System.out.println("4: s1 == s5 " + (s1 == s5));    // 4
    System.out.println("5: s1 == s6 " + (s1 == s6));    // 5

    final String fhe = "He";
    String s7 = fhe + "llo";
    String s8 = fhe.concat("llo");
    System.out.println("6: s1 == s7 " + (s1 == s7));    // 6
    System.out.println("7: s1 == s8 " + (s1 == s8));    // 7
}

どの線が正しいかわかりますか?

true、true、false、false、false、true、false
なぜ3と7が真ではないのか疑問に思われるかもしれません。簡単に言うと、Javaコンパイラは
concat()呼び出しを認識するほど賢くプログラムされていないため、通常の関数呼び出しとして扱われます。

于 2010-09-26T20:11:43.413 に答える
4

== はビット パターンをチェックせず、オブジェクトのメモリ アドレスを比較します。同じオブジェクトだけが同じメモリ アドレスを持ちます。

于 2010-09-26T19:36:47.767 に答える
0

あなたが提供した例では、これが起こっていることです:

String s7 = “He”;    //s7 is an object referencing a part of memory holding “He”
String s8 = “Hello”;   //s8 is an object referencing a part of memory holding “Hello”
s7 = s7.concat(“llo”); //s7.concat(“llo”) created a new object in memory that contains “Hello” and s7 now references this now object 

(s7==s8)             //checks if the equality of the object reference and this is false since they reference different memory addresses.

(s7.equals(s8))         //this will compare s7 and s8 however way the String class compares two String objects. 
于 2010-09-26T19:59:44.640 に答える
0

equals 演算子は、参照の値が同じかどうかではなく、参照が同じかどうか (つまり、同じオブジェクトを指しているかどうか) をテストします。ある文字列が別の文字列と等しいかどうかをテストする必要がある場合は、組み込みの .equals メソッドを使用する必要があります。これにより、オブジェクト値の比較が行われます。例えば

final String expectedValue = "Foo";
final String actualValue = "F" + "oo";
if (expectedValue.equals(actualValue)) {
  // This will trigger where == would not
}

さらに、安全のために、2 つの文字列を比較し、1 つが定数である場合は、通常、定数に対して equals を呼び出すのが最善です。つまり、

String myValue = getMyValue;
boolean theSame = "Some value".equals(myValue);

それよりも

String myValue = getMyValue;
boolean theSame = myValue.equals("Some Value");

その理由は、2 番目の形式では null ポインター例外が発生するリスクがあるためです。この例外は、そこに存在することが保証されている定数文字列に対して equals() を呼び出すことで回避できます。

于 2010-09-26T19:38:07.833 に答える
0

文字列オブジェクトについて何の仮定もできません。

VM は、まったく同じ char 配列を含む 2 つの文字列オブジェクトが同時に存在しないようにするために懸命に作業できますが、他の VM では重複が許可されます。

于 2010-09-26T19:38:23.493 に答える
0

オペレーターは==、2 つのオブジェクトが同じアドレス (ポインター) を持っているかどうかのみをチェックします。参照ではないプリミティブ型 (int、char など) の場合のみ、値を比較します。

s1.equals(s2)2 つの文字列の内容を比較するには、次のようなものを使用する必要があります。

于 2010-09-26T19:39:31.800 に答える