Welcome everyone

原型模式之克隆羊

设计模式 汪明鑫 664浏览 0评论

原型模式

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

以原型为模,克隆出所有属性均相同的对象

如克隆羊多利,记住这个例子,就不难理解原型模式了

 

 

使用场景

● 资源优化场景
类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。

● 性能和安全要求的场景
通过 new 产生一个对象需要非常繁琐的数据准备或访问权限。

● 一个对象多个修改者的场景
一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用.

 

通用代码

public class PrototypeClass implements Cloneable{
 @Override
 public PrototypeClass clone(){
   PrototypeClass prototypeClass = null;
   try {
      prototypeClass = (PrototypeClass)super.clone();
    } catch (CloneNotSupportedException e) {
    }
    return prototypeClass;
 }
}

 

Cloneable & clone()

原型模式实际上就是实现 Cloneable 接口,重写 clone()方法。

 

public interface Cloneable {
}

Cloneable里没有方法就是一个标示,实现了这个接口就要重写clone方法

clone方法是顶级父类Object的方法

 

clone方法与与构造函数

clone方法会避开构造函数

从堆内存中以二进制流的方式进行拷贝,重新分配一个内存块

 

克隆羊例子

/**
 * 原型羊🐑
 *
 * @author wmx
 * @date 2019-08-20
 */
@Data
public class Sheep implements Cloneable{
    private String name;
    private String sex;

    public Sheep() {
        System.out.println("enter Constructor");
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep source = new Sheep();
        source.setName("Jack");
        source.setSex("公");
        Sheep clone = (Sheep) source.clone();
        clone.setName("Tom");
        System.out.println("clone name : "+clone.getName()+",sex : "+clone.getSex());
    }
}

输出:

enter Constructor
clone name : Tom,sex : 公

 

只进入了一次构造函数,证明了clone时没有进入构造函数

 

而且我们如果在clone对象生成后修改原型的属性,克隆的对象属性不变

source.setSex("母");

Tom还是公的

 

浅拷贝 vs 深拷贝

概念:

 

浅拷贝: Object 类提供的方法 clone 只是拷贝本对象,其对象内部的数组、引用
对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝,其
他的原始类型比如 int、 long、 char、 string(当做是原始类型)等都会被拷贝。
注意: 使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一是
类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个
原始类型或不可变对象。

深拷贝: 对私有的类变量进行独立的拷贝
如: arrayList = (ArrayList<String>)this.arrayList.clone();

 

举个例子:

Sheep 类加个list记录羊的爱好

@Data
public class Sheep implements Cloneable{
    private String name;
    private String sex;

    private List<String> hobbies;

    public Sheep() {
        System.out.println("enter Constructor");
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

 

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep source = new Sheep();
        source.setName("Jack");
        source.setSex("公");
        source.setHobbies(Lists.newArrayList("吃草"));
        Sheep clone = (Sheep) source.clone();
        clone.setName("Tom");
        List<String> hobbies = clone.getHobbies();
        hobbies.add("睡觉");
        source.setHobbies(hobbies);
        System.out.println("clone name : "+clone.getName()+",sex : "+clone.getSex()+",hobbies"+clone.getHobbies());
    }
}

修改了原型的list,然后克隆对象的list也被修改了

因此原型对象和克隆出的对象共用堆内的同一个list,没有对原型类中的类变量进行单独处理称为浅克隆

 

解决:在clone() 中对list也进行单独的处理

@Data
public class Sheep implements Cloneable{
    private String name;
    private String sex;

    private ArrayList<String> hobbies = new ArrayList<>();

    public Sheep() {
        System.out.println("enter Constructor");
    }

    @Override
    public Sheep clone(){
        Sheep sheep = null;
        try{
            sheep = (Sheep) super.clone();
            sheep.hobbies = (ArrayList<String>)this.hobbies.clone();
        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return sheep;
    }
}
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep source = new Sheep();
        source.setName("Jack");
        source.setSex("公");
        source.setHobbies(Lists.newArrayList("吃草"));

        Sheep clone = (Sheep) source.clone();
        clone.setName("Tom");
        ArrayList<String> hobbies = clone.getHobbies();
        hobbies.add("睡觉");
        //clone.setHobbies(hobbies);

        System.out.println("clone name : "+clone.getName()+",sex : "+clone.getSex()+",hobbies : "+clone.getHobbies());

        System.out.println("source name : "+source.getName()+",sex : "+source.getSex()+",hobbies : "+source.getHobbies());
    }
}

输出:

enter Constructor
clone name : Tom,sex : 公,hobbies : [吃草, 睡觉]
source name : Jack,sex : 公,hobbies : [吃草]

 

 

转载请注明:汪明鑫的个人博客 » 原型模式之克隆羊

喜欢 (0)

说点什么

您将是第一位评论人!

提醒
avatar
wpDiscuz