Напомним, что все массивы реализуют интерфейс Cloneable и, таким образом, доступны для клонирования.
Важно помнить, что все поля клонированного объекта приравниваются, их значения никогда не клонируются. Рассмотрим пример:
public class Test implements Cloneable { Point p; int height;
public Test(int x, int y, int z) { p=new Point(x, y); height=z; }
public static void main(String s[]) { Test t1=new Test(1, 2, 3), t2=null; try { t2=(Test) t1.clone(); } catch (CloneNotSupportedException e) {} t1.p.x=-1; t1.height=-1; System.out.println("t2.p.x=" + t2.p.x + ", t2.height=" + t2.height); } }
Результатом работы программы будет:
t2.p.x=-1, t2.height=3
Из примера видно, что примитивное поле было скопировано и далее существует независимо в исходном и клонированном объектах. Изменение одного не сказывается на другом.
А вот ссылочное поле было скопировано по ссылке, оба объекта ссылаются на один и тот же экземпляр класса Point. Поэтому изменения, происходящие с исходным объектом, сказываются на клонированном.
Этого можно избежать, если переопределить метод clone() в классе Test.
public Object clone() { Test clone=null; try { clone=(Test) super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(e.getMessage()); } clone.p=(Point)clone.p.clone(); return clone; }
Обратите внимание, что результат метода Object.clone() приходится явно приводить к типу Test, хотя его реализация гарантирует, что клонированный объект будет порожден именно от этого класса. Однако тип возвращаемого значения в данном методе для универсальности объявлен как Object, поэтому явное сужение необходимо.
Теперь метод main можно упростить:
public static void main(String s[]) { Test t1=new Test(1, 2, 3); Test t2=(Test) t1.clone(); t1.p.x=-1; t1.height=-1; System.out.println("t2.p.x=" + t2.p.x + ", t2.height=" + t2.height); }
Результатом будет:
t2.p.x=1, t2.height=3
То есть теперь все поля исходного и клонированного объектов стали независимыми.
Реализация такого "неглубокого" клонирования в методе Object.clone() необходима, так как в противном случае клонирование второстепенного объекта могло бы привести к огромным затратам ресурсов, ведь этот объект может содержать ссылки на более значимые объекты, а те при клонировании также начали бы копировать свои поля, и так далее. Кроме того, типом поля клонируемого объекта может быть класс, не реализующий Cloneable, что приводило бы к дополнительным проблемам. Как показано в примере, при необходимости дополнительное копирование можно добавить самостоятельно.