Иногда необходимо получить копию объекта, которая не зависела
бы от оригинала. С которой можно было бы производить манипуляции, при этом, не
изменяя оригинал. При обыкновенном присваивание объектов (obj1 = obj2;) передаются ссылки на объект. В
итоге два экземпляра ссылаются на один объект, и изменение одного приведет к
изменению другого. Как мы видим это не то, что нам нужно. И в данном случае,
нам на помощь придет интерфейс Cloneable
и метод clone() класса Object.
И так, если нам необходимо получить независимый клон объекта,
то необходимо вызвать метод clone().
Данный метод объявлен, как protected,
а это значит, что метод защищен, и может быть доступен только при наследовании
объекта. Как выясняется, это не является проблемой, потому как любой класс,
является потомком класса Object.
Однако при защищенном методе класс может клонировать только свои собственные
объекты. Чтобы клонировать другие объекты, метод clone() необходимо расширить до public.
Пример расширения метода clone().
public
User clone() throws CloneNotSupportedException {
return (User)super.clone();
}
Как мы можем видеть метод clone() может выбрасывать исключение CloneNotSupportedException. Данное исключение возникает в случае, когда клонируемый
класс не имеет реализации интерфейса Cloneable. Интерфейс
Cloneable не
реализует ни одного метода. Он является всего лишь маркером, говорящим, что данный
класс реализует клонирование объекта.
Само клонирование осуществляется вызовом родительского метода clone(). Данный вид клонирования называется
поверхностным клонированием. Его можно использовать только в том случае, если у
клонируемого класса объявлены неизменяемые
типы объекты.
Пример реализации клонирования объекта.
package
my.cloneable;
class
User implements Cloneable {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String
name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int
age) {
this.age = age;
}
public User clone() throws
CloneNotSupportedException {
return (User)super.clone();
}
}
public
class App {
public static void
main(String[] args) {
User user = new User();
user.setName("Иванов");
user.setAge(25);
System.out.println("Данные до клонирования:
" +
user.getName()
+ " - " + user.getAge() + "лет");
User clone;
try {
clone = user.clone();
clone.setName("Петров");
clone.setAge(30);
System.out.println("Клон после изменения данные: "
+
clone.getName()
+ " - " + clone.getAge() + "лет");
} catch
(CloneNotSupportedException e) {
System.out.println("Объект не может быть клонированным.");
}
System.out.println("Оригинал, после манипуляций
с клоном: " +
user.getName()
+ " - " + user.getAge() + "лет");
}
} /* результат:
Данные до клонирования: Иванов - 25лет
Клон после изменения данные: Петров - 30лет
Оригинал, после манипуляций с клоном: Иванов - 25лет
*/
Как видно из данного примера, мы сначала создаем экземпляр объект
User, затем передаем
ему значения. После этого присваиваем клон объекта User переменной clone. Проводим манипуляции с
клонированным объектом. На всем этапе выводим промежуточные значения
оригинального экземпляра объекта User и его клона. Как можно заметить, из
результата примера, оригинальный экземпляр объекта остался с первоначальными
значениями, а значения клон экземпляра были изменены.
Существует так же второй вид клонирования объекта, который
называется глубокое клонирование. Его используют в тех случаях, когда в
клонируемом классе есть изменяемые объекты.
Пример глубокого клонирования.
package
my.cloneable;
import
java.util.Calendar;
import
java.util.Date;
import
java.util.GregorianCalendar;
class
User implements Cloneable {
private String name;
private int age;
private GregorianCalendar birthday;
public String getName() {
return name;
}
public void setName(String
name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int
age) {
this.age = age;
}
public String getBirthday() {
return birthday.get(Calendar.DAY_OF_MONTH)
+ "." +
birthday.get(Calendar.MONTH)
+ "." +
birthday.get(Calendar.YEAR);
}
public void setBirthday(int
day, int month, int year) {
birthday = new
GregorianCalendar();
birthday.set(Calendar.DAY_OF_MONTH,
day);
birthday.set(Calendar.MONTH,
month);
birthday.set(Calendar.YEAR,
year);
}
public User clone() throws
CloneNotSupportedException {
User clone = (User)super.clone();
clone.birthday =
(GregorianCalendar) birthday.clone();
return clone;
}
}
public
class App {
public static void
main(String[] args) {
User user = new User();
user.setName("Иванов");
user.setAge(25);
user.setBirthday(12, 03, 1975);
System.out.println("Данные до клонирования:
" + user.getName() + " - " + user.getAge() + "лет, день рождение: "
+ user.getBirthday());
User clone;
try {
clone = user.clone();
clone.setName("Петров");
clone.setAge(30);
clone.setBirthday(15, 11,
1992);
System.out.println("Клон после изменения данные: "
+ clone.getName() + " - " + clone.getAge() + "лет, день рождение: "
+ clone.getBirthday());
} catch
(CloneNotSupportedException e) {
System.out.println("Объект не может быть клонированным.");
}
System.out.println("Оригинал, после манипуляций с клоном: "
+ user.getName() + " - " + user.getAge() + "лет, день рождение: "
+ user.getBirthday());
}
}
/*
результат:
Данные до клонирования: Иванов - 25лет, день рождение:
12.3.1975
Клон после изменения данные: Петров - 30лет, день рождение:
15.11.1992
Оригинал, после манипуляций с клоном: Иванов - 25лет,
день рождение: 12.3.1975
*/
В данном примере, в классе User, добавлена изменяемая переменная birthday, в которой хранится дата
рождения.
Чтобы она нормально клонировалась необходимо переопределить
метод clone() следующим
образом :
public
User clone() throws CloneNotSupportedException {
User clone = (User)super.clone();
clone.birthday =
(GregorianCalendar) birthday.clone();
return clone;
}
Как вы видите, добавилась одна строчка
clone.birthday =
(GregorianCalendar) birthday.clone();
в которой напрямую клонируется поле birthday.
Однако с клонированием объектов
необходимо быть очень аккуратным, потому как возникает много ошибок из-за
неправильной передачи объектов класса. По этому используйте данный механизм
только в тех случаях, когда это необходимо.
день рождениЯ
ОтветитьУдалитьполезная инфа
ОтветитьУдалитьспасибо
ОтветитьУдалитьпросто и понятно, спасибо!
ОтветитьУдалитьНет нужды делать:
ОтветитьУдалитьclone.birthday = (GregorianCalendar) birthday.clone();
достаточно реализовать интерфейс Cloneable во всех классах ссылки на которые используются в изначальном классе
Из доков:
A "deep" copy,
* in contrast, would also recursively clone nested objects. A subclass that
* needs to implement this kind of cloning should call {@code super.clone()}
* to create the new instance and then create deep copies of the nested,
* mutable objects.
Если не делать:
Удалитьclone.birthday = (GregorianCalendar) birthday.clone();
то у клона вместо нового объекта типа GregorianCalendar будет ссылка на уже существующий объект, поскольку тип GregorianCalendar является mutable.
Спасибо!
ОтветитьУдалитьТакже полное клонирование можно сделать если сериализовать и десериализовать объект.
ОтветитьУдалитьНо это скорее хак.
Спасибо брат
ОтветитьУдалитьКакие объекты могут быть клонированы?
ОтветитьУдалитьлюбые, класс которых реализует интерфейс Cloneable
ОтветитьУдалить