1. Использование Object.clone() метод Классы, которым нужна функция копирования, могут использовать Object.clone() метод, который создает и возвращает копию объекта. Прототип Object.clone() является
1
| protected Object clone() throws CloneNotSupportedException;
| В качестве возвращаемого типа Object.clone() является Object, приведение типов необходимо для присвоения возвращаемой ссылки Object ссылке на объект.
Все задействованные классы должны реализовывать Клонируемый интерфейс для указания Object.clone() метод о том, что для этого метода разрешено создавать полевые копии экземпляров этого класса. Вызов метода clone объекта для экземпляра, который не реализует Cloneable интерфейс приводит к CloneNotSupportedException.
Поскольку каждый класс неявно расширяет Object учебный класс, Object.clone() является переопределяемым методом. Поскольку Java поддерживает ковариантные возвращаемые типы, возвращаемый тип clone() можно изменить с Object на тип клонируемого объекта, и clone() должен переопределить защищенный Object.clone() метод с общедоступным методом.
The clone() ведет себя так же, как Копировать конструктор. Он называет clone() метод своего родительского класса для получения копии и т. д., пока он в конечном итоге не достигнет класса Object. clone() метод, который создает новый экземпляр того же класса, поскольку объект копирует все поля в новый экземпляр.
Минусы:
1. Object.clone() не будет работать с интерфейсами и абстрактными классами.
Единственный способ использовать Object.clone() метод, если класс объекта известен, т. е. мы не можем получить доступ к clone() метод для абстрактного типа, так как большинство интерфейсов и абстрактных классов в Java не определяют общедоступный clone() метод.
Например, нельзя вызвать clone() в ссылке на карту в Java, потому что карта не указывает общедоступного clone() метод. Только реализации карты, такие как HashMap а также LinkedHashMap имеют clone() методы, но носить с собой тип класса объекта не рекомендуется, и это противоречит принципу «программа для интерфейса, а не для реализации».
2. Реализация по умолчанию Object.clone() метод возвращает Неглубокое копирование.
При неглубоком копировании, если значение поля является примитивным типом, оно копирует свое значение; в противном случае, если значение поля является ссылкой на объект, оно копирует ссылку и, следовательно, ссылается на тот же объект. Теперь, если один из этих объектов изменен, изменение будет видно в другом. В Глубокое копирование, в отличие от мелкой копии, объекты, на которые есть ссылки, не являются общими; вместо этого новые объекты создаются для любых объектов, на которые есть ссылки.
Поверхностное клонирование
Следующая программа демонстрирует использование Object.clone() метод, используя его реализацию по умолчанию, которая возвращает поверхностную копию. Мы рассмотрим глубокое копирование с помощью clone() метод в следующем разделе.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
| import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.Map;
// `Subject` также должен реализовать `Cloneable`!!
class Subject implements Cloneable
{
private Set subjects;
public Subject()
{
subjects = new HashSet<>(
Arrays.asList("Maths", "Science", "English", "History")
);
}
@Override
public Object clone() throws CloneNotSupportedException {
// вызовите `super.clone()`, чтобы получить ссылку на клонированный объект
return super.clone();
}
@Override
public String toString() {
return subjects.toString();
}
public Set getSubjects() {
return subjects;
}
}
// Обратите внимание, что `Student` реализует интерфейс `Cloneable`
class Student implements Cloneable
{
private String name; // неизменяемое поле
private int age; // примитивное поле
private Subject subjects;
private Map map;
public Student(String name, int age)
{
this.name = name;
this.age = age;
map = new HashMap() {{
put(name, age);
}};
subjects = new Subject();
}
@Override
public String toString()
{
return Arrays.asList(name, String.valueOf(age),
subjects.toString()).toString();
}
@Override
public Object clone() throws CloneNotSupportedException {
// вызовите `super.clone()`, чтобы получить ссылку на клонированный объект
return super.clone();
}
public Set getSubjects() { return subjects.getSubjects(); }
public Map getMap() { return map; }
// включаем оставшиеся геттеры и сеттеры
}
// Демонстрация поверхностного копирования объектов с помощью метода `clone()`
class Main
{
// Вспомогательный метод для сравнения двух объектов. Он печатает мелкую копию
// если оба объекта имеют один и тот же объект; в противном случае он печатает глубокую копию
public static void compare(Object ob1, Object ob2)
{
if (ob1 == ob2) {
System.out.println("Shallow Copy");
}
else {
System.out.println("Deep Copy");
}
};
public static void main(String[] args)
{
Student student = new Student("Jon Snow", 22);
Student clone = null;
try {
clone = (Student) student.clone();
System.out.println("Cloned Object: " + clone + '\n');
} catch (CloneNotSupportedException ex) {
ex.printStackTrace();
}
compare(student.getSubjects(), clone.getSubjects());
compare(student.getMap(), clone.getMap());
// любое изменение, внесенное в карту клона, отразится на карте ученика
clone.getMap().put("John Cena", 40);
System.out.println(student.getMap());
}
}
| Скачать Выполнить код
результат:
Cloned Object: [Jon Snow, 22, [Maths, English, Science, History]] Shallow Copy Shallow Copy {Jon Snow=22, John Cena=40}
Глубокое клонирование
Если класс содержит только примитивы и поля Immutable, поверхностная копия работает нормально. Но любые изменяемые поля, такие как коллекции и массивы, будут совместно использоваться оригиналом и копией, поскольку Object.clone() возвращает точную копию исходного объекта.
Для глубокого клонирования, если класс содержит какие-либо ссылки на объекты, clone() Метод должен выполнить все необходимые модификации объекта, полученного от суперкласса, прежде чем вернуться к вызывающей стороне. т.е., clone() метод должен изменять изменяемые поля объектов, возвращаемых super.clone(). Один из способов сделать это — вызвать clone() метод на изменяемых полях.
Если мы попытаемся присвоить значения конечным полям в clone() метод, это приведет к ошибке компиляции. Значение поля является неизменяемым объектом, мы можем просто позволить ему скопировать ссылку, и и оригинал, и его клон будут использовать один и тот же объект. Но для изменчивых объектов он должен быть глубоко скопирован.
Сериализация и десериализация — это еще одна альтернатива использованию клона, который правильно обрабатывает окончательные элементы данных, как показано в следующем разделе. Мы также можем использовать Копировать конструктор который берет экземпляр того же класса, что и объект, копирует все примитивные поля и создает новые объекты для любых объектов, на которые ссылаются, в этот новый экземпляр.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
| import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.Map;
// `Subject` также должен реализовать `Cloneable`!!
class Subject implements Cloneable
{
private Set subjects;
public Subject()
{
subjects = new HashSet<>(
Arrays.asList("Maths", "Science", "English", "History")
);
}
@Override
public Subject clone() throws CloneNotSupportedException {
Subject obj = (Subject)super.clone();
obj.subjects = new HashSet<>(this.subjects);
return obj;
}
@Override
public String toString() {
return subjects.toString();
}
public Set getSubjects() {
return subjects;
}
}
// Обратите внимание, что `Student` реализует интерфейс `Cloneable`
class Student implements Cloneable
{
private String name; // неизменяемое поле
private int age; // примитивное поле
private Subject subjects;
private Map map;
public Student(String name, int age)
{
this.name = name;
this.age = age;
map = new HashMap() {{
put(name, age);
}};
subjects = new Subject();
}
@Override
public String toString()
{
return Arrays.asList(name, String.valueOf(age),
subjects.toString()).toString();
}
@Override
public Object clone() throws CloneNotSupportedException {
Student student = (Student) super.clone();
// примитивные поля типа int не копируются, так как их содержимое
// уже скопировано
// Строка неизменна
// вызов `clone()` для объекта `Subject`
student.subjects = this.subjects.clone();
// создаем новый экземпляр `Hashmap`
student.map = new HashMap<>(this.map);
return student;
}
public Set getSubjects() { return subjects.getSubjects(); }
public Map getMap() { return map; }
// включаем оставшиеся геттеры и сеттеры
}
// Демонстрация глубокой копии объектов с помощью метода `clone()`
class Main
{
// Вспомогательный метод для сравнения двух объектов. Он печатает мелкую копию
// если оба объекта имеют один и тот же объект; в противном случае он печатает глубокую копию
public static void compare(Object ob1, Object ob2)
{
if (ob1 == ob2) {
System.out.println("Shallow Copy");
}
else {
System.out.println("Deep Copy");
}
};
public static void main(String[] args)
{
Student student = new Student("Jon Snow", 22);
Student clone = null;
try {
clone = (Student) student.clone();
System.out.println("Cloned Object: " + clone + '\n');
} catch (CloneNotSupportedException ex) {
ex.printStackTrace();
}
compare(student.getSubjects(), clone.getSubjects());
compare(student.getMap(), clone.getMap());
// любые изменения, внесенные в карту клона, не отразятся на карте ученика
clone.getMap().put("John Cena", 40);
System.out.println(student.getMap());
}
}
| Скачать Выполнить код
результат:
Cloned Object: [Jon Snow, 22, [Maths, English, Science, History]] Deep Copy Deep Copy {Jon Snow=22}
Обычай clone() метод утомителен в реализации, подвержен ошибкам и сложен в обслуживании. Код необходимо модифицировать каждый раз, когда в поля класса вносятся какие-либо изменения.
|