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

Что произойдет при выполнении команды String a = "Hello "+"World"?

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

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

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

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

Конкатенация строк: String a = "Hello " + "World"

Эта простая на первый взгляд операция демонстрирует несколько важных концепций Java: компиляцию, оптимизацию компилятора и работу со строками. Давайте разберёмся, что происходит на разных уровнях.

1. Что видит разработчик

String a = "Hello " + "World";
System.out.println(a);  // Выведет: Hello World

На поверхности всё просто: две строки конкатенируются (объединяются) в одну, и переменной a присваивается значение "Hello World".

2. Что происходит на уровне компилятора

Ключевой момент: Обе строки — это литералы (константы), известные на этапе компиляции. Компилятор не создаёт две отдельные строки, а выполняет оптимизацию:

// То что мы пишем:
String a = "Hello " + "World";

// То что компилятор преобразует (оптимизация):
String a = "Hello World";

Это называется compile-time constant folding — компилятор вычисляет результат конкатенации на этапе компиляции, а не во время выполнения.

3. Байт-код и процесс компиляции

Давайте посмотрим, что происходит на уровне байт-кода:

public class StringConcatenation {
    public static void main(String[] args) {
        String a = "Hello " + "World";
        String b = "Hello ";
        String c = b + "World";
    }
}

Если скомпилировать и посмотреть в javap -c:

public static void main(java.lang.String[]);
    0: ldc           #2    // String Hello World  <- ОДНА строка!
    2: astore_1
    3: ldc           #3    // String Hello 
    5: astore_2
    6: aload_2
    7: invokedynamic  #4,  0  // InvokeDynamic makeConcatWithConstants
   12: astore_3
   13: return

Обратите внимание:

  • Для a: загружается уже готовая строка "Hello World" (индекс #2)
  • Для c: используется invokedynamic с методом makeConcatWithConstants, потому что b — это переменная

4. Пул строк (String Pool)

Все строковые литералы хранятся в специальной области памяти — String Pool:

String a = "Hello " + "World";  // Результат: "Hello World" в String Pool
String b = "Hello World";        // Указывает на ТУ ЖЕ строку в Pool
String c = new String("Hello World");  // Новый объект в Heap, но содержимое = String Pool

System.out.println(a == b);  // true  — один и тот же объект
System.out.println(a == c);  // false — разные объекты в памяти
System.out.println(a.equals(c));  // true — одинаковое содержимое

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

Так как обе строки — это литералы, компилятор создаёт одну строку в String Pool, и обе переменные указывают на неё. Это экономит память.

5. Процесс во время выполнения (Runtime)

┌─────────────────────────────────────┐
│         HEAP (Куча)                 │
├─────────────────────────────────────┤
│  Переменная a ──┐                   │
│  Переменная b ──┼──→ [Hello World]  │
│                 │   (в String Pool) │
└─────────────────────────────────────┘

Что происходит:

  1. Компилятор анализирует выражение "Hello " + "World"
  2. Обе части — это литералы, известные на момент компиляции
  3. Компилятор вычисляет результат: "Hello World"
  4. В String Pool добавляется одна строка (если её там ещё нет)
  5. Переменной a присваивается ссылка на эту строку

6. Сравнение с конкатенацией переменных

Делаем сравнение с похожим кодом, где используются переменные:

String a = "Hello " + "World";      // Compile-time constant folding
System.out.println(a);               // Hello World

String part1 = "Hello ";
String part2 = "World";
String b = part1 + part2;            // Runtime конкатенация
System.out.println(b);               // Hello World

System.out.println(a == b);          // false! — разные объекты
System.out.println(a.equals(b));     // true  — одинаковое содержимое

Почему a == b вернул false?

  • a указывает на строку в String Pool
  • b указывает на новый объект String, созданный во время выполнения (в результате конкатенации переменных)
  • Даже если содержимое одинаково, это разные объекты

7. Как на самом деле компилятор обрабатывает конкатенацию

В современной Java (7+) конкатенация строк обрабатывается через StringBuilder (или новый механизм в Java 9+):

// Что пишем:
String a = "Hello " + "World";

// ДО компиляции (что делает компилятор для переменных):
StringBuilder sb = new StringBuilder();
sb.append("Hello ");
sb.append("World");
String a = sb.toString();

// НО для литералов компилятор оптимизирует и делает это на этапе компиляции:
String a = "Hello World";  // Готовый результат
// Пример с переменными:
String x = "Hello ";
String y = "World";
String z = x + y;

// Компилятор преобразует в:
StringBuilder sb = new StringBuilder();
sb.append(x);
sb.append(y);
String z = sb.toString();  // Новый объект, создано во время выполнения

8. Java 9+ и новый механизм конкатенации

Начиная с Java 9, конкатенация реализована через invokedynamic и StringConcatFactory:

String a = "Hello " + "World";
String b = "Value: " + 42 + " items";

Байт-код использует:

invokedynamic #4,  0  // makeConcatWithConstants

Это позволяет JVM оптимизировать конкатенацию более эффективно и изменять реализацию без изменения байт-кода.

9. Практические следствия

Оптимизация:

// ХОРОШО: компилятор оптимизирует
String result = "SQL: " + "SELECT * FROM" + " users";

// ПЛОХО: не оптимизируется (переменная в цикле)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    String data = "Item: " + i;  // Каждый раз новый объект!
    sb.append(data);
}

// ХОРОШО: используем StringBuilder явно
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append("Item: ").append(i);
}

10. Проверим в действии

public class StringTest {
    public static void main(String[] args) {
        // Пример 1: Два литерала
        String a = "Hello " + "World";
        String b = "Hello World";
        String c = "Hello World";
        
        System.out.println(a == b);  // true  (один объект в String Pool)
        System.out.println(b == c);  // true  (один объект в String Pool)
        
        // Пример 2: С переменной
        String x = "Hello ";
        String y = x + "World";
        String z = "Hello World";
        
        System.out.println(y == z);  // false (y - новый объект, z - из Pool)
        System.out.println(y.equals(z));  // true (содержимое одинаково)
        
        // Пример 3: Использование intern()
        String y2 = y.intern();  // Добавляем в String Pool
        System.out.println(y2 == z);  // true! (оба в Pool)
    }
}

// Вывод:
// true
// true
// false
// true
// true

Итоговая схема: что происходит

Код:                    String a = "Hello " + "World";
                                 ↓
Парсинг:               Две строковые константы
                                 ↓
Оптимизация:           Compile-time constant folding
                                 ↓
Результат компиляции: String a = "Hello World";  (одна строка в Pool)
                                 ↓
Время выполнения:      a указывает на объект в String Pool
                                 ↓
Результат:             a = "Hello World" (ссылка на String Pool)

Ключевые выводы

  1. Компилятор оптимизирует конкатенацию строковых литералов на этапе компиляции
  2. String Pool хранит результат, экономя память
  3. Переменная a указывает на одну строку в памяти, а не на две
  4. Это работает для литералов — для переменных конкатенация происходит во время выполнения
  5. Оператор == сравнивает ссылки, не содержимое — это почему a == b может быть false даже если содержимо одинаково

Это один из тех вопросов, который показывает, как глубоко вы понимаете Java: не просто знаете синтаксис, но понимаете, что происходит под капотом.