Skip to content

Latest commit

 

History

History
133 lines (112 loc) · 7.56 KB

Клонирование.md

File metadata and controls

133 lines (112 loc) · 7.56 KB
tags
Java/ОсновыЯзыка
  1. Переопределение метода clone() и реализация интерфейса Cloneable().
  2. Использование конструктора копирования.
  3. Использовать для клонирования механизм сериализации.

Метод clone

Класс Object определяет метод clone(), который создает копию объекта. Если вы хотите, чтобы экземпляр вашего класса можно было клонировать, необходимо переопределить этот метод и реализовать интерфейс Cloneable. 

Интерфейс Clonable - это интерфейс маркер, он не содержит ни методов, ни переменных. Интерфейсы маркер просто определяют поведение классов.

Object.clone() выбрасывает исключение CloneNotSupportedException при попытке клонировать объект не реализующий интерфейс Cloneable

Метод clone() в классе Object действительно объявлен с модификатором protected, это сделано для того, чтобы дать классам-наследникам возможность переопределить его и настроить процесс клонирования под свои потребности. Наследники могут расширить видимость метода clone() до public, если это необходимо.

Поверхностное копирование

Метод clone() в родительском классе Object является protected, поэтому желательно переопределить его как public. Реализация по умолчанию метода Object.clone() выполняет неполное/поверхностное (shallow) копирование. Рассмотрим пример:

public class Car implements Cloneable {
    private String name;
    private Driver driver;

    public Car(String name, Driver driver) {
        this.name = name;
        this.driver = driver;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Driver getDriver() {
        return driver;
    }

    public void setDriver(Driver driver) {
        this.driver = driver;
    }

    @Override
    public Car clone() throws CloneNotSupportedException {
        return (Car) super.clone();
    }
}

В этом примере клонируются объект класса Car. Клонирование выполняется поверхностное - новый объект clonedCar содержит ссылку на тот же объект класса Driver, что и объект car. Если вас это не устраивает, то необходимо самим написать "глубокое" клонирование - создать новый объект класса Driver.

Глубокое копирование

Полное копирование всех полей и объектов класса

Глубокое клонирование требует выполнения следующих правил:

  • Все классы-члены в оригинальном классе должны поддерживать клонирование.
  • Для каждого члена класса должен вызываться super.clone() при переопределении метода clone();

Если какой-либо член класса не поддерживает клонирование, то в методе клонирования необходимо создать новый экземпляр этого класса и скопировать каждый его член со всеми атрибутами в новый объект класса, по одному.

@Override
public Car clone() throws CloneNotSupportedException {
    Car newCar = (Car) super.clone();
    Driver driver = this.getDriver().clone();
    newCar.setDriver(driver);
    return newCar;
}

Почему метод clone объявлен в классе Object, а не в интерфейсе Cloneable

Метод clone() объявлен в классе Object с сигнатурой native, чтобы обеспечить доступ к стандартному механизму "поверхностного копирования" объектов (копируются значения всех полей, включая ссылки на сторонние объекты).

Метод clone() объявлен, как protected, чтобы нельзя было вызвать этот метод у не переопределивших его объектов.

Конструктор копирования

Принимающий на вход объект того же класса, который необходимо клонировать:

Неглубокое копирование

public class Car implements Cloneable {
    private String name;
    private Driver driver;
	// Обычный конструктор
    public Car(String name, Driver driver) {
        this.name = name;
        this.driver = driver;
    }

    /**
     * Конструктор копирования.
     *
     * @param otherCar
     */
    public Car(Car otherCar) {
        this(otherCar.getName(), otherCar.getDriver());
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Driver getDriver() {
        return driver;
    }

    public void setDriver(Driver driver) {
        this.driver = driver;
    }
}

Глубокое копирование

public Car(Car otherCar) throws CloneNotSupportedException {
    this(otherCar.getName(), otherCar.getDriver().clone());
}

Отличие между поверхностным и глубоким клонированием

При поверхностном копировании клонируются ссылки, а не объекты.

Глубокое копирование дублирует все.

Какой способ клонирования предпочтительней?

Конструктор копирования

  • Поля для клонирования указываются явно;
  • Возможность клонировать даже final поля.

Как еще создать глубокую копию объекта

  1. Сериализация – это еще один способ глубокого копирования. Мы просто сериализуем нужный объект и десериализуем его. Очевидно, объект должен поддерживать интерфейс Serializable. Мы сохраняем объект в массив байт и потом прочитаем из него.
  2. При помощи библиотеки DeepCloneable Глубокое клонирование с этой библиотекой сводится с двум строкам кода:
  • Cloner cloner = new Cloner();
  • DeepCloneable clone = cloner.deepClone(this);