最近买了一本设计模式的书籍看了看,发现自己对设计模式没有什么概念,同时,在看某些设计模式的时候发现在项目中应用了不少,但是是哪个设计模式却说不明白!! 不扯皮,今天记录的是原型模式,java中天然支持原型模式,也就是在jdk层面就支持这个了(clone),代理模式也是(Proxy)。

原型模式的概念:

用原型实例制定创建对象的种类,并通过拷贝这些原型并创建新的对象,相当于是拷贝一份副本

这个时候我突然诗兴大发, 忽如一夜春风来,千树万树梨花开…… 这特么不是说的是java中Object类的clone方法么? 难怪java天然支持拷贝模型,但是,在java中如果实现克隆对象,要有两个标准

  1. 必须要实现人家自带的Cloneable接口
  2. 必须要重写clone方法。 emmm... 文字太干燥了,直接上栗子:
import lombok.Data;
@Data
public class User implements Cloneable{
    
    private String name ;

    private int age;

    private int sex;

    @Override
    protected Object clone() {
        try {
		 // 这里是重写clone方法的逻辑
            User    user = (User) super.clone();
            return  user;

        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return null;
    }
}

再来上一个测试类:

public class Client {

    public static void main(String[] args) {
        User user = new User();
        user.setAge(18);
        User cloneUser = (User)user.clone();
        System.out.println(cloneUser);
    }
}
##得到结果 ::User(name=null, age=18, sex=0)

说明: 拷贝成功 原理: 因为需要实现cloneable接口,这个接口它是一个标记接口,没有任何方法(跟Serializable一样),jvm用来对标记的对象执行它的clone方法,然后进行内存二进制流的拷贝,因此性能会比new好很多。

这里问题又来了,如果你克隆的对象里面引用了其他对象呢??试试?

@Data
public class User implements Cloneable{
    //很多根头发
    private ArrayList hairs = new ArrayList();

    private String name ;

    private int age;

    private int sex;

    @Override
    protected Object clone() {
        try {
            User    user = (User) super.clone();
            return  user;

        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return null;
    }
}
public class Client {

    public static void main(String[] args) {
        User user = new User();
        user.setAge(18);
        user.getHairs().add("aaaa");
        User cloneUser = (User)user.clone();
        cloneUser.getHairs().add("abc");
        System.out.println(cloneUser);
        System.out.println(user);
    }
}
### User(hairs=[aaaa, abc], name=null, age=18, sex=0)
###User(hairs=[aaaa, abc], name=null, age=18, sex=0)

翻车了? 没错,当对象里面引用另外一个对象的时候,它这里其实是引用这个对象的地址,这种拷贝也就是我们常说的浅拷贝,有人说了,String不就是引用的对象么?注意:String它是jdk提供的不可变对象,它内部是通过final类型的char数组实现的,并没有实现clone 方法,因此它是可以直接被克隆的,与此相同的,比如int Integer,long Long ...等等都是可以直接被拷贝的。

那诸如上面的情况,应该如何修改呢?也就是如何修改为深拷贝呢? so essy!!!直接对内部的对象引用也执行clone方法

 @Override
    protected Object clone() {
        try {
            User    user = (User) super.clone();
            user.hairs = (ArrayList) hairs.clone();
            return  user;

        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return null;
    } 

对,没错,这里我们在点到ArrayList源码看一下,它是有实现clone方法的 file

至此,原型模式的栗子就举完毕了,

原型模式使用场景

  1. 在使用多个对象但又不便于创建多个对象所承担的消耗的时候,可以使用克隆
  2. 通过new一个对象的时候需要很繁琐的进行数据准备,可以使用原型模式对当前对象进行重复修改(一个对象多个修改者的场景)