JAVA对象克隆-保护封装性 一、方法返回对象引用的问题:破坏类的封装性 这仅仅是对象引用的拷贝,而不是对象的拷贝。 class Employee{ String name; int salary; ……… public String getName(){ return name; } public void setName(String aName){ name = aName; } } public class OEmployee{ private int number = 11; private Employee harry = new Employee(); ……… public Employee getEmployee(){return harry;} public void setEmployeeName(String name){ harry.setName(name);} public String getEmployeeName(){ harry.getName();} } //main OEmployee hotel = new OEmployee(); oe.setEmployeeName( “中国”); System.out.println(hotel. getEmployeeName()); Employee alice = hotel.getEmployee(); alice.setName( “美国”); System.out.println(hotel. getEmployeeName()); //output 中国 美国 // OEmployee的harry是private,应当只能够通过OEmployee对象hotel来修改harry的内容,即第二个输出语句应当是:中国。现在,不通过OEmployee对象hotel也能够修改harry的内容(这里通过alice),这就破坏了类的封装性(OEmployee的harry暴露了)。 //harry和alice都指向了同一个对象:拷贝的是对象的位置值 这种破坏了类的封装性的现象称为对象发布。 对象发布:使对象能够在当前作用域之外的代码中使用。 有一种情况:我们需要使用对象当前的状态数据,而又不希望破坏类的封装性,如何解决? 二、解决方法:对象克隆 1、Cloneable接口是java的标记接口之一,是一个空接口。 2、实现对象克隆的类需要实现实现Cloneable接口,以便标记该类的对象可进行克隆。同时,重写Object类的protected方法clone()。 3、对象克隆分为浅度克隆和深度克隆。 三、浅度克隆 对原始对象中的数值或基本类型字段的拷贝没有任何问题,但对原始对象中的对象引用字段或字段是数组的拷贝的最终结果则是:原始对象与克隆对象共享子对象和数组。这就是说浅度克隆仅仅执行了一个逐字段拷贝。 public class OEmployee implements Cloneable{ private int number = 11; private Employee harry = new Employee(); ……… public Employee getEmployee(){return harry;} public void setEmployeeName(String name){ harry.setName(name);} public String getEmployeeName(){ harry.getName();} public OEmployee clone() throws CloneNotSupportedException{ return super.clone(); } } //main() OEmployee original = new OEmployee(); OEmployee copy = original.clone(); 可见,Object类的clone()只是将将原始对象的各个字段拷贝一份给给目标对象。 四、深度克隆 在浅度克隆的基础上,也要拷贝原始对象的子对象所指向的对象,最终结果则是:原始对象与克隆对象不会再有共享数据。这就是说:深度克隆能够通过子对象和数组中的每个条目条目对引用对象进行克隆,并且能够递归下去直至该对象可达的所有对象。 public class OEmployee implements Cloneable{ private int number = 11; private Employee harry; ……… public Employee getEmployee(){return harry;} public void setEmployeeName(String name){ harry.setName(name);} public String getEmployeeName(){ harry.getName();} public OEmployee clone() throws CloneNotSupportedException{ OEmployee copy = (OEmployee)super.clone(); copy.harry = (harry)harry.clone(); // Employee也必须是深度克隆 return copy; } public void setHarry(String aName, int aSarary){ harry = new Employee(aName, aSarary); } } //main() OEmployee original = new OEmployee(); original.setHarry(“中国”, 5); OEmployee copy = original.clone(); copy. original.setHarry(“国中”, 7); 希望进行深度克隆,首先调用super.clone()进行克隆,再用自定制的克隆操作来覆盖原先的clone()。 五、对象序列化:对象克隆的另一种方式。 六、Cloneable的应用实例 1、接口Cloneable 2、抽象父类Collator public Object clone() { try { return (Collator)super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(); } } 3、子类RuleBasedCollator public Object clone() { // if we know we're not actually a subclass of RuleBasedCollator // (this class really should have been made final), bypass // Object.clone() and use our "copy constructor". This is faster. if (getClass() == RuleBasedCollator.class) { return new RuleBasedCollator(this); } else { RuleBasedCollator result = (RuleBasedCollator) super.clone(); result.primResult = null; result.secResult = null; result.terResult = null; result.sourceCursor = null; result.targetCursor = null; return result; } } |