← Назад к вопросам

Почему нельзя сравнивать строки через ==?

1.6 Junior🔥 271 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Почему нельзя сравнивать строки через ==?

В Java оператор == сравнивает ссылки (references) на объекты в памяти, а не их содержимое. Для сравнения содержимого строк нужно использовать метод .equals() или .equalsIgnoreCase().

Разница между == и .equals()

Оператор ==

Сравнивает, указывают ли две переменные на один и тот же объект в памяти:

String s1 = new String("Hello");
String s2 = new String("Hello");
String s3 = s1;

System.out.println(s1 == s2);  // false (разные объекты в памяти)
System.out.println(s1 == s3);  // true (одна и та же ссылка)
System.out.println(s1.equals(s2));  // true (одинаковое содержимое)

Визуально в памяти:

Heap (куча памяти):
┌─────────────────┐
│ String[0]: "Hello" │ ← s1 указывает сюда
└─────────────────┘
┌─────────────────┐
│ String[1]: "Hello" │ ← s2 указывает сюда
└─────────────────┘

s1 == s2 → false (разные адреса в памяти)
s1.equals(s2) → true (одинаковое содержимое)

Почему это происходит

В Java String — это объект (reference type), а не примитивный тип. Каждый вызов new String() создаёт новый объект в памяти с собственным адресом.

Оператор == был разработан для сравнения адресов в памяти, а не для сравнения содержимого. Это работает правильно для примитивных типов:

int a = 5;
int b = 5;
System.out.println(a == b);  // true (одинаковые значения)

Но для объектов нужно переопределить логику сравнения через метод .equals().

String Pool и исключение

Есть одно исключение — String Pool (строковый пул). Когда вы создаёте строку без new, Java помещает её в специальный пул строк:

String s1 = "Hello";  // создаётся в String Pool
String s2 = "Hello";  // берётся из String Pool (уже существует)
String s3 = new String("Hello");  // создаётся в Heap, а не в Pool

System.out.println(s1 == s2);  // true! (одна и та же ссылка из Pool)
System.out.println(s1 == s3);  // false (s3 в отдельном месте Heap)
System.out.println(s1.equals(s3));  // true (содержимое одинаково)

Визуально:

String Pool (специальная область памяти):
┌──────────────┐
│ "Hello"      │ ← s1 и s2 указывают сюда
└──────────────┘

Heap:
┌──────────────┐
│ "Hello"      │ ← s3 указывает сюда
└──────────────┘

Практические примеры проблем

Пример 1: Опасная логика

public boolean isAdmin(String role) {
    if (role == "admin") {  // ОПАСНО!
        return true;
    }
    return false;
}

String userRole = new String("admin");
boolean result = isAdmin(userRole);
System.out.println(result);  // false! (хотя должно быть true)

Пример 2: Чтение из БД или сети

String usernameFromDB = getUsernameFromDatabase();
if (usernameFromDB == "admin") {  // НЕПРАВИЛЬНО
    // Это никогда не сработает, так как
    // БД всегда возвращает новый объект String
}

// Правильно:
if (usernameFromDB.equals("admin")) {
    // Правильная логика
}

Правильный способ сравнения

String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");

// 1. .equals() — сравнение содержимого (чувствительно к регистру)
if (s1.equals(s2)) {  // true
    System.out.println("Содержимое одинаковое");
}

// 2. .equalsIgnoreCase() — игнорирует регистр
if (s1.equalsIgnoreCase("HELLO")) {  // true
    System.out.println("Одинаковое (без учёта регистра)");
}

// 3. .compareTo() — лексикографическое сравнение
String a = "Apple";
String b = "Banana";
int result = a.compareTo(b);  // отрицательное число (a < b)
if (result < 0) {
    System.out.println("Apple раньше Banana в алфавитном порядке");
}

// 4. .contentEquals() — для CharSequence
StringBuilder sb = new StringBuilder("Hello");
if (s1.contentEquals(sb)) {  // true
    System.out.println("Содержимое одинаковое");
}

Реализация .equals() в String

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;  // если это один объект
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            // посимвольное сравнение
            for (int i = 0; i < n; i++) {
                if (value[i] != anotherString.value[i]) {
                    return false;
                }
            }
            return true;
        }
    }
    return false;
}

Ключевые моменты

  1. == сравнивает ссылки, .equals() сравнивает содержимое
  2. String Pool создаёт исключение, но надёжнее использовать .equals()
  3. Всегда используйте .equals() для сравнения строк
  4. NullPointerException — .equals() может выбросить, если строка null
  5. Objects.equals() — безопасный способ, игнорирует null
// Безопасное сравнение (Java 7+)
if (Objects.equals(s1, s2)) {
    // Работает даже если s1 или s2 == null
}

Это одно из самых частых "подводных камней" Java для новичков, поэтому запомните правило: для строк всегда используйте .equals(), а не ==.

Почему нельзя сравнивать строки через ==? | PrepBro