4、原型模式

1、简介

原型模式(Prototype Pattern)用于创建重复的对象,同时又能保证性能。它属于创建型设计模式,它提供了一种创建对象的最佳方法。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。在需要短时间创建大量的对象和创建对象很耗时的情况下,原型模式比通过new对象大大提高了时间效率。

使用场景;
1、资源优化场景。
2、类初始化需要消耗非常多的资源,这个资源包括数据、硬件资源等。
性能和安全要求的场景。
3、通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
4、一个对象多个修改者的场景。
5、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
6、在实际项目中,原型模式很少单独出现,一般是和工厂模式一起出现,通过clone方法创建一个对象,然后由工厂方法提供给调用者。

注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现Cloneable,重写,深拷贝是通过实现Serializable读取二进制流
在这里插入图片描述
2、具体实现

class student implements  Cloneable{    //继承Cloneable接口 实现拷贝
    private String name;
    private String sex;
    //  Getter...Setter...
    public student(){
        //  wait()。。。
        //假设一个类的初始化需要很长的时间
    }

    protected  Object clone() {

        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}
public class Main {

    public static void main(String[] args) {
        student s = new student();
        student s1 = (student) s.clone();   //只是对已有对象的克隆,在堆中进行,不执行类加载过程,不执行类初始化

    }
}

假设上述student类的初始化要执行很长时间的话 使用克隆模式是一种很高效的办法。

3、浅拷贝

(1)简介
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。

(2)特点
(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个。
(2) 对于引用类型,比如数组或者类对象,因为引用类型是引用传递,所以浅拷贝只是把内存地址赋值给了成员变量,它们指向了同一内存空间。改变其中一个,会对另外一个也产生影响。

class subject{

    private  String name;

    public String getName() {
        return name;
    }

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


class student implements  Cloneable{    //继承Cloneable接口 实现拷贝
    private String name;
    private subject subject;
    //  Getter...Setter...
    public student(){
        //  wait()。。。
        //假设一个类的初始化需要很长的时间
    }

    public String getName() {
        return name;
    }

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

    public com.company.subject getSubject() {
        return subject;
    }

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

    protected  Object clone() {

        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}
public class Main {

    public static void main(String[] args) {
      subject subjectA = new subject();
      subjectA.setName("aaa");
      student studentA = new student();
      studentA.setName("AAA");
      studentA.setSubject(subjectA);
      // 克隆前
      System.out.println("我是A 我的名字是"+studentA.getName()+" 我自己的subject是"+studentA.getSubject().getName());

      //另一个
      student studentB = (student) studentA.clone();
      System.out.println("我是b  我的名字是"+studentB.getName());
      subject subjectB = studentB.getSubject();
      subjectB.setName("bbbb");
      System.out.println("我还是A  B在克隆完后 本来改变的是他的subject 但是我现在的subject名字是"+studentA.getSubject().getName());
    }
}

运行结果:
在这里插入图片描述
由此可见,浅克隆 ,基本数据类型克隆的直接是值,对象克隆的是引用,如果克隆后发生改变,那被克隆之前的对象也会改变。

5、深拷贝

(1)简介
通过上面的例子可以看到,浅拷贝会带来数据安全方面的隐患,例如我们只是想修改了 studentB 的 subject,但是 studentA 的 subject 也被修改了,因为它们都是指向的同一个地址。所以,此种情况下,我们需要用到深拷贝。

深拷贝,在拷贝引用类型成员变量时,为引用类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝。

(2)特点
(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。
(2) 对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响。
(3) 对于有多层对象的,每个对象都需要实现 Cloneable 并重写 clone() 方法,进而实现了对象的串行层层拷贝。
(4) 深拷贝相比于浅拷贝速度较慢并且花销较大。

(3) 实现

class subject implements Serializable {

    private  String name;

    public String getName() {
        return name;
    }

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


class student implements  Cloneable ,Serializable{    //继承Cloneable接口 实现拷贝
    private String name;
    private subject subject;
    //  Getter...Setter...
    public student(){
        //  wait()。。。
        //假设一个类的初始化需要很长的时间
    }

    public String getName() {
        return name;
    }

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

    public com.company.subject getSubject() {
        return subject;
    }

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

    protected  Object clone() {
        try {
            //将对象写入流中
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
            objectOutputStream.writeObject(this);
            //从流中取出
            ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
            ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
            return objectInputStream.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}
public class Main {

    public static void main(String[] args) {
      subject subjectA = new subject();
      subjectA.setName("aaa");
      student studentA = new student();
      studentA.setName("AAA");
      studentA.setSubject(subjectA);
      // 克隆前
      System.out.println("我是A 我的名字是"+studentA.getName()+" 我自己的subject是"+studentA.getSubject().getName());

      //另一个
      student studentB = (student) studentA.clone();
      System.out.println("我是b  我的名字是"+studentB.getName());
      subject subjectB = studentB.getSubject();
      subjectB.setName("bbbb");
      System.out.println("我还是A  B在克隆完后 改变的是他的subject 我现在的subject名字还是"+studentA.getSubject().getName());
    }
}

运行结果:
在这里插入图片描述
由此可见 studentB对subject的改变并不能影响studentA中的subject。

总而言之,浅拷贝与深拷贝,除了性能外,最大的区别就是,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象,只是复制了一个引用。深拷贝把要复制的对象所引用的对象都复制了一遍,为引用类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值