用于比较字符串的陷阱

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 中测试字符串几乎总是不正确的,但不能保证给出错误的答案。