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

Что такое поверхностное копирование?

2.0 Middle🔥 141 комментариев
#Другое

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

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

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

Поверхностное копирование (Shallow Copy)

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

Контрастное сравнение: Поверхностное vs Глубокое копирование

Поверхностное копирование:

  • Копируются только примитивные значения
  • Ссылки на объекты остаются теми же
  • Экономно по памяти
  • Может привести к неожиданным побочным эффектам

Глубокое копирование:

  • Копируются все данные, включая вложенные объекты
  • Каждый объект имеет отдельную копию
  • Требует больше памяти
  • Полностью независимые копии

Пример поверхностного копирования

class Address {
    public String city;
    public String country;
    
    public Address(String city, String country) {
        this.city = city;
        this.country = country;
    }
}

class Person {
    public String name;
    public Address address;  // Вложенный объект
    
    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }
}

// Поверхностное копирование
Address addr = new Address("Moscow", "Russia");
Person original = new Person("Alice", addr);

// Простое копирование по ссылке (плохой способ)
Person shallowCopy = original;  // Обе переменные указывают на одно место в памяти

// Когда изменяем данные
original.address.city = "Saint Petersburg";

// Изменения видны в обеих переменных!
System.out.println(original.address.city);  // Saint Petersburg
System.out.println(shallowCopy.address.city);  // Saint Petersburg (проблема!)

Реальное поверхностное копирование через создание нового объекта

class Person implements Cloneable {
    public String name;  // Примитивный тип (строка - это специальный случай)
    public Address address;
    
    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }
    
    // Поверхностное копирование через Object.clone()
    @Override
    public Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();  // Создаёт новый объект, но ссылки остаются теми же
    }
}

// Использование
Address addr = new Address("Moscow", "Russia");
Person original = new Person("Alice", addr);
Person shallowCopy = original.clone();

// Примитивное поле скопировалось
shallowCopy.name = "Bob";
System.out.println(original.name);  // Alice (независимо)
System.out.println(shallowCopy.name);  // Bob (независимо)

// Но объект address остался тем же!
shallowCopy.address.city = "Saint Petersburg";
System.out.println(original.address.city);  // Saint Petersburg (проблема!)
System.out.println(shallowCopy.address.city);  // Saint Petersburg (одинаково)

Поверхностное копирование с массивами

int[] original = {1, 2, 3, 4, 5};
int[] shallowCopy = original;  // Просто ссылка, не копирование

shallowCopy[0] = 99;
System.out.println(original[0]);  // 99 (изменение видно в original)

// Правильное поверхностное копирование массива
int[] properShallowCopy = new int[original.length];
System.arraycopy(original, 0, properShallowCopy, 0, original.length);

properShallowCopy[0] = 100;
System.out.println(original[0]);  // 99 (не изменилось)
System.out.println(properShallowCopy[0]);  // 100

Поверхностное копирование с коллекциями

List<String> original = new ArrayList<>(Arrays.asList("A", "B", "C"));

// Поверхностное копирование через конструктор
List<String> shallowCopy = new ArrayList<>(original);

// Изменение элементов списка
shallowCopy.set(0, "X");
System.out.println(original.get(0));  // A (независимо)
System.out.println(shallowCopy.get(0));  // X

// Пример с объектами внутри списка
List<Person> originalList = new ArrayList<>();
originalList.add(new Person("Alice", new Address("Moscow", "Russia")));

List<Person> shallowCopyList = new ArrayList<>(originalList);

// Изменяем объект Person внутри списка
shallowCopyList.get(0).address.city = "SPB";
System.out.println(originalList.get(0).address.city);  // SPB (проблема!)

Поверхностное копирование с Map

Map<String, Address> original = new HashMap<>();
original.put("home", new Address("Moscow", "Russia"));
original.put("work", new Address("SPB", "Russia"));

// Поверхностное копирование
Map<String, Address> shallowCopy = new HashMap<>(original);

// Изменение значения в копии
shallowCopy.get("home").city = "Kazan";
System.out.println(original.get("home").city);  // Kazan (та же ссылка!)

Когда использовать поверхностное копирование

  • Когда вложенные объекты не изменяются: Если Address является immutable, поверхностного копирования достаточно
  • Для производительности: Когда нужно быстро скопировать объект с большим количеством вложенных данных
  • Специальные случаи: Когда необходимо сохранить ссылки на те же объекты

Когда это становится проблемой

// Опасный код
Person person1 = new Person("Alice", new Address("Moscow", "Russia"));
Person person2 = new Person("Bob", person1.address);  // Ошибка дизайна!

// Если изменить адрес в person1, он изменится и в person2
person1.address.city = "SPB";
System.out.println(person2.address.city);  // SPB (неожиданно!)

Итоговые замечания

Поверхностное копирование — это низкоуровневая операция, которая часто используется в Java неосознанно. Разработчик должен чётко понимать, работает ли он с примитивными типами или с объектами, чтобы избежать ошибок. Для сложных структур рекомендуется использовать глубокое копирование или создавать новые объекты явно, чтобы избежать неожиданных побочных эффектов.