当“”== s为假但“”.equals(s)为真时

时间:2009-07-10 18:47:47

标签: java comparison equals equality

修改 感谢您的快速回复。请看看真正的问题是什么。我这次大胆了。

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


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

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

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

所以那些情况的修复是

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

我很好。这很清楚。

今天这又发生了一次,但它让我感到困惑,因为这次应用程序是一个非常小的独立应用程序,它根本不使用网络,所以没有新的string是从数据库中获取的,也不是从另一个节点中取消激活的。

所以问题是:


OTHER 情况下:

"" == value // yields false 

"".equals( value ) // yields true

对于本地独立应用程序?

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

字符串引用可以是“”的唯一方法是因为它直接在代码中分配“”(或者我认为的那样),如:

String a = "";
String b = a;

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

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

更多关于jjnguys的回答:

字节!

编辑:结论

我找到了原因。

在jjnguy建议之后,我能够以不同的眼光看待代码。

guilty方法:StringBuilder.toString()

  

分配并初始化一个新的String对象,以包含此对象当前表示的字符序列。

Doh!...

    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. 

P.S。我最近读过太多CodingHorror吗?或者为什么我觉得有必要在这里添加一些有趣的动物图片?

10 个答案:

答案 0 :(得分:26)

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

这样的事情会导致s == s2评估为假。

很多代码会创建新的Strings而不会将调用公开给new String()

答案 1 :(得分:21)

"" == value // yields false

"".equals( value ) // yields true

任何时候变量value的值都没有被实现。如果值是在运行时计算的,则会出现这种情况。有关说明此内容的示例代码,请参阅JLS section 3.10.5 String Literals

  

因此,测试程序由编译单元(第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节)在编译时计算,然后视为文字。
  •   
  • 在运行时计算的字符串是新创建的,因此是不同的。
  •   
  • 显式实现计算字符串的结果与具有相同内容的任何预先存在的文字字符串的字符串相同。
  •   

答案 2 :(得分:10)

如果你能抓住Joshua Bloch和Neal Gafter所着的 Java Puzzlers 这本书,看看拼图13,“动物农场”......他对这个问题有很好的建议。我将复制一些相关文字:

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

这是我在上面提到的参考资料......在我的书中第30-31页。

答案 3 :(得分:7)

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

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

现在考虑子串的长度为0,substring(1, 1)。它产生一个零长度的字符串,但"abcde".substring(1,1)"zbcdefgh".substring(1,2)是不同的对象并不奇怪,因此它们中至少有一个是与“”不同的对象。

答案 4 :(得分:3)

据我所知,在将Java代码编译为字节码或运行程序时,在大多数情况下,相同的字符串将被引用到相同的对象以节省内存。所以有时你会逃避==字符串的比较。但这是一个你不能依赖的编译器优化。

但有时会发生编译器决定不执行此优化或者程序没有办法看到字符串是相同的,并且突然检查失败,因为您依赖于某些底层优化巫术这取决于你正在使用的jvm的实现等等。

所以使用equals总是好事。对于空字符串,还有其他可能性,例如与length == 0进行比较,或者如果您不关心向后兼容性,则会有string.empty()。

答案 5 :(得分:2)

您应该尝试考虑String.length() == 0

答案 6 :(得分:0)

为什么不使用:

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

您应该使用equals(),因为对象的==会比较引用,即它们是同一个对象。虽然在编译时Java发现相同的字符串并使它们共享相同的引用(字符串是不可变的),但在运行时很容易创建具有不同引用的空字符串,其中==因{1 {}的典型意图而失败。

答案 7 :(得分:0)

答案 8 :(得分:0)

String.intern()的{​​{3}}对==.equals()有一些很好的评论。

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

  

public String intern()

     

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

     

字符串池(最初为空)由String类私有维护。

     

调用实习方法时,如果   池已经包含一个字符串   等于这个String对象   由等于(对象)确定   方法,然后从池中的字符串   退回。否则,这个String   对象被添加到池中和   对这个String对象的引用是   返回。

     

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

     

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

     

返回:具有相同字符串的字符串   内容为此字符串,但是   保证来自一个独特的池   字符串。

答案 9 :(得分:0)

如果您使用谷歌代码搜索,您可以找到许多人们犯同样错误的地方:google for file:.java \=\=\ \"\"当然,在精心控制的情况下,这可能是一个正确的习惯用法,但通常,它只是一个错误。