java设计模式(三)——原型模式

1、基本概念

原型模式属于创造型模式,通过二进制流拷贝已有的对象。

原型模式有浅克隆和深度克隆

2、原型类型

2.1、浅克隆

案例:

原型接口Iprototype:

public interface Iprototype<T> {

    T clone();

}

原对象实现原型接口:

@Data
public class Teacher implements Iprototype<Teacher>{

    private String name;
    private Integer age;
    private String no;
    private String addr;


    @Override
    public Teacher clone() {
        Teacher teacher = new Teacher();
        teacher.setName(this.name);
        teacher.setNo(this.no);
        teacher.setAge(this.age);
        teacher.setAddr(this.addr);
        return teacher;
    }
}

测试类:

public class Test {

    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        teacher.setAddr("wuahn");
        teacher.setAge(20);
        teacher.setName("lilei");
        teacher.setNo("1001");


        Teacher clone = teacher.clone();
        System.out.println("原对象:"+teacher);
        System.out.println("克隆对象:"+clone);

    }
}

输出:

原对象:Teacher(name=lilei, age=20, no=1001, addr=wuahn)
克隆对象:Teacher(name=lilei, age=20, no=1001, addr=wuahn)

这就是一个原型设计,我们自己手写的clone方法,如果属性过多,那么就需要设置很多属性,比较麻烦。

所以实际上我们开发上只需要实现Cloneable接口即可,是jdk为我们提供的。

我们需要覆写clone方法:

@Data
public class Teacher implements Cloneable{

    private String name;
    private Integer age;
    private String no;
    private String addr;


    @Override
    protected Teacher clone() {
        try{
            return (Teacher)super.clone();
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
}

测试输出的结果和上面一样。

上面的克隆是一种浅克隆,那么浅克隆有什么缺点?

再看一个案例:Teacher类新增一个属性classroms

@Data
public class Teacher implements Cloneable{

    private String name;
    private Integer age;
    private String no;
    private String addr;
    private List<String> classroms;

测试:

public class Test {

    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        teacher.setAddr("wuahn");
        teacher.setAge(20);
        teacher.setName("lilei");
        teacher.setNo("1001");
        List<String> list =  new ArrayList<>();
        list.add("101教室");
        list.add("102教室");
        teacher.setClassroms(list);

        Teacher clone = teacher.clone();
        clone.getClassroms().add("103教室");
        System.out.println("原对象:"+teacher);
        System.out.println("克隆对象:"+clone);

    }
}

输出:

原对象:Teacher(name=lilei, age=20, no=1001, addr=wuahn, classroms=[101教室, 102教室, 103教室])
克隆对象:Teacher(name=lilei, age=20, no=1001, addr=wuahn, classroms=[101教室, 102教室, 103教室])

当我们修改克隆对象的时候,会把原对象也修改掉,这肯定就有问题了,这也是浅克隆的问题了

当对象中引入了其他对象的时候,如集合,数组,Student等类型的属性时,克隆只能复制它们的地址。

所以它们还是指向同一内存空间。

 

 

 

 

 

这时候就需要深克隆来解决:使用序列化接口Serializable

2.2、深克隆

使用io流来进行操作。


@Data
public class Teacher implements Cloneable,Serializable{

private String name;
private Integer age;
private String no;
private String addr;
private List<String> classroms;

protected Teacher deepClone() {
try{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);

ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Teacher)ois.readObject();
}catch (Exception e){
e.printStackTrace();
return null;
}

}

@Override
protected Teacher clone() {
try{
return (Teacher)super.clone();
}catch (Exception e){
e.printStackTrace();
return null;
}
}

}
 

测试:

public class Test {

    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        teacher.setAddr("wuahn");
        teacher.setAge(20);
        teacher.setName("lilei");
        teacher.setNo("1001");
        List<String> list =  new ArrayList<>();
        list.add("101教室");
        list.add("102教室");
        teacher.setClassroms(list);

        Teacher clone = teacher.deepClone();
        clone.getClassroms().add("103教室");
        System.out.println("原对象:"+teacher);
        System.out.println("克隆对象:"+clone);

    }
}

输出:

原对象:Teacher(name=lilei, age=20, no=1001, addr=wuahn, classroms=[101教室, 102教室])
克隆对象:Teacher(name=lilei, age=20, no=1001, addr=wuahn, classroms=[101教室, 102教室, 103教室])

我们可以看到已经解决了浅克隆的问题。

注意点:深克隆会破坏单例模式,所以单例模式下不要去实现cloneable接口

 

在开发中,很多项目都提供了克隆工具,如Apache BeanUtils 和Spring BeanUtils

不推荐使用Apache  BeanUtils,在阿里巴巴的泰山版开发手册中有提到:

 

posted @ 2020-07-01 23:34  来一杯可乐  阅读(216)  评论(0编辑  收藏  举报