将字符串与空字符串进行比较(Java)

时间:2009-02-10 10:16:51

标签: java string comparison string-comparison

我有一个关于在Java中将字符串与空字符串进行比较的问题。如果我将字符串与空字符串与==equals进行比较,是否有区别?例如:

String s1 = "hi";

if (s1 == "")

if (s1.equals("")) 

我知道应该将字符串(和一般对象)与equals进行比较,而不是==,但我想知道空字符串是否重要。

8 个答案:

答案 0 :(得分:67)

s1 == ""

不可靠,因为它测试引用相等而不是对象相等(并且String不是严格规范的)。

s1.equals("")

更好,但可能会受到空指针异常的影响。更好的是:

"".equals(s1)

没有空指针异常。

编辑:好的,有人询问canonical form。本文将其定义为:

  

假设我们有一些S对象,   具有等价关系。一个   规范形式由指定给出   S的某些对象是“规范的   形式“,使每个对象下   考虑完全等同于   一个规范形式的对象。

给你一个实际的例子:取一组有理数(或“分数”,它们通常被称为)。有理数由分子和denomoinator(除数)组成,两者都是整数。这些有理数字是等价的:

3 / 2,5 / 4,24 / 16

通常编写Rational nubmers使得gcd(最大公约数)为1.因此所有这些都将简化为3/2。 3/2可以被视为这组有理数的规范形式

那么当使用术语“规范形式”时,它在编程中意味着什么?这可能意味着一些事情。以这个想象的类为例:

public class MyInt {
  private final int number;

  public MyInt(int number) { this.number = number; }
  public int hashCode() { return number; }
}

类MyInt的哈希码是该类的规范形式,因为对于MyInt的所有实例的集合,您可以使用任何两个元素m1和m2,它们将遵循以下关系:

m1.equals(m2) == (m1.hashCode() == m2.hashCode())

这种关系是规范形式的本质。这种方法更常见的方法是在类上使用工厂方法,例如:

public class MyClass {
  private MyClass() { }

  public MyClass getInstance(...) { ... }
}

实例无法直接实例化,因为构造函数是私有的。这只是一种工厂方法。工厂方法允许您做的是:

  • 始终返回相同的实例(抽象的单例);
  • 只需为每次通话创建一个新的intsance;
  • 规范形式返回对象(稍后会详细介绍);或
  • 无论你喜欢什么。

基本上工厂方法抽象对象创建,我个人认为这将是一个有趣的语言特性,迫使所有构造函数都是私有的,以强制使用这种模式,但我离题了。

使用此工厂方法可以执行的操作是缓存您创建的实例,以便对于任何两个实例s1和s2,它们都遵循以下测试:

(s1 == s2) == s1.equals(s2)

所以当我说String不是严格的规范时,它意味着:

String s1 = "blah";
String s2 = "blah";
System.out.println(s1 == s2); // true

但是,正如其他人已经说明的那样,你可以通过使用:

来改变这种情况
String s3 = new String("blah");

可能:

String s4 = String.intern("blah");

所以你不能完全依赖引用平等,所以你根本不应该依赖它。

作为对上述模式的一个警告,我应该指出,使用私有构造函数和工厂方法控制对象创建并不能保证引用相等意味着因为序列化而导致对象相等。序列化绕过了普通的对象创建机制。 Josh Bloch在Effective Java中介绍了这个主题(最初是在第一版中讨论了类型安全的枚举模式,后来成为Java 5中的语言特性),你可以通过重载(私有)readResolve()方法来解决它。但这很棘手。类加载器也会影响这个问题。

无论如何,这是规范形式。

答案 1 :(得分:26)

这将取决于字符串是否是文字。如果使用

创建字符串
new String("")

然后它将永远不会与equals运算符匹配,如下所示:

    String one = "";
    String two = new String("");
    System.out.println("one == \"\": " + (one == ""));
    System.out.println("one.equals(\"\"): " + one.equals(""));
    System.out.println("two == \"\": " + (two == ""));
    System.out.println("two.equals(\"\"): " + two.equals(""));

-

one == "": true
one.equals(""): true
two == "": false
two.equals(""): true

基本上,您希望始终使用equals()

答案 2 :(得分:10)

这与你原来的问题有些不同,但总有

if(s1.length() == 0)

我相信这相当于1.6中的isEmpty()方法。

答案 3 :(得分:9)

"".equals(s)

似乎是最好的选择,但Apache commons lang库中还包含Stringutils.isEmpty(s)

答案 4 :(得分:8)

简短回答

s1 == ""         // No!
s1.equals("")    // Ok
s1.isEmpty()     // Ok: fast (from Java 1.6) 
"".equals(s1)    // Ok: null safe

我保证s1不为null并使用isEmpty()。

注意:空字符串""不是一个特殊的字符串,而是计为任何其他"值"。

稍长一点的回答

对String对象的引用取决于它们的创建方式:

使用运算符 new 创建的字符串对象始终引用单独的对象,即使它们存储相同的字符序列也是如此:

String s1 = new String("");
String s2 = new String("");
s1 == s2 // false

使用operator =创建的字符串对象后跟一个用双引号括起来的值( ="值" )存储在String对象池中:在创建新对象之前在池中,在池中搜索具有相同值的对象,如果找到则引用它。

String s1 = ""; // "" added to the pool
String s2 = ""; // found "" in the pool, s2 will reference the same object of s1
s1 == s2        // true

对于使用双引号括起来的字符串创建的字符串也是如此("值"),所以:

String s1 = "";  
s1 == "";        //true

字符串等于方法检查两者,这就是为什么可以安全地编写:

s1.equals("");

如果s1 == null,此表达式可能会抛出NullPointerException,因此,如果您之前没有检查过null,则写入更安全:

"".equals(s1);

请同时阅读How do I compare strings in Java?

希望它可能有助于没有经验丰富的用户,他们可能会发现其他答案有点过于复杂。 :)

答案 5 :(得分:6)

一个字符串,是一个字符串,是一个字符串,无论它是否为空字符串。使用equals()

答案 6 :(得分:3)

如果您需要空检查,请使用String.isEmpty()StringUtils.isEmpty(String str)

答案 7 :(得分:0)

给出两个字符串:

String s1 = "abc";
String s2 = "abc";

- 或 -

String s1 = new String("abc");
String s2 = new String("abc");

对两个对象执行的==运算符检查对象标识(如果两个运算符返回到同一对象实例,则返回true。)应用于java.lang.Strings的==的实际行为似乎并不总是如此由于字符串实习而一致。

在Java中,字符串是interned(至少部分由JVM自行决定。)在任何时间点,s1和s2可能已经或可能没有被实习为相同的对象引用(假设它们具有相同的值。)因此s1 == s2可能会或可能不会返回true,仅基于s1和s2是否都被实习。

使s1和s2等于空字符串对此没有影响 - 它们仍然可能已经或可能没有被实习。

简而言之,如果s1和s2具有相同的内容,==可能会也可能不会返回true。如果s1和s2具有相同的内容,则保证s1.equals(s2)返回true。