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

新手上路,请多包涵

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

 String s1 = "hi";

if (s1 == "")

或者

if (s1.equals(""))

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

原文由 user42155 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 719
1 个回答
s1 == ""

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

 s1.equals("")

更好,但可能会出现空指针异常。更好的是:

 "".equals(s1)

没有空指针异常。

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

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

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

3/2、6/4、24/16

有理数通常写成 gcd(最大公约数)为 1。因此所有这些都将简化为 3/2。 32 可以看作是这组有理数的 规范形式

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

 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() 方法来解决这个问题。但这很棘手。类加载器也会影响这个问题。

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

原文由 cletus 发布,翻译遵循 CC BY-SA 2.5 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题