В чем разница между глубоким (deep copy) и поверхностным (shallow copy) копированием объектов в Java?

Ответ

Поверхностное копирование (Shallow Copy) создает новый объект, но копирует в него ссылки на те же вложенные объекты, что и оригинал. Глубокое копирование (Deep Copy) создает полностью независимую копию, рекурсивно копируя все вложенные объекты.

Различия:

Аспект Поверхностное копирование Глубокое копирование
Вложенные объекты Копируются ссылки Создаются новые объекты
Зависимость Изменения вложенных объектов видны в обеих копиях Копии полностью независимы
Сложность/стоимость Быстрое, мало памяти Медленное, больше памяти

Пример:

class Engine { int power; }
class Car {
    String model;
    Engine engine; // Вложенный объект
}

Car original = new Car("Sedan", new Engine(150));

// Поверхностная копия (например, через Object.clone())
Car shallowCopy = original.clone();
shallowCopy.engine.power = 200; // Меняет двигатель и у original!

// Глубокая копия (требует ручной реализации)
Car deepCopy = new Car(original.model, new Engine(original.engine.power));
deepCopy.engine.power = 250; // Не влияет на original

Как создать:

  • Shallow copy: метод Object.clone() (если класс реализует Cloneable).
  • Deep copy: ручное копирование конструкторами, сериализацией или библиотеками (Apache Commons, ObjectMapper).

Ответ 18+ 🔞

Да ты посмотри, какая красота, блядь! Придумали же, сука, в Java 8, чтобы старые интерфейсы не ломать, как гнилую избу, когда новую пристройку делаешь. Вот эти default-методы — это ж гениально, ёпта!

Раньше-то, если в интерфейс новый метод сунешь, так все, кто его реализовывал, — пиздец, нахуй, компилятор на каждом углу орет, как резаный. А теперь взяли, и говорят: «А давайте сделаем метод с готовой реализацией прямо в интерфейсе!». И назвали это default. И жить стало проще, блядь.

А static-методы в интерфейсах — это вообще отдельная песня. Раньше для утилиток отдельный класс-помойку заводили, а теперь можно прямо в интерфейсе их приткнуть, красиво, удобно. Не надо плодить сущностей, как блох на бродячей собаке.

Вот смотри, табличка, чтобы в голове не еблось:

Признак default метод static метод
Кому принадлежит Каждой конкретной тачке (объекту) Самому интерфейсу, как начальнику цеха
Наследуется ли Да, и его можно переписать, если не нравится Ни хуя, он сам по себе, как кот, который гуляет
Как вызвать Через объект: myCar.honk() Через имя интерфейса: Vehicle.isValidSpeed(50)
Зачем придуман Чтобы старый код не взрывался, когда интерфейс растет Чтобы удобные функции рядом лежали, а не по всему проекту разбросаны

А вот живой пример, чтобы вообще всё встало на свои места, блядь:

interface Vehicle {
    // Ну это обычный метод, который все обязаны реализовать
    void start();

    // А это — default-метод. Хочешь — используй как есть, хочешь — перепиши под себя.
    default void honk() {
        System.out.println("Beep beep!");
    }

    // А это static-метод. Общая проверка для всех, кто про скорость спросит.
    static boolean isValidSpeed(int speed) {
        return speed >= 0;
        // Отрицательную скорость, блядь, не предлагать!
    }
}

class Car implements Vehicle {
    @Override
    public void start() {
        System.out.println("Car started");
    }
    // Метод honk() можем оставить дефолтным, а можем и свой гудок придумать.
}

// Использование на практике:
Car myCar = new Car();
myCar.start(); // Выведет: Car started
myCar.honk();  // Выведет: Beep beep! (работает дефолтная реализация)
boolean valid = Vehicle.isValidSpeed(50); // true (вызов статического метода)
boolean invalid = Vehicle.isValidSpeed(-10); // false (пиздец, куда ты гонишь?)

Вот и вся магия, блядь. default — чтобы не ломать старое, static — чтобы не плодить лишних классов. Красота, в рот меня чих-пых! Главное — не путать, а то получится мартышлюшка, а не код.