博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式(3)-- 原型模式 (clone分析)
阅读量:4580 次
发布时间:2019-06-09

本文共 7711 字,大约阅读时间需要 25 分钟。

    原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建对象。

    在java中有语言级别的支持:clone

    在java中使用原型模式是非常简单的事情,实现Cloneable接口,调用Object的clone方法,便可以实现对象的拷贝。

    浅复制:被复制的对象的值与原对象的值是相同的,对其他对象的复制仅仅是该对象的引用

    深复制:被复制的对象的值与原对象的值是相同的,对其他对象的复制不是引用原有对象的引用,而是重新复制了该对象。

    浅复制:

   

/** * 浅复制 *   1.在派生类的clone()方法中,调用super.clone()  *   2.派生类中实现Cloneable接口,否则会报CloneNotSupportedException错误  *   3.克隆对象与原对象不是同一个对象  克隆对象与原对象的类型一样 * @author junjin4838 * */public class ShallowStudent implements Cloneable {        private String name;        private int age;       Professor p;     public ShallowStudent2(String name, int age, Professor p) {
        this.name = name;         this.age = age;         this.p = p;     } public Object clone(){ ShallowStudent o = null; try { o = (ShallowStudent)super.clone(); } catch (CloneNotSupportedException e) { System.out.println(e.toString()); } return o; } public static void main(String[] args) { People people1 = new People("test1",23);         ShallowStudent s1 = new ShallowStudent("jibingkun",23,people1); ShallowStudent s2 = (ShallowStudent)s1.clone(); s2.name = "zhangtianyu"; s2.age = 22; s2.people.peopleName = "test2";         s2.people.peopleAge = 123; System.out.println("s1 name: "+s1.name + " s1 age: "+s1.age + "s1 people.name "+ s1.people.peopleName + "s1 people.age "+ s1.people.peopleAge);         System.out.println("s2 name: "+s2.name + " s2 age: "+s2.age + "s2 people.name "+ s2.people.peopleName + "s2 people.age "+ s2.people.peopleAge); boolean b1 = s1.equals(s2); boolean b2 = (s1.getClass())== (s2.getClass()); System.out.println(b1); System.out.println(b2); } class Professor {
    String name;     int age;     public Professor(String name, int age) {
        this.name = name;         this.age = age;     } } ------》》》------- s1 name: jibingkun s1 age: 23 s1 people.name test2 s1 people.age 123 s2 name: zhangtianyu s2 age: 22 s2 people.name test2 s2 people.age 123 false true -----《《《--------- 从中可以看出,调用Object类中clone()方法产生的效果是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内容。对基本数据类型,这样的操作是没有问题的. 但对非基本类型变量,我们知道它们保存的仅仅是对象的引用,这也导致clone后的非基本类型变量和原始对象中相应的变量指向的是同一个对象。 大多时候,这种clone的结果往往不是我们所希望的结果,这种clone也被称为”影子clone”。

     Object.clone()的源码实现:

/**     * Creates and returns a copy of this object.        * x.clone() != x  will be true     * x.clone().getClass() == x.getClass()  will be true}     * if the class of this object does  not implement the interface {
@code Cloneable}, then a{
@code CloneNotSupportedException} is thrown. * 是native方法,native一般要高于非native的方法,因此使用clone往往会比new一个对象的效率要高 * 是protected属性的方法,这也意味着如果要应用 clone()方法,必须继承Object类,在Java中所有的类是缺省继承Object类的,也就不用关心这点了 * 重载clone()方法。还有一点要考虑的是为了让其它类能调用这个clone类的clone()方法,重载之后要把clone()方法的属性设置为public。 * Cloneable接口是不包含任何方法的!其实这个接口仅仅是一个标志,而且这个标志也仅仅是针对Object类中clone()方法的。 * 如果clone类没有实现Cloneable接口,并调用了Object的 clone()方法(也就是调用了super.Clone()方法),那么Object的clone()方法就会抛出 CloneNotSupportedException异常。 */ protected native Object clone() throws CloneNotSupportedException;

      深复制(实现深度拷贝,则需要将实现了Cloneable接口并重写了clone方法的类中,所有的引用类型也全部实现Cloneable接口并重写clone方法,而且需要将引用类型的属性全部拷贝一遍。

/** * 深复制  * */public class ShallowStudent implements Cloneable {
         private String name;          private int age;          public People people;          public ShallowStudent(){
        this.name  = "test1";         this.age = 23;         this.people = new People("test1",23);     }          public People getPeople(){
        return people;     }          public Object clone(){
        ShallowStudent o = null;         try {
            o = (ShallowStudent)super.clone(); //子对象也要进行复制             o.people = (People) this.people.clone();         } catch (CloneNotSupportedException e) {
            System.out.println(e.toString());         }         return o;     }          public static void main(String[] args) {
        ShallowStudent s1 = new ShallowStudent();         ShallowStudent s2 = (ShallowStudent)s1.clone();         s2.name = "test2";         s2.age = 22;         System.out.println("s1 name: "+s1.name + " s1 age: "+s1.age + " People: " + s1.getPeople());         System.out.println("s2 name: "+s2.name + " s2 age: "+s2.age + " People: " + s2.getPeople());     } } class People implements Cloneable{
         public String peopleName;          public int  peopleAge;          public People(String peopleName,int peopleAge){
        this.peopleName = peopleName;         this.peopleAge = peopleAge;     }              public Object clone(){
        People o = null;         try {
            o = (People)super.clone();         } catch (CloneNotSupportedException e) {
            e.printStackTrace();         }         return o;     } } ---------》》》--------- s1 name: test1 s1 age: 23 People: com.pingan.haofang.fjs.web.action.People@15db9742 s2 name: test2 s2 age: 22 People: com.pingan.haofang.fjs.web.action.People@6d06d69c --------《《《----------

 

   从原型模式的使用方式不难推断出,原型模式常使用于以下场景:

               1、对象的创建非常复杂,可以使用原型模式快捷的创建对象。

               2、在运行过程中不知道对象的具体类型,可使用原型模式创建一个相同类型的对象,或者在运行过程中动态的获取到一个对象的状态。

 

下面我们来看下原型模式的主要优点:

               1、由于clone方法是由虚拟机直接复制内存块执行,所以在速度上比使用new的方式创建对象要快。

               2、可以基于原型,快速的创建一个对象,而无需知道创建的细节。可以在运行时动态的获取对象的类型以及状态,从而创建一个对象。

 

               然而原型模式的缺点也是相当明显的,主要的缺点就是实现深度拷贝比较困难,需要很多额外的代码量

 

               不过实际当中我们使用原型模式时,也可以写一个基类实现Cloneable接口重写clone方法,然后让需要具有拷贝功能的子类继承自该类,这是一种节省代码量的常用方式

 

              JDK的StringBuffer类没有重写clone()方法,还是final类型的,说明不能用继承的方法来实现clone()方法。

              如果一个类中包含有StringBuffer类型对象或和 StringBuffer相似类的对象,我们有两种选择:要么只能实现影子clone,要么就在类的clone()方法中加一句(假设是 SringBuffer对象,而且变量名仍是p): o.p = new StringBuffer(p.toString());

              利用串行化来做深复制

             

import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.ObjectStreamField;import java.io.Serializable;/** * 通过串行化实现深复制 * @author junjin4838 * */public class ShallowStudent implements Serializable {        private String name;        private int age;        public People people;        public ShallowStudent(String name,int age,People people){        this.name  = name;        this.age = age;        this.people = people;    }        public People getPeople(){        return people;    }        public Object deepClone() throws IOException, ClassNotFoundException{        ByteArrayOutputStream bo = new ByteArrayOutputStream();        ObjectOutputStream oo = new  ObjectOutputStream(bo);        //从流里面读出数据        oo.writeObject(this);        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());        ObjectInputStream oi = new ObjectInputStream(bi);         return (oi.readObject());    }        public static void main(String[] args) throws ClassNotFoundException, IOException {        People people = new People("test1",20);        ShallowStudent s1 = new ShallowStudent("test2",21,people);        ShallowStudent s2 = (ShallowStudent) s1.deepClone();        s2.name = "test3";        s2.age = 22;        System.out.println("s1 name: "+s1.name + " s1 age: "+s1.age + " People: " + s1.getPeople());        System.out.println("s2 name: "+s2.name + " s2 age: "+s2.age + " People: " + s2.getPeople());    }}class People implements Serializable{        public String peopleName;        public int  peopleAge;        public People(String peopleName,int peopleAge){        this.peopleName = peopleName;        this.peopleAge = peopleAge;    }    } -----------》》》--------- s1 name: test2 s1 age: 21 People: com.pingan.haofang.fjs.web.action.People@232204a1 s2 name: test3 s2 age: 22 People: com.pingan.haofang.fjs.web.action.People@4554617c 两个对象是不一样的 ----------《《《----------

 

 

 

 

               

转载于:https://www.cnblogs.com/junjin4838/p/5444797.html

你可能感兴趣的文章