Looking for java Keywords? Try Ask4Keywords

Java Language Pitfall: использование == для сравнения строк


пример

Общей ошибкой для начинающих 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?");
            }
        }
    }
}

Вышеупомянутая программа должна проверять первый аргумент командной строки и печатать разные сообщения, когда она не является словом «привет». Но проблема в том, что это не сработает. Эта программа выведет «Вы чувствуете себя сердитой сегодня?» независимо от того, что первый аргумент командной строки.

В этом конкретном случае 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: литералы строк) предусматривает, что любые две строки >> литералы <<, состоящие из одних и тех же символов, будут фактически представлены одним и тем же объектом Java. Следовательно, тест == даст истину для равных литералов. (Строковые литералы «интернированы» и добавляются в общий «пул строк», когда ваш код загружен, но это фактически деталь реализации.)

Чтобы добавить к путанице, спецификация языка 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 почти всегда неверно, но не гарантированно дает неправильный ответ.