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

Каким будет результат, если записать переменную через new String и " " и вызвать equals?

2.0 Middle🔥 181 комментариев
#Spring Framework

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

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

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

Результат использования new String() vs " " и вызова equals()

Это классический вопрос о String pool в Java. Нужно разобраться, что происходит при создании String разными способами и как это влияет на equals() и ==.

Создание String: new String() vs литерал

В Java есть два основных способа создания String:

// Способ 1: Литерал (рекомендуется)
String s1 = "Hello";

// Способ 2: Через new (создаёт новый объект в heap)
String s2 = new String("Hello");

Эти два способа создают разные объекты:

  • Литерал "Hello" → создаётся в String pool (специальная память для интернирования String)
  • new String("Hello") → создаётся новый объект в heap, отдельно от pool

Демонстрация с equals()

Если вызвать equals(), результат будет одинаковым для обоих случаев:

String s1 = "Hello";           // Литерал в pool
String s2 = new String("Hello");  // Новый объект в heap

// equals() сравнивает СОДЕРЖИМОЕ
System.out.println(s1.equals(s2));   // true ✅
// Содержимое одинаковое, поэтому equals() вернёт true

// А == сравнивает ССЫЛКИ (адреса объектов)
System.out.println(s1 == s2);        // false ❌
// Разные объекты, разные ссылки, поэтому == вернёт false

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

equals() в String — сравнивает содержимое

// В классе String
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String aString = (String) anObject;
        if (coder() == aString.coder()) {
            return isEqual(value, aString.value);  // Сравнивает символы
        }
    }
    return false;
}

// isEqual() проверяет, одинаковые ли символы в массиве
private static boolean isEqual(byte[] value, byte[] other) {
    if (value.length != other.length) {
        return false;
    }
    for (int i = 0; i < value.length; i++) {
        if (value[i] != other[i]) {
            return false;
        }
    }
    return true;
}

== сравнивает ссылки (адреса в памяти)

String s1 = "Hello";              // Адрес: 0x1234
String s2 = new String("Hello"); // Адрес: 0x5678 (разный!)

System.out.println(s1 == s2);  // false
// 0x1234 != 0x5678, хотя содержимое одинаковое

System.out.println(s1.equals(s2));  // true
// Содержимое: "Hello" == "Hello"

Полный пример с объяснением

public class StringEqualsExample {
    public static void main(String[] args) {
        // Создание String разными способами
        String literal1 = "Hello";              // Pool
        String literal2 = "Hello";              // Та же ссылка из pool
        String newString = new String("Hello"); // Новый объект в heap
        
        // 1. Литерал и литерал (одна и та же ссылка)
        System.out.println("literal1 == literal2: " + (literal1 == literal2));      // true
        System.out.println("literal1.equals(literal2): " + literal1.equals(literal2));  // true
        
        // 2. Литерал и new String (разные ссылки, одинаковое содержимое)
        System.out.println("literal1 == newString: " + (literal1 == newString));    // false ❌
        System.out.println("literal1.equals(newString): " + literal1.equals(newString));  // true ✅
        
        // 3. Два new String (разные ссылки, одинаковое содержимое)
        String newString2 = new String("Hello");
        System.out.println("newString == newString2: " + (newString == newString2));      // false
        System.out.println("newString.equals(newString2): " + newString.equals(newString2));  // true ✅
    }
}

// Вывод:
// literal1 == literal2: true
// literal1.equals(literal2): true
// literal1 == newString: false     ← Это частая ошибка!
// literal1.equals(newString): true  ← equals() работает правильно
// newString == newString2: false
// newString.equals(newString2): true

Визуализация в памяти

String pool (в перманентной памяти):
┌──────────────────────┐
│  "Hello" → 0x1234   │
└──────────────────────┘
    ↑           
    │
  s1, s2 (литералы указывают на один объект)

Heap (основная память):
┌──────────────────────┐
│  "Hello" → 0x5678   │ (newString)
└──────────────────────┘

┌──────────────────────┐
│  "Hello" → 0x9999   │ (newString2)
└──────────────────────┘

s1, s2 → 0x1234 (одна ссылка)
newString → 0x5678 (разная ссылка)
newString2 → 0x9999 (ещё одна разная ссылка)

Оно все содержат "Hello", но разные адреса:
s1.equals(newString) → true (содержимое одинаковое)
s1 == newString → false (адреса разные)

Типичные ошибки разработчиков

// ❌ Ошибка 1: Использование == для сравнения String
String userInput = getInput();  // Может быть new String
if (userInput == "admin") {  // ❌ Может быть false!
    grantAdminAccess();
}

// ✅ Правильно
if (userInput.equals("admin")) {  // ✅ Всегда работает
    grantAdminAccess();
}

// ❌ Ошибка 2: Создание String через new в цикле
for (int i = 0; i < 10000; i++) {
    String s = new String("Hello");  // ❌ Создаёт 10000 объектов!
    process(s);
}

// ✅ Правильно
String s = "Hello";  // ✅ Создаёт 1 объект
for (int i = 0; i < 10000; i++) {
    process(s);
}

// ❌ Ошибка 3: Не знание о String pool
String s1 = "ABC" + "DEF";      // Литерал, в pool
String s2 = new String("ABC") + "DEF";  // new String, потом +
System.out.println(s1 == s2);  // false ❌
System.out.println(s1.equals(s2));  // true ✅

Когда == может вернуть true для String?

// 1. Когда обе переменные — литералы
String s1 = "Hello";
String s2 = "Hello";
System.out.println(s1 == s2);  // true (одна ссылка из pool)

// 2. Когда один String интернирован
String s1 = new String("Hello");
String s2 = s1.intern();  // Добавляет в pool, возвращает из pool
System.out.println(s1 == s2);  // false (s1 из heap)
System.out.println(s2 == "Hello");  // true (s2 из pool)

// 3. Когда это одна и та же переменная
String s1 = new String("Hello");
String s2 = s1;
System.out.println(s1 == s2);  // true (одна и та же ссылка)

intern() — способ добавить String в pool

String s1 = new String("Hello");       // Heap
String s2 = s1.intern();                // Pool (добавляет, возвращает ссылку из pool)

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

// intern() полезен, когда нужна оптимизация памяти
// Но используй осторожно: может привести к утечкам памяти

Лучшие практики

1. Используй equals(), а не == для сравнения String

// ❌ Плохо
if (str == "expected") { }

// ✅ Хорошо
if (str.equals("expected")) { }

// ✅ Или для case-insensitive сравнения
if (str.equalsIgnoreCase("expected")) { }

2. Используй литералы вместо new String

// ❌ Плохо
String s = new String("Hello");

// ✅ Хорошо
String s = "Hello";

3. Если нужна ссылка из pool — используй intern()

// Оптимизация памяти для часто повторяющихся строк
String userId = getUserId().intern();  // Если одинаковые ID повторяются

4. Используй Objects.equals() для null-safe сравнения

// ❌ Может быть NullPointerException
String s = null;
if (s.equals("value")) { }  // ❌ NPE!

// ✅ Безопасно
if (Objects.equals(s, "value")) { }

5. Используй compareTo() для лексикографического сравнения

String a = "apple";
String b = "banana";
int result = a.compareTo(b);  // -1 (apple < banana лексикографически)

Резюме сравнения

┌───────────────────┬─────────────────────────────────────┐
│ Способ            │ Результат в памяти                  │
├───────────────────┼─────────────────────────────────────┤
│ "Hello"           │ В String pool (оптимизировано)     │
│ new String("H.")  │ В heap (отдельный объект)          │
│ "Hello" == "H."   │ true (если оба из pool)            │
│ "Hello".equals(..)│ true (сравнивает содержимое)       │
└───────────────────┴─────────────────────────────────────┘

Итог

Когда используешь new String() и литерал " " и вызываешь equals():

String literal = "Hello";
String newString = new String("Hello");

System.out.println(literal.equals(newString));  // ✅ true
System.out.println(literal == newString);       // ❌ false

equals() всегда вернёт true, потому что она сравнивает содержимое строк. Это правильное поведение и именно то, что нужно использовать для сравнения String. А == вернёт false, потому что это разные объекты с разными адресами в памяти.

Правило: Для сравнения содержимого String используй equals(), а не ==.

Каким будет результат, если записать переменную через new String и " " и вызвать equals? | PrepBro