У объекта java.lang.Object есть метод protected Object clone() throws CloneNotSupportedException. Этот метод позволяет создавать копии объектов. Он бросает исключение CloneNotSupportedException для всех объектов, которые не реализуют интерфейс java.lang.Cloneable, а для классов, реализующих этот интерфейс, возвращает копию объекта, которая создана копированием всех полей исходного объекта. Содержимое полей не копируется. Если поле является ссылочным, то оно будет указывать на тот же самый объект, что и исходное поле.
Во многих случаях простого копирования недостаточно, так как некоторые поля могут содержать сложные структуры, для которых нужно создать копию, чтобы объекты были действительно независимы. А они должны быть независимы, так как это копирование. Тогда нужно будет переопределить метод копирования своим методом, вызвать super.clone() и создать копии структур для тех полей, которые содержат эти сложные изменяемые структуры. Массивы уже реализуют интерфейс java.lang.Cloneable.
Пример класса, реализующего интерфейс java.lang.Cloneable:
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 124 125 126 127 128 129 130 131 |
/** * Пример класса, реализующего интерфейс Cloneable. * @see <a href="https://urvanov.ru/2015/10/05/%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D1%84%D0%B5%D0%B9%D1%81-java-lang-cloneable-%D0%BA%D0%BE%D0%BF%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BE%D0%B2/">https://urvanov.ru/2015/10/05/интерфейс-java-lang-cloneable-копирование-объектов/</a> */ public class MyCloneableClass implements java.lang.Cloneable { private MyClassB b; /** * Массивы реализуют java.lang.Cloneable. */ private int[] myArray; /** * Будет копироваться значение. */ private int x; /** * Будет копироваться значение. */ private double y; /** * Строки неизменяемы, значит копирование по ссылке здесь приемлемо. */ private String str1; public MyClassB getB() { return b; } public void setB(MyClassB b) { this.b = b; } public int[] getMyArray() { return myArray; } public void setMyArray(int[] myArray) { this.myArray = myArray; } public int getX() { return x; } public void setX(int x) { this.x = x; } public double getY() { return y; } public void setY(double y) { this.y = y; } public String getStr1() { return str1; } public void setStr1(String str1) { this.str1 = str1; } class MyClassB implements java.lang.Cloneable { private int x; public int getX() { return x; } public void setX(int x) { this.x = x; } /** * Меняем видимость clone() на public. */ public MyClassB clone() throws java.lang.CloneNotSupportedException { return (MyClassB) super.clone(); } } public MyCloneableClass clone() throws java.lang.CloneNotSupportedException { MyCloneableClass result = (MyCloneableClass) super.clone(); result.setB(b.clone()); // MyClassB должен реализовать интерфейс // Cloneable. result.setMyArray((int[]) this.myArray.clone()); // копируем массив. // Массивы реализуют // Cloneable // здесь копирование других структур. return result; } public static void main(String[] args) throws CloneNotSupportedException { MyCloneableClass a = new MyCloneableClass(); a.setB(a.new MyClassB()); a.setStr1("test"); a.setX(3); int[] myArray = { 1, 2 }; a.setMyArray(myArray); MyCloneableClass clon1 = a.clone(); print(a, clon1); clon1.setX(55); clon1.setStr1("test2"); clon1.getMyArray()[0] = 6; clon1.getB().setX(4); System.out.println("After modifying clon1:"); print(a, clon1); } private static void print(MyCloneableClass a, MyCloneableClass clon1) { System.out.println("a.b=" + a.getB() + "; clon1.b=" + clon1.getB()); System.out.println("a.b.x=" + a.getB().getX() + "; clon1.b.x=" + clon1.getB().getX()); System.out.println("a.x=" + a.getX() + "; clon1.x=" + clon1.getX()); System.out.println("a.str1=" + a.getStr1() + "; clon1.str1=" + clon1.getStr1()); System.out.println("a.myArray=" + a.getMyArray() + "; clon1.myArray=" + clon1.getMyArray()); System.out.println("a.myArray[0]=" + a.getMyArray()[0] + "; clon1.myArray[0]=" + clon1.getMyArray()[0]); } } |
Код выше будем иметь примерно такой вывод в консоль:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
a.b=MyCloneableClass$MyClassB@4fb529d6; clon1.b=MyCloneableClass$MyClassB@7ecdc97b a.b.x=0; clon1.b.x=0 a.x=3; clon1.x=3 a.str1=test; clon1.str1=test a.myArray=[I@7866eb46; clon1.myArray=[I@2ce07e6b a.myArray[0]=1; clon1.myArray[0]=1 After modifying clon1: a.b=MyCloneableClass$MyClassB@4fb529d6; clon1.b=MyCloneableClass$MyClassB@7ecdc97b a.b.x=0; clon1.b.x=4 a.x=3; clon1.x=55 a.str1=test; clon1.str1=test2 a.myArray=[I@7866eb46; clon1.myArray=[I@2ce07e6b a.myArray[0]=1; clon1.myArray[0]=6 |
Здравствуйте! Вы пишете в статье, что «Содержимое полей не копируется.» А затем в примере можно увидеть, что копируется, так как содержимое полей a и clon1 совпадает. Поясните, пожалуйста, этот момент
Здравствуйте, Маргарита. В этой статье имеется в виду, что если мы делаем класс implements Cloneable, то метод clone будет создавать копию, но без уровней вложенности. Для полей примитивных типов (int, double и т. д.) это не имеет значения, но если поле ссылается на другой класс, то копия экземпляра другого класса создаваться не будет, будет копироваться только сама ссылка. Созданный клон объекта и исходный объект в этом поле будут ссылаться на один и тот же класс.
В примере, который в этой статье, мы переопределяем метод clone и принудительно вызываем clone у поля b, которое является объектом, поэтому в нашем примере мы получаем копию и этого поля тоже. Если бы мы не переопределяли метод clone, то у нас бы был метод clone от класса Object, который бы скопировал только ссылку на исходный объект b, не создавая новых объектов.