34

编辑 感谢您的及时回复。请看看真正的问题是什么。这次我把它变得大胆了。

我确实理解 == 和 .equals 之间的区别。所以,这不是我的问题(我实际上为此添加了一些上下文)


我正在对空字符串执行以下验证:

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

过去数据库中获取值或从另一个节点反序列化对象时,此测试失败,因为两个字符串实例确实是不同的对象引用,尽管它们包含相同的数据。

所以解决这些情况的方法是

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

我很好。这很清楚。

今天再次发生这种情况,但让我感到困惑,因为这次应用程序是一个非常小的独立应用程序,根本不使用网络,所以没有从数据库中获取新字符串,也没有从另一个节点反序列化。

所以问题是:


其他情况下:

"" == value // yields false 

"".equals( value ) // yields true

对于本地独立应用程序?

我很确定代码中没有使用new String() 。

字符串引用可能是“”的唯一方法是因为它是直接在代码中分配的“”(或者这就是我的想法),例如:

String a = "";
String b = a;

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

不知何故(在阅读了更多代码之后,我有了一个线索)创建了两个不同的空字符串对象引用,我想知道如何

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 == s2评估为假。

许多代码仍然会创建新Strings的而不暴露对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

这个例子说明了六点:

  • 同一包 (§7) 中同一类 (§8) 中的文字字符串表示对同一 String 对象 (§4.3.1) 的引用。
  • 同一包中不同类中的文字字符串表示对同一 String 对象的引用。
  • 不同包中不同类中的文字字符串同样表示对同一 String 对象的引用。
  • 由常量表达式(第 15.28 节)计算的字符串在编译时计算,然后将其视为文字。
  • 在运行时计算的字符串是新创建的,因此是不同的。
  • 显式实习计算字符串的结果是与任何具有相同内容的预先存在的文字字符串相同的字符串。
于 2009-07-10T19:10:13.347 回答
11

如果你能抓住 Joshua Bloch 和 Neal Gafter 所著的Java Puzzlers一书,并查看第 13 题“动物农场”……他在这个问题上有很好的建议。我要复制一些相关的文字:

“您可能知道类型String的编译时常量是内部的[JLS 15.28]。换句话说,任何两个指定相同字符序列的类型常量表达式String都由相同的对象引用表示……您的代码应该很少,如果有的话, 依赖于字符串常量的实习。实习只是为了减少虚拟机的内存占用而设计的,而不是作为程序员的工具...比较对象引用时,除非需要,否则应优先使用该equals方法而不是==运算符比较对象身份而不是价值。”

那是来自我提到的上述参考资料……我书中的第 30-31 页。

于 2009-07-10T19:05:24.013 回答
8

你会期望"abcde".substring(1,2)"zbcdefgh".substring(1,2)产生相同的 String 对象吗?

它们都产生从两个不同字符串中提取的“相等”子字符串,但它们是不同的对象似乎很合理,因此 == 将它们视为不同的。

现在考虑当子字符串的长度为 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

为什么不使用:

if (value != null && value.length == 0) {
    // do stuff (above could be "== null ||"
}

您应该使用equals()因为==对象比较引用,即它们是同一个对象。虽然在编译时 Java 会找到相同的字符串并让它们共享相同的引用(字符串是不可变的),但在运行时很容易创建具有不同引用的空字符串,其中 == 对于equals().

于 2009-07-10T18:55:23.360 回答
0

查看此参考: http: //mindprod.com/jgloss/string.html#COMPARISON在优秀的Canadian Mind Products Java & Internet Glossary中。值得收藏。

于 2009-07-10T19:02:35.327 回答
0

javadoc for对vs.有一些很好的String.intern()评论。==.equals()

该文档还阐明了每个字符串文字都是intern'd.

公共字符串实习生()

返回字符串对象的规范表示。

一个字符串池,最初是空的,由 String 类私下维护。

当调用 intern 方法时,如果池中已经包含一个等于该 String 对象的字符串,由 equals(Object) 方法确定,则返回池中的字符串。否则,将此 String 对象添加到池中并返回对该 String 对象的引用。

由此可见,对于任何两个字符串 s 和 t,当且仅当 s.equals(t) 为真时,s.intern() == t.intern() 才为真。

所有文字字符串和字符串值的常量表达式都是实习的。字符串文字在 Java 语言规范的 §3.10.5 中定义

返回: 与此字符串具有相同内容的字符串,但保证来自唯一字符串池。

于 2009-07-10T19:15:30.637 回答
0

If you use google code search, you can find lots of places where people make this same error: google for file:.java \=\=\ \"\" Of course, this can be a correct idiom in carefully controlled circumstances, but usually, its just a bug.

于 2009-07-10T21:11:14.740 回答