用于比较字符串的陷阱
Java 初学者的一个常见错误是使用 ==
运算符来测试两个字符串是否相等。例如:
public class Hello {
public static void main(String[] args) {
if (args.length > 0) {
if (args[0] == "hello") {
System.out.println("Hello back to you");
} else {
System.out.println("Are you feeling grumpy today?");
}
}
}
}
上面的程序应该测试第一个命令行参数并打印不同的消息,而不是单词 hello
。但问题是它不起作用。该计划将输出“你今天感觉脾气暴躁吗?” 无论第一个命令行参数是什么。
在这种特殊情况下,String``hello
放在字符串池中,而 String
args [0]驻留在堆上。这意味着有两个对象代表相同的文字,每个对象都有它的参考。由于 ==
测试引用而非实际相等,因此比较将在大多数时间产生错误。这并不意味着它总会这样做。
当你使用 ==
测试字符串时,你实际测试的是两个 String
对象是否是同一个 Java 对象。不幸的是,这不是 Java 中字符串相等的含义。实际上,测试字符串的正确方法是使用 equals(Object)
方法。对于一对字符串,我们通常要测试它们是否由相同顺序的相同字符组成。
public class Hello2 {
public static void main(String[] args) {
if (args.length > 0) {
if (args[0].equals("hello")) {
System.out.println("Hello back to you");
} else {
System.out.println("Are you feeling grumpy today?");
}
}
}
}
但它实际上变得更糟。问题是 ==
会在某些情况下给出预期的答案。例如
public class Test1 {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
if (s1 == s2) {
System.out.println("same");
} else {
System.out.println("different");
}
}
}
有趣的是,这将打印相同,即使我们以错误的方式测试字符串。这是为什么?因为 Java 语言规范(第 3.10.5 节:字符串文字) 规定任何两个由相同字符组成的字符串>> literals <<实际上将由同一个 Java 对象表示。因此,==
测试将给出相同的文字。 (字符串文字是 interned
并在加载代码时添加到共享的字符串池,但这实际上是一个实现细节。)
为了增加混淆,Java 语言规范还规定,当你有一个连接两个字符串文字的编译时常量表达式时,它相当于一个文字。从而:
public class Test1 {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hel" + "lo";
String s3 = " mum";
if (s1 == s2) {
System.out.println("1. same");
} else {
System.out.println("1. different");
}
if (s1 + s3 == "hello mum") {
System.out.println("2. same");
} else {
System.out.println("2. different");
}
}
}
这将输出“1. same”和“2. different”。在第一种情况下,+
表达式在编译时进行评估,我们将一个 String
对象与自身进行比较。在第二种情况下,它在运行时进行评估,我们比较两个不同的 String
对象
总之,使用 ==
在 Java 中测试字符串几乎总是不正确的,但不能保证给出错误的答案。