首页 > 其他分享 >设计模式之【原型模式】,深入理解深拷贝与浅拷贝

设计模式之【原型模式】,深入理解深拷贝与浅拷贝

时间:2022-12-17 11:00:14浏览次数:45  
标签:对象 clone 原型 Student 拷贝 设计模式 public


文章目录

  • ​​一、什么是原型模式​​
  • ​​二、原型模式实现方式​​
  • ​​1、传统方式​​
  • ​​2、原型模式​​
  • ​​熟悉浅拷贝和深拷贝​​
  • ​​浅拷贝实现对象克隆​​
  • ​​深拷贝实现对象克隆​​

一、什么是原型模式

原型模式: 用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。

对于熟悉 JavaScript 语言的前端程序员来说,原型模式是一种比较常用的开发模式。这是因为,有别于 Java、C++ 等基于类的面向对象编程语言,JavaScript 是一种基于原型的面向对象编程语言。即便 JavaScript 现在也引入了类的概念,但它也只是基于原型的语法糖而已。不过,如果你熟悉的是 Java、C++ 等这些编程语言,那在实际的开发中,就很少用到原型模式了。

二、原型模式实现方式

1、传统方式

public class Student {
private String name;
private int age;

// get set
}

public static void main(String[] args) {
//传统的方法
Student student = new Student("tom", 1);

Student student2 = new Student(student.getName(), student.getAge());
Student student3 = new Student(student.getName(), student.getAge());
//....

System.out.println(student2);
System.out.println(student3);
//...
}

传统的方式的优缺点:

优点是比较好理解,简单易操作。

在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低

总是需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活

2、原型模式

原型模式(Prototype 模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。

原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。

工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象.clone()。

熟悉浅拷贝和深拷贝

原型模式主要用于对象的复制,复制的类需要具备以下两个条件:

1)实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出 CloneNotSupportedException异常。

2)重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,需要拷贝的类需要将clone方法的作用域修改为public类型。

注:Object类的clone方法只会拷贝java中的8中基本类型以及他们的封装类型,另外还有String类型。对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。

浅拷贝实现对象克隆

浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值(仅对于简单的值类型数据),而所有的对其他对象的引用都仍然指向原来的对象。换言之,只负责克隆按值传递的数据(比如:基本数据类型、String类型)。

public class Student implements Cloneable {
private String name;
private int age;

private Address addr;

public Student(String name, int age, Address addr) {
this.name = name;
this.age = age;
this.addr = addr;
}

public String getName() {
return name;
}

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

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Address getAddr() {
return addr;
}

public void setAddr(Address addr) {
this.addr = addr;
}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", addr=" + addr +
'}';
}

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

class Address implements Cloneable {
private String addrName;

public Address(String addrName) {
this.addrName = addrName;
}

public String getAddrName() {
return addrName;
}

public void setAddrName(String addrName) {
this.addrName = addrName;
}

@Override
public String toString() {
return "Address{" +
"addrName='" + addrName + '\'' +
'}';
}

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

class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Student student = new Student("zhangsan", 13, new Address("China"));

Student clone = (Student) student.clone();

System.out.println(student);
System.out.println(clone);

student.setName("lisi");
student.getAddr().setAddrName("Canada");
System.out.println(student);
System.out.println(clone);

}
}

运行结果:

Student{name=‘zhangsan’, age=13, addr=Address{addrName=‘China’}}
Student{name=‘zhangsan’, age=13, addr=Address{addrName=‘China’}}
Student{name=‘lisi’, age=13, addr=Address{addrName=‘Canada’}}
Student{name=‘zhangsan’, age=13, addr=Address{addrName=‘Canada’}}

我们发现,Java的clone,只是单纯的浅拷贝,对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。

对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。

这里我们修改了原Student的Address中的值,克隆过的Student中的Address中的值也相应的改变了。

深拷贝实现对象克隆

1、
把上面的Student中clone方法改造一下,即可以实现深拷贝:

@Override
public Object clone() throws CloneNotSupportedException {
Student newStu = (Student) super.clone();
newStu.addr = (Address) this.getAddr().clone();
return newStu;
}

拷贝的时候,将对象中的引用数据也拷贝一份即可。

2、可以使用序列化 + 反序列化的方式实现深拷贝:

public Object deepCopy(Object object) {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(object);

ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);

return oi.readObject();
}

当然,序列化反序列化的方式有很多,这里就不一一列举了。


标签:对象,clone,原型,Student,拷贝,设计模式,public
From: https://blog.51cto.com/u_13540373/5949514

相关文章

  • Datawhale组队学习——大话设计模式Task02学习笔记
    Task02:策略、装饰、代理个人抽象理解:策略:灵活多变的算法实现规则装饰:把类的核心职责和装饰功能区分开了,去除相关类中重复的装饰逻辑;把类中的装饰功能从类中搬移去除,简......
  • 【设计模式】行为型模式之备忘录模式
    对当前的状态进行保存,封装成一个状态类,使用另一个状态管理者对当前角色的某些状态进行保存(只限于保存,不能修改)一般来说,会先定义一个状态接口,使用时会在需要保存状态的类......
  • 设计模式--行为型模式
    行为型模式模板模式在含有继承结构的代码中,模板方法模式是非常常用的。用一个统一的父类实现定义所有的方法和接口。不同的子类来完成自己具体的实现。父类定义了骨架(调......
  • 设计模式--创建型模式
    创建型模式      创建型模式的作用就是创建对象,说到创建一个对象,最熟悉的就是new一个对象,然后set相关属性。但是,在很多场景下,我们需要给客户端提供更加友......
  • 设计模式--结构型模式
    结构型模式建型模式介绍了创建对象的一些设计模式,这节介绍的结构型模式旨在通过改变代码结构来达到解耦的目的,使得我们的代码容易维护和扩展。代理模式第一个要介绍的代......
  • 深入理解 Python 的对象拷贝和内存布局
    深入理解Python的对象拷贝和内存布局前言在本篇文章当中主要给大家介绍python当中的拷贝问题,话不多说我们直接看代码,你知道下面一些程序片段的输出结果吗?a=[1,2,......
  • 我学设计模式 之 观察者模式
      观察者(Observer)模式1.观察者模式介绍      观察者模式是对象行为模式,又叫做发布-定义模式、模型-视图模式、源-监听器模式或从属者模式。       观察......
  • 前端面试题【js动态创建节点、怎么阻止冒泡事件、怎么阻止默认事件、什么是深拷贝,什么
    前端的那些基本标签​​......
  • 程序设计模式急救笔记
    打完游戏发现考试内容一点没看,紧急抢救,精神状态不甚正常,慎读。例子有的不是很好,为了考试的时候抄个UML图方便罢了。0.UML图1.关联关系类A用到了类B,A->B类A用到了类B......
  • 说说设计模式~ 观察者模式与消费者模式的区别
    ​​返回目录​​再说概念这两个模式确实有点相似,都为了实现程序的解耦产生的,观察者一般又称发布/订阅模式,它一般是有一个主题对象,然后有多个订阅者去关注它,当它的状态发生......