原型模式总结

原型模式:通过给出一个原型对象来指明所创建的对象的类型,然后使用自身实现的克隆接口来复制这个原型对象,该模式就是用这种方式来创建出更多同类型的对象。

实现:Object类的clone方法可以将一个对象复制一份,需要实现clone方法的java类必须实现Cloneable接口,该接口表示该类能够复制且具备复制的能力。Object 类的 clone 方法是一个本地方法,它可以直接操作内存中的二进制流,所以性能相对 new 实例化来说,更佳。

一个对象通过new创建的过程为:
1、在内存中开辟一块空间;
2、在开辟的内存空间中创建对象;
3、调用对象的构造函数进行初始化对象。

而一个对象通过clone创建的过程为:
1、根据原对象内存大小开辟一块内存空间;
2、复制已有对象,克隆对象中所有属性值。

相对new来说,clone少了调用构造函数。如果构造函数中存在大量属性初始化或大对象,则使用clone的复制对象的方式性能会好一些。

实现原型类的条件

  • 实现Cloneable接口;Cloneable 接口与序列化接口的作用类似,它只是告诉虚拟机可以安全地在实现了这个接口的类上使用 clone 方法。在 JVM 中,只有实现了 Cloneable 接口的类才可以被拷贝,否则会抛出 CloneNotSupportedException 异常。
  •  重写 Object 类中的 clone 方法:在 Java 中,所有类的父类都是 Object 类,而 Object 类中有一个 clone 方法,作用是返回对象的一个拷贝
  • 在重写的 clone 方法中调用 super.clone():默认情况下,类不具备复制对象的能力,需要调用 super.clone() 来实现。


/**
 * 实现Cloneable接口的原型抽象类Prototype
 */
public class Prototype implements Cloneable{
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}


/**
 * 实现原型类
 */
public class ConcretePrototype extends Prototype {

    public ConcretePrototype(){
        System.out.println("原型模式实现类构造方法");
    }
    public void show(){
        System.out.println("原型模式实现类show方法");
    }
}

public class Client {
    public static void main(String[] args) throws Exception{
        ConcretePrototype cp = new ConcretePrototype();
        for(int i = 0; i < 10; i++){
            ConcretePrototype clonecp = (ConcretePrototype)cp.clone();
            clonecp.show();
        }
    }
}

--输出结果
原型模式实现类构造方法
原型模式实现类show方法
原型模式实现类show方法
原型模式实现类show方法
原型模式实现类show方法
原型模式实现类show方法
原型模式实现类show方法
原型模式实现类show方法
原型模式实现类show方法
原型模式实现类show方法
原型模式实现类show方法

从结果可以看出,Prototype的构造方法只调用了一次。

原型模式与普通对象引用复制的区别

普通对象引用复制(Object o1 = new Object();Object o2 = o1)

public class Student {

    private String name;

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

    public String getName() {
        return name;
    }
}

public class Test {
    public static void main(String[] args) {
        Student student1 = new Student();
        student1.setName("张三");

        Student student2 = student1;
        student2.setName("李四");

        System.out.println("学生1的name:" + student1.getName());
        System.out.println("学生2的name:" + student2.getName() );

    }
}

此时输出的结果为:

学生1的name:李四
学生2的name:李四

小结:此时student1和student2指向的是同一个内存地址,student2的值改变了,student1的值也随之改变。

原型模式实现对象复制

public class Student implements Cloneable{

    private String name;

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

    public String getName() {
        return name;
    }

    @Override
    protected Object clone(){
        Student stu = null;
        try {
            stu = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return stu;
    }
}

public class Test {
    public static void main(String[] args) {
        Student student1 = new Student();
        student1.setName("张三");

        Student student2 = (Student) student1.clone();
        student2.setName("李四");

        System.out.println("学生1和学生2对象是否相等:" +(student1 == student2));

        System.out.println("学生1的name:" + student1.getName());
        System.out.println("学生2的name:" + student2.getName() );

    }
}

此时输出的结果为:

学生1和学生2对象是否相等:false
学生1的name:张三
学生2的name:李四

小结:通过clone方法复制对象才是真正的对象复制,它是一个完全独立的对象 (不同的内存地址)

 深拷贝和浅拷贝

浅拷贝:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向对象的内存地址;

深拷贝:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

浅拷贝的问题

public class Teacher implements Cloneable{

    private String name;

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

    public String getName() {
        return name;
    }

    @Override
    protected Object clone() {
        Teacher teacher = null;
        try {
            teacher = (Teacher) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return teacher;
    }
}

public class Student implements Cloneable{

    private String name;

    private Teacher teacher;

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

    public String getName() {
        return name;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    @Override
    protected Object clone(){
        Student stu = null;
        try {
            stu = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return stu;
    }
}

public class Test {
    public static void main(String[] args) {
        Student student1 = new Student();
        student1.setName("张三");

        Teacher teacher1 = new Teacher();
        teacher1.setName("张老师");

        student1.setTeacher(teacher1);

        Student student2 = (Student) student1.clone();
        student2.setName("李四");
        student2.getTeacher().setName("李老师");


        System.out.println("学生" + student1.getName() +"的老师是:" + student1.getTeacher().getName());
        System.out.println("学生" + student2.getName() +"的老师是:" + student2.getTeacher().getName());

    }
}

输出结果:

学生张三的老师是:李老师
学生李四的老师是:李老师

小结:在给李四修改老师的时候,张三的老师也跟着被修改了

深拷贝

方式一:重写Student类的clone方法实现深拷贝

@Override
    protected Object clone(){
        Student stu = null;
        try {
            stu = (Student) super.clone();
            Teacher teacher = (Teacher) stu.getTeacher().clone(); //克隆teacher对象
            stu.setTeacher(teacher);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return stu;
    }

此时输出结果为:

学生张三的老师是:张老师
学生李四的老师是:李老师

如果Student还有一个address属性,那么clone方法还需要加上克隆address的代码

/**
     * 基于clone方法实现深拷贝
     * @return
     */
    @Override
    protected Object clone(){
        Student stu = null;
        try {
            stu = (Student) super.clone();
            Teacher teacher = (Teacher) stu.getTeacher().clone(); //克隆teacher对象
            stu.setTeacher(teacher);

            Address address = (Address) stu.getAddress().clone(); //克隆address对象
            stu.setAddress(address);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return stu;
    }

方式二:使用序列化实现深拷贝(推荐)

/**
     * 使用序列化实现深拷贝
     * @return
     */
    public Object deepCLone(){
        Student stu = null;
        try(
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(bos);
        ){
            //序列化,当前对象以流的方式输出
            oos.writeObject(this);
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            //反序列化
            stu = (Student) ois.readObject();
            bis.close();
            ois.close();
        } catch (Exception e){
            e.printStackTrace();
        }
        return stu;
    }

此时输出结果为: 

学生张三的老师是:张老师
学生李四的老师是:李老师

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值