← Назад к вопросам
Каким будет результат, если записать переменную через 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(), а не ==.