一、原型模式

原型模式就是从一个对象再创建另一个可定制的对象,而且不需要知道任何创建的细节。

二、基本的原型模式

这里模拟简历的创建与复制来说明原型模式的应用。

class Resume implements Cloneable{
    private String name;
    private String sex;
    private String age;
    private String timeArea;
    private String company;

    public Resume(String name){
        this.name=name;
    }

    //设置个人信息
    public void setPersonalInfo(String sex,String age){
        this.sex=sex;
        this.age=age;
    }

    //设置工作经历
    public void setWorkExperience(String timeArea,String company){
        this.timeArea=timeArea;
        this.company=company;
    }

    //打印
    public void display(){
        System.out.println("name sex age:"+name+" "+sex+" "+age);
        System.out.println("work experience"+timeArea+" "+company);
    }

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

public class PrototypePattern {
    public static void main(String[] args) throws CloneNotSupportedException{
        Resume a = new Resume("a的简历");
        a.setPersonalInfo("male", "18");
        a.setWorkExperience("2017", "companya");

        Resume b=(Resume) a.clone();
        b.setWorkExperience("2016","companyb");

        Resume c=(Resume) a.clone();
        c.setWorkExperience("2019","companyc");

        a.display();
        b.display();
        c.display();
    }
}

输出结果:

 

三、原型模式中的浅复制与深复制

上面的Resume类通过实现Cloneable接口才能使用clone方法,进行对象的克隆。

Java中对象的创建通常是通过new来实现的,通过从堆中申请一块与需要的对象类型对应的内存空间的大小,在调用构造方法返回对象给引用,

假如需要创建一批属性值都相同的对象,或许可以通过new一批对象来实现,但是这样的话,效率未免太低,并不划算。

那么如果是这样呢?

class PersonTest{

}

public class Test {
    public static void main(String[] args) {
        PersonTest personTest=new PersonTest();
        PersonTest personTest1=personTest;
        System.out.println(personTest);
        System.out.println(personTest1);
    }
}

输出结果:

可见personTest和personTest1的地址是一样的,说明他们两个其实是指向同一个对象的引用,在这个过程中并没有进行对象的复制。   

那如果是进行一次克隆呢?像这样:

class Person implements Cloneable{
    int id;
    String name;
    Education education;

    public Person(int id, String name,Education education) {
        this.id = id;
        this.name = name;
        this.education=education;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Education getEducation() {
        return education;
    }

    public void setEducation(Education education) {
        this.education = education;
    }

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

class Education{
    String theUniversity;
    String degree;

    public Education(String theUniversity, String degree) {
        this.theUniversity = theUniversity;
        this.degree = degree;
    }

    public String getTheUniversity() {
        return theUniversity;
    }

    public void setTheUniversity(String theUniversity) {
        this.theUniversity = theUniversity;
    }

    public String getDegree() {
        return degree;
    }

    public void setDegree(String degree) {
        this.degree = degree;
    }
}

public class ObjectClone {
    public static void main(String[] args) throws CloneNotSupportedException{
        Education educationA=new Education("清华大学","学士");
        Person personA=new Person(1,"小明",educationA);
        Person personB=(Person) personA.clone();
        System.out.println(personA==personB);
        System.out.println(personA.id==personB.id);
        System.out.println(personA.name==personB.name);
        System.out.println(personA.education==personB.education);
        System.out.println();
        System.out.println(personA);
        System.out.println(personB);
        System.out.println(personA.id);
        System.out.println(personB.id);
        System.out.println(personA.name);
        System.out.println(personB.name);
        System.out.println(personA.education);
        System.out.println(personB.education);
    }
}

 

输出结果:

可见克隆以后进行了对象的复制,personA与personB的地址是不同的,复制以后两个对象的id值与name,education都相同,比较结果都为true,乍看上去这好像是预期中的现象。

但是,

name作为String对象,在进行“==”的比较时比较的是对象是否相同,可见这次复制过程中,name的值并没有复制,

Education的值是一个对象,education的地址也相同,

所以这次对象复制后的情况,为什么跟上面的例子只传引用没有什么区别啊?

克隆的过程中有几个问题需要进行思考,

id作为int型数据,int是基本数据类型,复制的过程当中是逐位复制的,

但是对于String类型与其他引用类型,这里都只是将原对象的引用值拷贝给了新对象的相应字段。

 

这种就是浅复制,clone()方法进行的就是浅拷贝,这是需要注意的问题。

 

那么如果需要进行深拷贝,则需要将clone()方法进行覆盖,并且在clone()方法内把原对象引用的其他对象也拷贝一份。

那么被引用的对象也需要实现Cloneable接口,并且实现clone()方法。

class Person implements Cloneable{
    int id;
    String name;
    Education education;

    public Person(int id, String name,Education education) {
        this.id = id;
        this.name = name;
        this.education=education;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person person=(Person) super.clone();
        person.education=(Education) education.clone();
        return person;
    }
}

class Education implements Cloneable{
    String theUniversity;
    String degree;

    public Education(String theUniversity, String degree) {
        this.theUniversity = theUniversity;
        this.degree = degree;
    }

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

public class ObjectClone {
    public static void main(String[] args) throws CloneNotSupportedException{
        Education educationA=new Education("清华大学","学士");
        System.out.println(educationA);
        Person personA=new Person(1,"小明",educationA);
        Person personB=(Person) personA.clone();
        System.out.println(personA==personB);
        System.out.println(personA.id==personB.id);
        System.out.println(personA.name==personB.name);
        System.out.println(personA.education==personB.education);
        System.out.println();
        System.out.println(personA);
        System.out.println(personB);
        System.out.println(personA.id);
        System.out.println(personB.id);
        System.out.println(personA.name);
        System.out.println(personB.name);
        System.out.println(personA.education);
        System.out.println(personB.education);
    }
}

输出结果如下:

代码跟上方相比,有一点点改动,但是根据输出结果来看,这次原对象中的某些字段的所引用的对象也进行了拷贝了。

 

四、关于clone的总结

由此可见clone()只是浅拷贝,也就是说除非基本类型外,引用类型只是拷贝引用,不会复制对象。

如果需要进行深拷贝,则需要使原对象所引用的对象类型也要继承Cloneable接口,并且实现clone()方法,同时要注意对象与对象之间存在的嵌套问题,避免拷贝后的两个对象仍然因为引用的某个对象存在关系。

 

五、关于Cloneable接口

上面提到了Cloneable接口,再对这个东西多了解一点,下面这个就是Cloneable接口,可见当中没有任何方法和属性。

Cloneable接口与Serializable接口一样,都是标记型的接口,

实现 Cloneable来表示该对象能被克隆,能使用Object.clone()方法。如果没有实现 Cloneable的类对象调用clone()就会抛出CloneNotSupportedException。

如果将上面最后一个实例代码中Education类实现的Cloneable接口去除,那么程序运行时会出现异常,

这里想思考的是标记型接口是如何起作用的呢?

标记接口是没有任何方法和属性的接口,仅仅表明实现它的类是属于一个特定的类型,我们知道实现一个接口的类是可以来代表这个接口类型的。

通常创建标记接口的目的就是主要是:

建立一个公共的父接口,之后可以通过多态对其进行扩展,但是Java虚拟机却可以根据这个接口的类型选择相应的事件处理方案,也就是对实现这个标记接口的对象进行这个处理方案。

关于标记接口的作用描述还是不够清晰,待之后学习的再深入一点再继续探究吧。