设计模式(3)-- 原型模式 (clone分析)

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

    在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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值