JAVA设计模式之原型模式

1、 原型模式概念

该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。java中复制通过clone()实现的。clone中涉及深、浅复制。深、浅复制的概念如下:

⑴浅复制(浅克隆)
   原型对象的成员中的变量是值类型时,则将复制一份给克隆对象值,如果 成员是对象则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。 Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址
方法:调用 java.lang.Object的clone()方法

⑵深复制(深克隆)
   被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
方法:
1、对象内部所有引用型对象都进行clone。
2、对象序列化
原型模式代码

public class Prototype implements Cloneable{

    private String name;

    public String getName() {
        return name;
    }

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

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

    public static void main ( String[] args){
        Prototype pro = new Prototype();
        Prototype pro1 = (Prototype)pro.clone();
    }
}

2、浅克隆:

/**
 * Created by yd on 2019/3/27.
 * 浅克隆
 */
public class Student implements Cloneable {
    private String name;
    private Subject subject;

    public Student(String name,Subject subject){
        this.name = name;
        this.subject = subject;
    }

    public String getName() {
        return name;
    }

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

    public Subject getSubject() {
        return subject;
    }

    public void setSubject(Subject subject) {
        this.subject = subject;
    }

    public String toString(){
        return "name:"+name+"; subject:{"+subject+"}";
    }

    public Object clone() throws CloneNotSupportedException{
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException{
        Subject subject = new Subject("100","100");
        Student student = new Student("YYY",subject);
        System.out.println(student);
        Student st = (Student)student.clone();
        st.setName("ZZZ");
        st.getSubject().setChinese("99");
        System.out.println(student);
        System.out.println(st);
    }
}
class Subject {
    private String english;
    private String chinese;

    public Subject(String chinese,String english){
        this.chinese = chinese;
        this.english = english;
    }

    public String toString(){
        return "english : "+english+"; chinese:"+chinese;
    }
    public String getEnglish() {
        return english;
    }

    public void setEnglish(String english) {
        this.english = english;
    }

    public String getChinese() {
        return chinese;
    }

    public void setChinese(String chinese) {
        this.chinese = chinese;
    }
}

运行结果:

name:YYY; subject:{english : 100; chinese:100}
name:YYY; subject:{english : 100; chinese:99}
name:ZZZ; subject:{english : 100; chinese:99}

在这里插入图片描述

浅克隆对于引用类型,只克隆了引用,因此两个对象的subject公共同一个内存地址,一个对象变化,会引起另一个对象响应的变化。

3、深克隆:

import java.io.*;

/**
 * Created by yd on 2019/3/28.
 * 深克隆
 */
public class Student2 implements Serializable,Cloneable{
    private String name;
    private Subject2 subject2;

    public Student2(String name,Subject2 subject2){
        this.name = name;
        this.subject2 = subject2;
    }

    public String getName() {
        return name;
    }

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

    public Subject2 getSubject2() {
        return subject2;
    }

    public void setSubject2(Subject2 subject2) {
        this.subject2 = subject2;
    }

    @Override
    public String toString(){
        return "name:"+name+"; subject:{"+subject2+"}";
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student2 student2 = (Student2)super.clone();
        student2.subject2 = (Subject2) this.subject2.clone();
        return student2;
    }

    /* 深复制 二进制的写法,需要类序列化*/
    public Object deepClone() throws IOException, ClassNotFoundException {

        //使用序列化和反序列化实现深复制
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        byte[] bytes = bos.toByteArray();
        /* 读出二进制流产生的新对象 */
        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }

    public static void main(String[] args) throws CloneNotSupportedException{
        Subject2 subject2= new Subject2("100","100");
        Student2 student2 = new Student2("YYY",subject2);
        System.out.println(student2);
        //方法一:对象内部所有引用型对象都进行clone。
        //Student2 st2 = (Student2)student2.clone();
        //方法二:对象序列化
        Student2 st2 = (Student2)student2.clone();
        st2.setName("ZZZ");
        st2.getSubject2().setChinese("99");
        System.out.println(student2);
        System.out.println(st2);
    }
}
class Subject2 implements Serializable, Cloneable {
    private String english;
    private String chinese;

    public Subject2(String chinese,String english){
        this.chinese = chinese;
        this.english = english;
    }

    @Override
    public String toString(){
        return "english : "+english+"; chinese:"+chinese;
    }
    @Override
    public Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
    public String getEnglish() {
        return english;
    }

    public void setEnglish(String english) {
        this.english = english;
    }

    public String getChinese() {
        return chinese;
    }

    public void setChinese(String chinese) {
        this.chinese = chinese;
    }
}

运行结果:

name:YYY; subject:{english : 100; chinese:100}
name:YYY; subject:{english : 100; chinese:100}
name:ZZZ; subject:{english : 100; chinese:99}

通过对引用类型值subject添加clone方法,并且对student对象的clone方法改造,实现深克隆。
在这里插入图片描述

4、其他:

拷贝还有2个知识点,对象拷贝时,类的构造函数是不会被执行的。一个实现了 Cloneable 并重写了 clone 方法的类 Programmer,有一个无参构造或有参构造 ,通过 new 关键字产生了一个对象 A,再然后通过 A.clone()方式产生了一个新的对象 T,那么在对象拷贝时构造函数是不会被执行的。即拷贝的过程中只执行一次构造方法。

Clone 与 final 两对冤家。对象的 clone 与对象内的 final 属性是由冲突.在上面的Programmer类中修改为private final Address address;去掉get,set方法, proto.address=(Address) address.clone();这一句就会报错: proto.address=(Address) address.clone();final类型不能重新设置值。解决办法就是删除掉fina咯

深拷贝和浅拷贝建议不要混合使用,一个类中某些引用使用深拷贝某些引用使用浅拷贝,这是一种非常差的设计,特别是是在涉及到类的继承,父类有几个引用的情况就非常的复杂,建议深拷贝和浅拷贝分开实现。

注意:final 类型修饰的成员变量不能进行深度拷贝

最后说一下,原型模式的使用场景

1、在创建对象的时候,我们不只是希望被创建的对象继承其基类的基本结构,还希望继承原型对象的数据。

2、希望对目标对象的修改不影响既有的原型对象(深度克隆的时候可以完全互不影响)。

3、隐藏克隆操作的细节,很多时候,对对象本身的克隆需要涉及到类本身的数据细节。

4、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等;

5、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式;

6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone的方法创建一个对象,然后由工厂方法提供给调用者。原型模式先产生出一个包含

大量共有信息的类,然后可以拷贝出副本,修正细节信息,建立了一个完整的个性对象。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值