关于Cloneable接口和clone方法
1、使用 ? 创建对象有两种方式: new 和 clone 当一个对象创建过程复杂,我们是否可以根据已有的对象直接来克隆一份,而不必关系创建的细节呢(原型模式)。 1.1 Java Object根类默认提供了clone方法: protected native Object clone() throws CloneNotSupportedException; 一个本地方法,protected权限: 这样做是为避免我们创建每一个类都默认具有克隆能力 ? 1.2 实现Cloneable接口 我们要使用一个对象的clone方法,必须Cloneable接口,这个接口没有任何实现,跟 Serializable一样是一种标志性接口 如果不实现Cloneable接口,会抛出CloneNotSupportedException异常 重写clone方法,使用public修饰(否则外部调用不到),调用父类的clone方法 如下: public class CloneModel implements Cloneable{ private String name; private int age; @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "CloneModel{" + "name=‘" + name + ‘‘‘ + ",age=" + age + ‘}‘; } } ? 1.3、Object.clone() 与 构造方法 我们看一个例子: CloneModel类: public class CloneModel implements Cloneable{ private String name; private int age; public CloneModel(){ System.out.println("will new a instance"); } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "CloneModel{" + "name=‘" + name + ‘‘‘ + ",age=" + age + ‘}‘; } } 当我们调用此对象的clone方法时,构造方法并没有被调用,所以我说创建一个对象new和clone是两条路 public static void main(String[] args) throws CloneNotSupportedException { CloneModel cloneModel = new CloneModel(); System.out.println(cloneModel.clone()); } 打印: CloneModel{name=‘null‘,age=0} ? 2、重写clone方法原则 ? x.clone != x将会是true; x.clone().getClass()==x.getClass()将会是true(不是绝对的,但建议这么做) x.clone().equals(x)将会是true(不是绝对的,但建议这么做) ? 3、浅克隆和深克隆 ? 3.1 默认clone方法时浅克隆 Object默认的clone方法实际是对域的简单拷贝,对于简单数据类型,是值的拷贝; 对于复杂类型的字段,则是指针地址的拷贝,clone后的对象和原对象指向的还是一个地址空间 所以说默认的clone方法时浅克隆。 例子: class Model2{ int height; } public class CloneModel implements Cloneable{ private String name; private int age; private Model2 model2; public CloneModel() { this.model2 = new Model2(); } public Model2 getModel2() { return model2; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "CloneModel{" + "name=‘" + name + ‘‘‘ + ",age=" + age + ‘}‘; } } clone之后比较复杂对象是否相等 public static void main(String[] args) throws CloneNotSupportedException { CloneModel cloneModel1 = new CloneModel(); CloneModel cloneModel2 = (CloneModel)cloneModel1.clone(); System.out.println(cloneModel1.getModel2()==cloneModel2.getModel2()); } ? 执行返回:true ? 3.2 如何实现深克隆 还是上面的例子,我们改下代码 class Model2 implements Cloneable{ int height; @Override public Object clone() throws CloneNotSupportedException { System.out.println("clone Model2"); return super.clone(); } } public class CloneModel implements Cloneable{ private String name; private int age; private Model2 model2; public CloneModel() { this.model2 = new Model2(); } public Model2 getModel2() { return model2; } public void setModel2(Model2 model2) { this.model2 = model2; } @Override public CloneModel clone() throws CloneNotSupportedException { CloneModel cloneModelTemp = (CloneModel)super.clone(); cloneModelTemp.setModel2((Model2)cloneModelTemp.getModel2().clone()); return cloneModelTemp; } @Override public String toString() { return "CloneModel{" + "name=‘" + name + ‘‘‘ + ",age=" + age + ‘}‘; } } 再次测试下: public static void main(String[] args) throws CloneNotSupportedException { CloneModel cloneModel1 = new CloneModel(); CloneModel cloneModel2 = cloneModel1.clone(); System.out.println(cloneModel1.getModel2()==cloneModel2.getModel2()); } 执行返回:false 这么做就要在super.clone的基础上 继续对非基本类型的对象递归的再次clone. 显然这么方式是繁琐的且不可靠的。 有没有其他的方式呢?有 序列化 ? 3.3 序列化实现深度克隆 (1) 使用java自身的序列化转为二进制数 ,再反序列化为对象 上面的例子改造下 import java.io.Serializable; class Model2 implements Serializable { int height; } public class CloneModel implements Serializable { private String name; private int age; private Model2 model2; public CloneModel() { this.model2 = new Model2(); } public Model2 getModel2() { return model2; } } ? 测试代码: import com.yangfei.test.CloneModel; import java.io.*; public class YfTest { public static <T extends Serializable> T deepCloneObject(T object) throws IOException { T deepClone = null; ObjectInputStream ois = null; try(ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); ) { oos.writeObject(object); ByteArrayInputStream bais = new ByteArrayInputStream(baos .toByteArray()); ois = new ObjectInputStream(bais); deepClone = (T)ois.readObject(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } finally { if(ois != null){ ois.close(); } } return deepClone; } public static void main(String[] args) throws IOException { CloneModel cloneModel1 = new CloneModel(); CloneModel cloneModel2 = deepCloneObject(cloneModel1); System.out.println(cloneModel1.getModel2()==cloneModel2.getModel2()); } } 测试输出:false ? (2)其他序列化方式,如对象序列化json字符串再反序列化为对象 ?我们使用google的gson来进行序列化,上代码 import com.google.gson.Gson; import java.io.Serializable; class Model2 implements Serializable { int height; } public class CloneModel implements Serializable { private String name; private int age; private Model2 model2; public CloneModel() { this.model2 = new Model2(); } public Model2 getModel2() { return model2; } public CloneModel deepClone() { Gson gson = new Gson(); return gson.fromJson(gson.toJson(this),CloneModel.class); } } 测试代码: public static void main(String[] args) throws IOException { CloneModel cloneModel1 = new CloneModel(); CloneModel cloneModel2 = cloneModel1.deepClone(); System.out.println(cloneModel1.getModel2()==cloneModel2.getModel2()); } 执行返回:false ? 性能对比测试: 上代码: public static void main(String[] args) throws IOException { CloneModel cloneModel1 = new CloneModel(); long time1 = System.currentTimeMillis(); for(int i=0;i<1000;i++){ // CloneModel cloneModel2 = cloneModel1.deepClone(); CloneModel cloneModel2 = deepCloneObject(cloneModel1); } long time2 = System.currentTimeMillis(); System.out.println((time2-time1)+"ms"); }
循环1000次 Serializable耗时:118ms json耗时:167ms
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |