tags |
---|
Java/ОсновыЯзыка |
- Переопределение метода clone() и реализация интерфейса Cloneable().
- Использование конструктора копирования.
- Использовать для клонирования механизм сериализации.
Класс 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 с сигнатурой 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 поля.
- Сериализация – это еще один способ глубокого копирования. Мы просто сериализуем нужный объект и десериализуем его. Очевидно, объект должен поддерживать интерфейс Serializable. Мы сохраняем объект в массив байт и потом прочитаем из него.
- При помощи библиотеки DeepCloneable Глубокое клонирование с этой библиотекой сводится с двум строкам кода:
- Cloner cloner = new Cloner();
- DeepCloneable clone = cloner.deepClone(this);