Как выполнить глубокое копирование объекта в Java?

Ответ

Глубокое копирование создает новый объект и рекурсивно копирует все поля, включая вложенные объекты, а не просто копирует ссылки на них.

Основные подходы:

  1. Ручная реализация (через конструктор копирования или clone()):

    public class Person {
        private String name;
        private Address address; // mutable объект
    
        // Конструктор глубокого копирования
        public Person(Person original) {
            this.name = original.name;
            this.address = new Address(original.address); // Копируем вложенный объект
        }
    
        // Или через переопределение clone() (менее предпочтительно)
        @Override
        protected Person clone() throws CloneNotSupportedException {
            Person cloned = (Person) super.clone();
            cloned.address = this.address.clone(); // Требует, чтобы Address тоже реализовывал Cloneable
            return cloned;
        }
    }
  2. Сериализация/десериализация:

    import java.io.*;
    
    public static <T extends Serializable> T deepCopy(T object) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        out.writeObject(object);
        out.flush();
    
        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream in = new ObjectInputStream(byteIn);
        return (T) in.readObject();
    }
    • Плюсы: Универсально для любых графов объектов.
    • Минусы: Низкая производительность; все объекты в графе должны реализовывать Serializable.
  3. Использование библиотек (рекомендуется для сложных объектов):

    • Apache Commons Lang: SerializationUtils.clone(object) (требует Serializable).
    • Jackson/Gson: Сериализация в JSON и обратная десериализация.
      // Пример с Gson
      Gson gson = new Gson();
      Person copy = gson.fromJson(gson.toJson(original), Person.class);

Выбор метода зависит от сложности объекта, требований к производительности и необходимости сериализуемости.

Ответ 18+ 🔞

А, ну это ж классика, блядь! Глубокое копирование, ёпта! Это когда ты не просто ссылку на ту же самую квартиру копируешь, а строишь новую, блядь, хрущёвку, со всеми потрохами, чтобы там свой бардак завести можно было, и в оригинале ничего не менялось. Не то что поверхностное, где вы оба за один хуй дергаетесь — там один поменял, второй охуел.

Ну, вариантов дохуя, как это провернуть:

  1. Своими кривыми ручками, внатуре. Сам всё описываешь, как копировать.

    public class Person {
        private String name;
        private Address address; // А это, сука, изменяемый объект, в нём тоже своя банда!
    
        // Конструктор для полного клонирования, чтоб всё было твоё
        public Person(Person original) {
            this.name = original.name;
            this.address = new Address(original.address); // Вот тут ключ, блядь! Не просто address, а НОВЫЙ address, скопированный!
        }
    
        // Или через этот ёбаный clone() (но его все не любят, честно)
        @Override
        protected Person clone() throws CloneNotSupportedException {
            Person cloned = (Person) super.clone();
            cloned.address = this.address.clone(); // И от адреса тоже требуешь клона, пидарас!
            return cloned;
        }
    }

    Работает, но если объектов как говна за баней — заебёшься каждый описывать, волнение ебать.

  2. Через сериализацию — мощно, но тупо. Берёшь объект, превращаешь в поток байтов, а потом из этих байтов собираешь нового такого же уродца.

    import java.io.*;
    
    public static <T extends Serializable> T deepCopy(T object) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        out.writeObject(object); // Записал старого
        out.flush();
    
        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream in = new ObjectInputStream(byteIn);
        return (T) in.readObject(); // Прочитал нового! Магия, блядь!
    }
    • Плюс: Хуярь хоть какие графы объектов, хоть циклические ссылки — всё скопирует, если они Serializable.
    • Минус: Медленно, пиздец как медленно. И все, сука, должны быть Serializable, а это не всегда так.
  3. Взять готовую банду (библиотеки). Самый разумный путь для сложных случаев.

    • Apache Commons Lang: SerializationUtils.clone(object). Красиво, но опять требует Serializable, терпения ноль ебать.
    • Jackson/Gson: А это хитрая жопа! Превращаешь объект в JSON (как будто в текст его размазал), а потом из текста собираешь свеженькую копию.
      // Gson, например
      Gson gson = new Gson();
      Person copy = gson.fromJson(gson.toJson(original), Person.class); // И охуенно, и сериализуемость не нужна!

Короче, выбор, блядь, зависит от того, насколько у тебя всё запущено. Если объект простой — руками. Если граф большой и ебнутый — библиотека в помощь. А сериализация — это как молоток: универсально, но не всегда изящно. Думай головой, а не жопой!