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

Почему при сравнении двух string это два разных объекта?

1.0 Junior🔥 132 комментариев
#JVM и память

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

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

Причины создания разных объектов при сравнении строк в Java/Kotlin

Когда вы сталкиваетесь с ситуацией, где две строки (String) являются разными объектами при сравнении, это связано с особенностями работы строкового пула (String Pool) и различными способами создания строк в Java/Kotlin. Давайте разберем ключевые механизмы.

Как работает String Pool

String Pool — это специальная область памяти в куче (heap), где JVM хранит уникальные строковые литералы. Основная цель — оптимизация памяти и производительности за счет повторного использования неизменяемых (immutable) строк.

// Java пример
String s1 = "hello";        // Создается в String Pool
String s2 = "hello";        // Берется из String Pool - ТОТ ЖЕ объект
System.out.println(s1 == s2); // true (одинаковые ссылки)

String s3 = new String("hello"); // Явное создание НОВОГО объекта
System.out.println(s1 == s3);    // false (разные объекты!)
// Kotlin пример
val s1 = "hello"           // Создается в String Pool  
val s2 = "hello"           // Берется из String Pool
println(s1 === s2)         // true (identical references)

val s3 = String("hello".toCharArray()) // Новый объект
println(s1 === s3)         // false (разные объекты)

Основные причины создания разных объектов

  1. Использование конструктора new String()

    • При явном вызове конструктора всегда создается новый объект в heap, даже если такая же строка уже существует в пуле.
  2. Динамическое создание строк во время выполнения

    • Строки, созданные через конкатенацию, чтение из файла, сетевых запросов или другие runtime-операции, обычно не помещаются в пул автоматически:
String s1 = "hello";
String s2 = "hel" + "lo";           // Компилятор оптимизирует - тот же объект
String s3 = "hel";
String s4 = s3 + "lo";              // Runtime конкатенация - НОВЫЙ объект
String s5 = new StringBuilder("hel").append("lo").toString(); // Новый объект

System.out.println(s1 == s2);       // true
System.out.println(s1 == s4);       // false
System.out.println(s1 == s5);       // false
  1. Отсутствие интернирования (intern())
    • Метод intern() помещает строку в пул или возвращает ссылку на уже существующую:
val s1 = "hello"
val s2 = String("hello".toCharArray())
val s3 = s2.intern()  // Теперь s3 ссылается на объект из пула

println(s1 === s2)    // false
println(s1 === s3)    // true
  1. Разные источники данных
    • Строки из разных источников (база данных, пользовательский ввод, десериализация) обычно являются отдельными объектами.

Правильное сравнение строк

НИКОГДА не используйте == в Java или === в Kotlin для сравнения содержимого строк! Эти операторы сравнивают ссылки на объекты, а не их содержимое.

// Java: для сравнения содержимого используйте equals()
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2);       // false (разные объекты)
System.out.println(s1.equals(s2));  // true (одинаковое содержимое)
// Kotlin: == вызывает equals() под капотом
val s1 = String("hello".toCharArray())
val s2 = String("hello".toCharArray())
println(s1 === s2)    // false (разные объекты)
println(s1 == s2)     // true (сравнивается содержимое)

Практические рекомендации

  • Для литералов — JVM автоматически использует String Pool
  • Для динамических строк — используйте equals() для сравнения содержимого
  • При необходимости пулинга — вызывайте intern() (с осторожностью, может привести к утечке памяти)
  • В Android — будьте внимательны с String.format(), StringBuilder, StringBuffer — они создают новые объекты
  • В Kotlin== безопасен для сравнения содержимого, === сравнивает ссылки

Понимание этих механизмов критически важно для оптимизации памяти в Android-приложениях, где необоснованное создание строковых объектов может привести к частым сборкам мусора и снижению производительности.