ПрограммированиеJava разработчик

Объясните различия между shallow copy и deep copy в Java. Как они реализуются, и когда может возникнуть необходимость их использования?

Проходите собеседования с ИИ помощником Hintsage

Ответ

Shallow copy (поверхностное копирование) создаёт новый объект, но копирует ссылки на вложенные объекты, а не сами объекты. Deep copy (глубокое копирование) создает новый объект и рекурсивно копирует все объекты, вложенные в исходный, таким образом между копией и оригиналом нет общих ссылок.

Когда использовать:

  • Shallow copy достаточно, если структура объекта не содержит изменяемых вложенных объектов, или если изменять вложенные объекты не требуется.
  • Deep copy необходим, если изменяемость вложенных объектов критична и любые модификации копии не должны отражаться в оригинале.

Пример реализации:

// Пример класса с вложенным объектом class Address { String city; Address(String city) { this.city = city; } } class Person implements Cloneable { String name; Address address; Person(String name, Address address) { this.name = name; this.address = address; } // Shallow copy public Person clone() throws CloneNotSupportedException { return (Person) super.clone(); } // Deep copy public Person deepClone() { return new Person(this.name, new Address(this.address.city)); } }

Вопрос с подвохом

Можно ли реализовать deep copy при помощи метода clone()?

Ответ: Нет, стандартный метод Object.clone() реализует только "shallow copy". Чтобы получить deep copy, нужно либо вручную клонировать все вложенные поля, либо использовать сторонние библиотеки (например, Apache Commons Lang).

Пример:

@Override public Person clone() throws CloneNotSupportedException { Person cloned = (Person) super.clone(); cloned.address = new Address(this.address.city); // deep copy вручную return cloned; }

История

В крупном проекте CRM, после реализации копирования объектов через super.clone(), разработчик ожидал полной независимости копий, но изменяя адрес скопированного пользователя, менялся также адрес оригинала. Баг выявился уже после релиза и вызвал путаницу в базе клиентов.


История

При сериализации/десериализации для глубокого копирования был выбран стандартный механизм Serializable. Однако забыли объявить одно из полей вложенного объекта как Serializable, что привело к ошибкам во время работы (NotSerializableException) и потерям данных при резервном копировании.


История

На одном из микросервисов использовали библиотеку копирования BeanUtils.copyProperties для копирования DTO, не осознавая, что механизм реализует только shallow copy. После рефакторинга, появилось множество неочевидных багов, когда изменение вложенных коллекций сказывалось на оригинальных данных, полученных из базы.