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

Как в куче создать 2 одинаковых экземпляра String

2.0 Middle🔥 181 комментариев
#Основы Java

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

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

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

# Создание 2 одинаковых экземпляров String в памяти (куче)

Введение в String Pool

В Java String — это специальный класс с поддержкой интернирования. String Pool — это специальная область памяти, где хранятся строковые литералы. Ключевой момент: если две строки имеют одинаковое значение и созданы через литералы, они будут указывать на ОДНОТот же объект в памяти.

Вопрос этого интервью звучит парадоксально: как создать 2 ОДИНАКОВЫХ (равные по значению) экземпляра, но разные объекты в памяти (куче).

Способы создания 2 разных объектов String с одинаковым значением

1. Использование оператора new (явное выделение памяти)

public class StringHeapExample {
    public static void main(String[] args) {
        // Способ 1: Прямое использование new
        String str1 = new String("Hello");
        String str2 = new String("Hello");

        // Проверка равенства значений
        System.out.println(str1.equals(str2));        // true — значения одинаковые
        System.out.println(str1 == str2);              // false — разные объекты в памяти
        System.out.println(System.identityHashCode(str1));  // разные значения
        System.out.println(System.identityHashCode(str2));
    }
}

Объяснение:

  • new String("Hello") ВСЕГДА создаёт новый объект в куче
  • Даже если строка "Hello" уже существует в String Pool, new создаст ещё один объект
  • Литерал "Hello" может быть интернирован, но сам новый объект будет отдельным

2. Использование конструктора с char массивом

public class StringCreationMethods {
    public static void main(String[] args) {
        // Метод 2: Через char массив
        char[] chars1 = {H, e, l, l, o};
        char[] chars2 = {H, e, l, l, o};
        
        String str1 = new String(chars1);
        String str2 = new String(chars2);
        
        System.out.println(str1.equals(str2));        // true
        System.out.println(str1 == str2);              // false
    }
}

3. Использование конструктора с byte массивом

public class StringFromBytes {
    public static void main(String[] args) {
        byte[] bytes1 = "Hello".getBytes();
        byte[] bytes2 = "Hello".getBytes();
        
        String str1 = new String(bytes1);
        String str2 = new String(bytes2);
        
        System.out.println(str1.equals(str2));        // true
        System.out.println(str1 == str2);              // false
    }
}

4. Использование StringBuilder

public class StringViaBuilder {
    public static void main(String[] args) {
        StringBuilder sb1 = new StringBuilder("Hello");
        StringBuilder sb2 = new StringBuilder("Hello");
        
        String str1 = sb1.toString();
        String str2 = sb2.toString();
        
        System.out.println(str1.equals(str2));        // true
        System.out.println(str1 == str2);              // false
    }
}

Почему false?

  • StringBuilder.toString() ВСЕГДА создаёт новый String объект
  • Каждый вызов toString() — это новый объект в памяти

5. Использование substring()

public class StringSubstring {
    public static void main(String[] args) {
        String original = "Hello World";
        
        String str1 = original.substring(0, 5);
        String str2 = original.substring(0, 5);
        
        System.out.println(str1.equals(str2));        // true
        System.out.println(str1 == str2);              // false (в современных Java)
    }
}

Примечание: В Java 7 и ранее substring() возвращал интернированную строку из String Pool, но в Java 8+ каждый вызов создаёт новый объект.

6. Использование intern() селективно

public class StringIntern {
    public static void main(String[] args) {
        // Способ 6: Создание в куче и НЕ интернирование
        String str1 = new String("Hello");
        String str2 = new String("Hello");
        String str3 = "Hello";  // В String Pool
        
        System.out.println(str1.equals(str2));        // true
        System.out.println(str1 == str2);              // false
        System.out.println(str1.equals(str3));        // true
        System.out.println(str1 == str3);              // false (str1 в куче, str3 в Pool)
    }
}

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

String Pool (PermGen/Metaspace):
  ["Hello"] ← интернирована автоматически

Heap:
  [new String("Hello")] → str1 (указатель на отдельный объект)
  [new String("Hello")] → str2 (указатель на отдельный объект)
  
Оба объекта содержат одно и то же значение "Hello",
но занимают разные области памяти.

Практический пример с проверкой

public class CompleteStringExample {
    public static void main(String[] args) {
        // Создаём 2 одинаковых объекта в куче
        String str1 = new String("Interview");
        String str2 = new String("Interview");
        String str3 = "Interview";  // String Pool
        
        // Проверки
        System.out.println("str1.equals(str2): " + str1.equals(str2));       // true
        System.out.println("str1 == str2: " + (str1 == str2));              // false
        System.out.println("str1.equals(str3): " + str1.equals(str3));       // true
        System.out.println("str1 == str3: " + (str1 == str3));              // false
        
        // Значения хешей идентичностей (адреса объектов)
        System.out.println("System.identityHashCode(str1): " + 
            System.identityHashCode(str1));  // разное
        System.out.println("System.identityHashCode(str2): " + 
            System.identityHashCode(str2));  // разное
        System.out.println("System.identityHashCode(str3): " + 
            System.identityHashCode(str3));  // разное от других
    }
}

Почему это важно в Java?

String Pool и оптимизация памяти

// ❌ Неэффективно — много объектов в куче
for (int i = 0; i < 1000; i++) {
    String s = new String("constant");
}

// ✅ Эффективно — все указывают на один объект в Pool
for (int i = 0; i < 1000; i++) {
    String s = "constant";
}

Когда нужны разные объекты?

  1. Мутируемые операции: Если нужно работать с "новыми" копиями строк
  2. Сравнение по ссылке: Специфические случаи где требуется == вместо equals()
  3. Debugging: При анализе памяти и heap dumps

Заключение

Чтобы создать 2 одинаковых (по значению) экземпляра String в куче:

  1. Используй оператор new: new String("value")
  2. Или StringBuilder.toString()
  3. Проверяй через equals() — для сравнения значений
  4. Проверяй через == — для сравнения ссылок
  5. Используй System.identityHashCode() — для получения адреса объекта

Это один из типичных вопросов на собеседовании, проверяющих понимание работы String Pool и памяти в Java.