39

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

String s1 = "hi";

if (s1 == "")

或者

if (s1.equals("")) 

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

4

8 回答 8

76
s1 == ""

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

s1.equals("")

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

"".equals(s1)

没有空指针异常。

编辑:好的,关于规范形式的问题。本文将其定义为:

假设我们有一些具有等价关系的对象集合 S。通过将 S 的一些对象指定为“规范形式”来给出规范形式,使得所考虑的每个对象都恰好等价于规范形式的一个对象。

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

3/2、6/4、24/16

有理数通常写成 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(...) { ... }
}

实例不能直接实例化,因为构造函数是私有的。这只是一种工厂方法。工厂方法允许您执行的操作如下:

  • 总是返回相同的实例(抽象单例);
  • 每次调用都创建一个新实例;
  • 以规范形式返回对象(稍后会详细介绍);或者
  • 随你喜欢。

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

您可以使用此工厂方法执行的操作是缓存您创建的实例,以便对于任何两个实例 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() 方法来绕过它。但这很棘手。类加载器也会影响这个问题。

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

于 2009-02-10T10:51:51.940 回答
28

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

new String("")

那么它永远不会匹配“”与等号运算符,如下所示:

    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()

于 2009-02-10T10:20:01.837 回答
14

简短的回答

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

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

注意:空字符串“”不是特殊的字符串,而是算作任何其他“值”。

再长一点的答案

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

使用操作符new创建的字符串对象总是引用单独的对象,即使它们存储相同的字符序列,所以:

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

使用操作符 = 创建的字符串对象后跟一个用双引号括起来的值 ( = "value" ) 存储在字符串对象池中:在池中创建新对象之前,会在池中搜索具有相同值的对象并在找到时引用。

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

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

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

String equals 方法检查两者,这就是编写安全的原因:

s1.equals("");

如果 s1 == null,此表达式可能会抛出 NullPointerException,因此,如果您之前不检查 null,则编写起来更安全:

"".equals(s1);

另请阅读如何比较 Java 中的字符串?

希望它可以帮助没有经验的用户,他们可能会发现其他答案有点太复杂了。:)

于 2015-05-21T15:17:14.033 回答
10

这与你原来的问题有点偏离,但总有

if(s1.length() == 0)

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

于 2009-02-10T18:39:45.560 回答
9
"".equals(s)

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

于 2009-02-10T10:54:32.310 回答
6

一个字符串,就是一个字符串,就是一个字符串,不管它是不是空字符串。使用equals().

于 2009-02-10T10:23:24.623 回答
4

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

于 2010-12-21T10:36:04.530 回答
0

给定两个字符串:

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

-或者 -

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

对两个 Object 执行的 == 运算符检查对象身份(如果两个运算符返回同一个对象实例,则返回 true。)应用于 java.lang.Strings 的 == 的实际行为并不总是一致的,因为字符串实习。

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

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

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

于 2009-02-11T01:26:53.227 回答