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

Что будет если выполнить команду a += d, где a и d - строки?

1.0 Junior🔥 181 комментариев
#ORM и Hibernate#Spring Boot и Spring Data#Базы данных и SQL

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

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

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

Операция += со строками в Java

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

Простой ответ

String a = "Hello";
String d = " World";
a += d;
System.out.println(a); // Hello World

Результат: переменная a содержит новую строку "Hello World".

Но что происходит на самом деле?

Это не просто конкатенация на месте — это создание новой строки с присваиванием.

String a = "Hello";
String d = " World";

// a += d эквивалентно:
// a = a + d

// Что происходит step by step:
// 1. Создается новая строка: a.concat(d)
// 2. Результат присваивается переменной a
a = a + d;

System.out.println(a); // "Hello World"

Как это работает: StringBuilder за кулисами

Java компилятор оптимизирует строковую конкатенацию:

// Исходный код
String a = "Hello";
String d = " World";
a += d;

// Что компилятор компилирует (bytecode):
String a = "Hello";
String d = " World";
a = new StringBuilder()
    .append(a)      // Append исходной строки
    .append(d)      // Append добавляемой строки
    .toString();    // Преобразование в String

Если бы это было в цикле:

// ❌ ПЛОХО - неэффективно
String result = "";
for (int i = 0; i < 1000; i++) {
    result += "x"; // Создает 1000 новых StringBuilder объектов!
}

// Компилятор переводит в:
String result = "";
for (int i = 0; i < 1000; i++) {
    result = new StringBuilder()
        .append(result)
        .append("x")
        .toString(); // новый объект каждый раз
}
// Это O(n²) операция!

// ✅ ХОРОШО - эффективно
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    builder.append("x");
}
String result = builder.toString();
// O(n) операция

Важные детали

1. String immutable (неизменяемая)

String a = "Hello";
String d = " World";
String original = a; // Сохраняем ссылку на исходную строку

a += d; // Создает новую строку

System.out.println(original); // "Hello" - не изменилась!
System.out.println(a);         // "Hello World"

// original и a указывают на разные объекты
System.out.println(original == a); // false
System.out.println(original.equals(a)); // false

2. Почему String immutable?

public class StringImmutabilityBenefits {
    /**
     * Преимущества immutable строк:
     * 
     * 1. Thread safety - не нужно синхронизация
     *    String можно безопасно передавать между потоками
     * 
     * 2. String pooling - Java может reuse строки
     *    String s1 = "Hello";
     *    String s2 = "Hello"; // Может быть тот же объект
     * 
     * 3. Security - HashMap keys, passwords, credentials
     *    Нельзя случайно изменить критические данные
     * 
     * 4. Performance - hashCode может кэшироваться
     *    Для HashMap/HashSet это важно
     */
}

// Пример pooling
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");

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

3. Производительность в разных ситуациях

public class StringConcatenationPerformance {
    
    // Сценарий 1: Одна операция += (хорошо)
    public String scenario1() {
        String a = "Hello";
        String d = " World";
        a += d; // Оптимизировано в StringBuilder
        return a; // "Hello World"
    }
    
    // Сценарий 2: Несколько +=  (хорошо - компилятор объединит)
    public String scenario2() {
        String result = "Hello";
        result += " ";
        result += "World";
        result += "!";
        return result;
        // Компилятор создаст один StringBuilder для всех
    }
    
    // Сценарий 3: Цикл с += (плохо - создает объект на каждой итерации)
    public String scenario3() {
        String result = "";
        for (int i = 0; i < 1000; i++) {
            result += i; // ПЛОХО!
        }
        return result;
    }
    
    // Сценарий 4: Цикл со StringBuilder (хорошо)
    public String scenario4() {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < 1000; i++) {
            result.append(i); // ХОРОШО
        }
        return result.toString();
    }
}

Null и пустые строки

// Случай 1: null значение
String a = null;
String d = " World";
a += d; // Что будет?

// Java конвертирует:
// a = a + d;
// a = null + " World";
// a = "null World"; // null конвертируется в строку "null"

System.out.println(a); // "null World"

// Случай 2: пустая строка
String a = "";
String d = "Hello";
a += d;
System.out.println(a); // "Hello" - просто добавилась строка

// Случай 3: обе пустые
String a = "";
String d = "";
a += d;
System.out.println(a); // "" - остается пустой
System.out.println(a == ""); // true - может быть из pool

Сравнение с другими типами

public class PlusOperatorComparison {
    
    // Со строками - создает новую строку
    public void stringConcatenation() {
        String a = "Hello";
        String d = " World";
        a += d; // Новая строка
    }
    
    // С числами - выполняет арифметическую операцию
    public void numberAddition() {
        int a = 5;
        int d = 3;
        a += d; // a = 8, та же переменная (примитив, не объект)
    }
    
    // Со StringBuilder - модификация на месте
    public void stringBuilder() {
        StringBuilder a = new StringBuilder("Hello");
        String d = " World";
        a.append(d); // Модифицируется существующий объект!
        // a указывает на тот же объект StringBuilder
    }
}

Bytecode уровень

// Как это выглядит в bytecode
public class StringConcatBytecode {
    public static void main(String[] args) {
        String a = "Hello";
        String d = " World";
        a += d;
    }
}

// Эквивалентный bytecode (упрощенно):
// 1. Создать StringBuilder
// 2. Append a
// 3. Append d
// 4. toString()
// 5. Присвоить результат переменной a

Оптимизация в Java 9+

Java 9 добавила invokedynamic для более эффективной конкатенации:

// В Java 8 и раньше - явное использование StringBuilder
// В Java 9+ - компилятор использует более оптимальные методы
// через MethodHandle и ByteCodeGenerator

// Результат - эффективнее памяти и быстрее работает
String a = "Hello";
String d = " World";
a += d; // Работает еще быстрее в Java 21!

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

public class StringConcatBestPractices {
    
    // ❌ ПЛОХО - в цикле
    public String badWay(List<String> items) {
        String result = "";
        for (String item : items) {
            result += item + ",";
        }
        return result;
    }
    
    // ✅ ХОРОШО - используй StringBuilder
    public String goodWay(List<String> items) {
        StringBuilder sb = new StringBuilder();
        for (String item : items) {
            sb.append(item).append(",");
        }
        return sb.toString();
    }
    
    // ✅ ХОРОШО - использую String.join()
    public String betterWay(List<String> items) {
        return String.join(",", items);
    }
    
    // ✅ ХОРОШО - используй String format
    public String anotherGood(String name, int age) {
        return String.format("Name: %s, Age: %d", name, age);
    }
    
    // ✅ ХОРОШО - Java 15+ text block (multiline)
    public String textBlock() {
        String html = """
            <div>
                <h1>Hello</h1>
                <p>World</p>
            </div>
            """;
        return html;
    }
}

Заключение

Когда вы выполняете a += d со строками:

  1. Java создает новую строку, не изменяет старую (immutable)
  2. Компилятор оптимизирует это в StringBuilder
  3. На памяти создается новый объект String
  4. Старая ссылка перенаправляется на новый объект
  5. В циклах это может быть неэффективно - используйте StringBuilder
  6. На современном Java эта оптимизация очень быстрая

Ключевое правило: помните про immutability строк и используйте StringBuilder когда нужна конкатенация в цикле.