Ответ
Поверхностное копирование (Shallow Copy) создает новый объект, но копирует значения всех полей "как есть". Для полей-примитивов копируется значение, для полей-ссылок копируется сама ссылка (адрес памяти). В результате оба объекта начинают ссылаться на одни и те же вложенные объекты.
Глубокое копирование (Deep Copy) создает новый объект и рекурсивно создает копии всех объектов, на которые ссылаются его поля. В итоге получается полностью независимая копия всего графа объектов.
Пример, иллюстрирующий разницу:
class Address {
String city;
Address(String city) { this.city = city; }
}
class Person implements Cloneable {
String name;
Address address;
// Поверхностное клонирование (стандартная реализация)
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // address будет скопирована как ссылка
}
// Глубокое клонирование (пользовательская реализация)
public Person deepCopy() {
Person copy = new Person();
copy.name = this.name;
copy.address = new Address(this.address.city); // Создаем новый объект Address
return copy;
}
}
// Использование
Person original = new Person("Alice", new Address("Moscow"));
Person shallowCopy = (Person) original.clone();
Person deepCopy = original.deepCopy();
original.address.city = "SPb";
// shallowCopy.address.city теперь тоже "SPb" (общая ссылка)!
// deepCopy.address.city останется "Moscow" (независимая копия).
Когда что использовать:
- Поверхностное копирование подходит для объектов, содержащих только примитивы или неизменяемые (immutable) ссылочные поля.
- Глубокое копирование необходимо, если объект содержит изменяемые (mutable) ссылочные поля, и вы хотите обеспечить полную независимость копии.
Ответ 18+ 🔞
А, ну это классика, ёпта! Сейчас объясню на пальцах, без всей этой академической суеты.
Представь, что у тебя есть папка с документами. Поверхностное копирование — это как если ты берешь и ксерокопируешь только обложку этой папки. На копии будет написано то же самое: «Документы Васьки». Но внутри-то, блядь, лежат те же самые листочки! Ты и оригинал, и копия — вы оба тычете пальцем в одну и ту же стопку бумаг. Изменяешь документ в оригинале — он меняется и в «копии», потому что копия-то, сука, только обложка!
А вот глубокое копирование — это уже серьёзно. Ты берешь эту папку, идешь нахуй к ксероксу и начинаешь копировать ВСЁ: и обложку, и каждый листочек внутри, и даже те стикеры, что на листочках налеплены. В итоге получаешь вторую, абсолютно независимую папку. Хоть в оригинальной папке все листы сожги — в копии они останутся целыми и невредимыми.
Смотри на коде, тут всё видно:
// Допустим, есть класс «Адрес», простая ссылочная хуйня.
class Address {
String city;
Address(String city) { this.city = city; }
}
// А вот и наш «Человек», который живёт по адресу.
class Person implements Cloneable {
String name; // Примитив, строка (в Java она immutable, но для примера сойдёт)
Address address; // А вот это уже ссылка, опасная зона!
// Стандартный clone() делает ПОВЕРХНОСТНУЮ копию. Лениво, по-быстрому.
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // name скопируется, а address — нет! Скопируется только ссылка на него!
}
// А вот это уже наш, ручной, ГЛУБОКОКИЙ метод. Тут мы вручную всё клонируем.
public Person deepCopy() {
Person copy = new Person();
copy.name = this.name; // Имя копируем значение
// А вот адрес — не копируем ссылку, а создаём НОВЫЙ объект с теми же данными!
copy.address = new Address(this.address.city);
return copy;
}
}
Теперь смотри, как это работает в жизни, блядь:
Person original = new Person("Васька", new Address("Москва"));
// Поверхностная копия (через clone())
Person shallowCopy = (Person) original.clone();
// Глубокая копия (наша самописная)
Person deepCopy = original.deepCopy();
// А теперь Васька решил переехать в Питер!
original.address.city = "Санкт-Петербург";
// И что мы имеем?
System.out.println(shallowCopy.address.city); // Выведет "Санкт-Петербург"! Охуеть!
// Потому что shallowCopy.address — это та же самая ссылка, что и у original. Один и тот же объект в памяти!
System.out.println(deepCopy.address.city); // А вот тут выведет "Москва"!
// Потому что deepCopy.address — это уже новый, отдельный объект. Его Питер не колышет!
Так когда что юзать, чтобы не обосраться?
- Поверхностную копию можно делать, если твой объект — как чемодан без потайного отделения. Только примитивы (
int,boolean) или неизменяемые (immutable) ссылки вродеString(хотя это тоже ссылка, но её не изменишь). Ну или если ты сознательно хочешь, чтобы объекты делились данными. - Глубокую копию делай ВСЕГДА, когда у тебя внутри объекта сидят изменяемые (
mutable) штуки: списки (List), массивы, другие кастомные объекты. Чтобы твоя копия жила своей жизнью, а не дергалась, как марионетка, когда меняют оригинал.
Короче, если не хочешь неожиданных сюрпризов, когда изменение в одном месте неведомой хуйней ломает всё в другом — думай головой, что копируешь. А то будет тебе, как Герасиму с Муму, блядь.