В Java shallow copy (поверхностное копирование) — это копирование объекта, при котором копируются только ссылки на вложенные объекты, а не сами объекты. Таким образом, изменения во вложенных объектах отразятся и в копии, и в оригинале. Deep copy (глубокое копирование) подразумевает создание новых независимых копий всех вложенных объектов.
class Address implements Cloneable { String city; Address(String city) { this.city = city; } protected Object clone() throws CloneNotSupportedException { return super.clone(); } } class Person implements Cloneable { String name; Address address; Person(String name, Address address) { this.name = name; this.address = address; } protected Object clone() throws CloneNotSupportedException { return super.clone(); } } // Использование Person p1 = new Person("Ivan", new Address("Moscow")); Person p2 = (Person) p1.clone(); p2.address.city = "Petersburg"; System.out.println(p1.address.city); // Выведет "Petersburg" — поверхностное копирование
class Address implements Cloneable { String city; Address(String city) { this.city = city; } protected Object clone() throws CloneNotSupportedException { return new Address(this.city); } } class Person implements Cloneable { String name; Address address; Person(String name, Address address) { this.name = name; this.address = address; } protected Object clone() throws CloneNotSupportedException { Person cloned = (Person) super.clone(); cloned.address = (Address) address.clone(); return cloned; } }
Глубокое копирование позволяет создать полностью независимую копию объекта.
Вопрос: Можно ли использовать метод clone() для гарантированного глубокого копирования произвольного сложного объекта?
Ответ: Нет, стандартный clone() делает только поверхностное копирование (shallow copy). Для глубокого копирования необходимо переопределять clone() во всех вложенных классах и явно клонировать все поля-ссылки. Также многие коллекции и стандартные классы не поддерживают глубокое клонирование.
История
Коллекция пользователей и их адресов клонировалась методом clone() без глубокого копирования. После изменения адреса у одного пользователя изменился адрес и у другого, потому что копировалась ссылка. Данные в базе "потекли" между пользователями.
История
При сериализации сложного объекта с вложенными коллекциями использовали clone(), а не глубокую копию. После восстановления из сериализации данные оказались неконсистентными и произошли NPE.
История
В REST API DTO-объекты клонировались по умолчанию через BeanUtils.copyProperties(), что приводило к разделению одной и той же коллекции между клиентами. В результате пользователь мог видеть и редактировать чужие данные.