34

編集 迅速な対応ありがとうございます。本当の質問が何であるかを見てください。今回は大胆にしました。

==と.equalsの違いは理解できます。だから、それは私の質問ではありません(私は実際にそのためのいくつかのコンテキストを追加しました)


空の文字列に対して以下の検証を実行しています。

if( "" == value ) { 
    // is empty string 
} 

以前は、dbから値をフェッチしたり、別のノードからオブジェクトを逆シリアル化したりしたときに、2つの文字列インスタンスが同じデータを含んでいたとしても、実際には異なるオブジェクト参照であったため、このテスト失敗しました。

したがって、これらの状況の修正は

if( "".equals( value ) ) {
   // which returns true for all the empty strings
}

私はそれで大丈夫です。それははっきりと理解されています。

今日、これはもう一度起こりましたが、今回のアプリケーションはネットワークをまったく使用しない非常に小さなスタンドアロンアプリケーションであるため、データベースから新しい文字列がフェッチされたり、別のノードから脱セライズされたりすることはありません。

したがって、問題は次のとおりです。


その他の状況:

"" == value // yields false 

"".equals( value ) // yields true

ローカルスタンドアロンアプリケーションの場合?

新しいString()がコードで使用されていないことは間違いありません。

そして、文字列参照が ""になる唯一の方法は、次のようにコード内で直接 ""が割り当てられているためです(またはそれが私が考えたものです)。

String a = "";
String b = a;

assert "" == b ; // this is true 

どういうわけか(コードをもっと読んだ後、私は手がかりがあります)2つの異なる空の文字列オブジェクト参照が作成されました、私は方法を知りたいです

jjnguysの答えの行の詳細:

バイト!

編集:結論

私はその理由を見つけました。

jjnguyの提案の後、私はコードをさまざまな目で見ることができました。

有罪のメソッド:StringBuilder.toString()

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

ドー!..。

    StringBuilder b = new StringBuilder("h");
    b.deleteCharAt( 0 );
    System.out.println( "" == b.toString() ); // prints false

謎が解けた。

このコードは、StringBuilderを使用して、増え続ける文字列を処理します。ある時点で誰かがしたことが判明しました:

 public void someAction( String string ) { 
      if( "" == string ) {
           return;
       }

       deleteBankAccount( string );
 }

と使用

 someAction( myBuilder.toString() ); // bug introduced. 

ps最近、CodingHorrorを読みすぎましたか?または、なぜここに面白い動物の写真を追加する必要があると感じるのですか?

4

10 に答える 10

27
String s = "";
String s2 = someUserInputVariale.toLowercase(); // where the user entered in ""

そのようなものはs == s2false に評価されます。

Strings多くのコードは、 への呼び出しを公開せずにnew を作成しnew String()ます。

于 2009-07-10T18:50:17.070 に答える
22
"" == value // yields false

"".equals( value ) // yields true

変数の値valueがインターンされていないときはいつでも。これは、値が実行時に計算される場合に当てはまります。これを示すコード例については、 JLS セクション 3.10.5 文字列リテラルを参照してください。

したがって、コンパイル ユニット (§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 { static String hello = "Hello"; }

出力を生成します:

true true true true false true

この例は、次の 6 つのポイントを示しています。

  • 同じパッケージ (§7) 内の同じクラス (§8) 内のリテラル文字列は、同じ String オブジェクト (§4.3.1) への参照を表します。
  • 同じパッケージ内の異なるクラス内のリテラル文字列は、同じ String オブジェクトへの参照を表します。
  • 同様に、異なるパッケージの異なるクラス内のリテラル文字列は、同じ String オブジェクトへの参照を表します。
  • 定数式 (§15.28) によって計算された文字列は、コンパイル時に計算され、リテラルであるかのように扱われます。
  • 実行時に計算される文字列は新しく作成されるため、区別されます。
  • 計算された文字列を明示的にインターンした結果は、同じ内容の既存のリテラル文字列と同じ文字列になります。
于 2009-07-10T19:10:13.347 に答える
11

JoshuaBlochとNealGafterによる本JavaPuzzlersを手に入れて、パズル13「AnimalFarm」を見ることができれば...彼はこの問題について素晴らしいアドバイスをしています。関連するテキストをいくつかコピーします。

String「型のコンパイル時定数がインターンされていることに気付くかもしれません[ JLS15.28]。言い換えると、同じ文字シーケンスを指定する型の2つの定数式Stringは、同一のオブジェクト参照によって表されます。、文字列定数のインターンに依存します。インターンは、プログラマー向けのツールとしてではなく、仮想マシンのメモリフットプリントを削減するためだけに設計されました...オブジェクト参照を比較するときは、必要がない限り、演算子よりもメソッドを使用するequals==必要があります。価値ではなくオブジェクトのアイデンティティを比較するためです。」

それは私が言及した上記の参考文献からです...私の本の30〜31ページ。

于 2009-07-10T19:05:24.013 に答える
8

同じ String オブジェクトを生成することを期待"abcde".substring(1,2)しますか?"zbcdefgh".substring(1,2)

どちらも、2 つの異なる文字列から抽出された「等しい」部分文字列を生成しますが、これらが異なるオブジェクトであることは非常に合理的であるように思われるため、== はそれらを異なるものとして認識します。

部分文字列の長さが 0 の場合を考えてみましょう substring(1, 1)"abcde".substring(1,1)長さゼロの文字列が生成されますが、 がとは異なるオブジェクトであることは驚くべきことではありません"zbcdefgh".substring(1,2)

于 2009-07-10T18:52:44.147 に答える
3

私が理解しているように、Javaコードをバイトコードにコンパイルしている間、またはプログラムを実行している間、ほとんどの場合、メモリを節約するために同じ文字列が同じオブジェクトを参照します。そのため、文字列の == 比較でうまくいかないことがあります。しかし、これは信頼できないコンパイラーの最適化です。

しかし、コンパイラがこの最適化を行わないことを決定したり、プログラムが文字列が同じであることを確認する方法がなく、突然チェックが失敗することがあります。使用しているjvmの実装など。

したがって、equals を使用することは常に良いことです。空の文字列の場合は、長さ == 0 と比較するなどの他の可能性があります。後方互換性を気にしない場合は、string.empty() があります。

于 2009-07-10T19:02:11.820 に答える
2

を検討してみてくださいString.length() == 0

于 2009-07-10T18:52:07.360 に答える
0

このリファレンスを確認してください:http://mindprod.com/jgloss/string.html#COMPARISON at the great Canadian Mind Products Java&InternetGlossary。ブックマークする価値があります。

于 2009-07-10T19:02:35.327 に答える
0

javadocには、 vs.String.intern()に関する優れた解説があります。==.equals()

internドキュメントでは、すべての文字列リテラルが'dであることも明確にしています。

public String intern()

文字列オブジェクトの正規表現を返します。

最初は空である文字列のプールは、クラス String によってプライベートに維持されます。

intern メソッドが呼び出されたときに、equals(Object) メソッドによって決定されたこの String オブジェクトと等しい文字列がプールにすでに含まれている場合、プールからの文字列が返されます。それ以外の場合は、この String オブジェクトがプールに追加され、この String オブジェクトへの参照が返されます。

したがって、任意の 2 つの文字列 s および t について、s.intern() == t.intern() は、s.equals(t) が true である場合にのみ true になります。

すべてのリテラル文字列と文字列値の定数式はインターンされます。文字列リテラルは、Java 言語仕様の §3.10.5 で定義されています。

戻り値: この文字列と同じ内容を持つ文字列ですが、一意の文字列のプールからのものであることが保証されています。

于 2009-07-10T19:15:30.637 に答える