好似你,饼印咁:原型模式、浅拷贝与深拷贝

简介: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。大概意思就是:允许一个对象再创建另外一个可定制的对象,根本无需知道对象创建的细节。

0x01:原型模式简介


用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。大概意思就是:允许一个对象再创建另外一个可定制的对象,根本无需知道对象创建的细节。其工作原理是通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。其UML类图如下:


微信图片_20220502091213.png


主要角色如下:


  • Prototype:为原型类,声明一个克隆自身的接口;
  • ConcretePrototype:为具体实现类,实现一个克隆自身的操作;
  • Client(客户端)只需让一个原型克隆自身,就可以创建一个新的对象。


原型模式主要用于对象的拷贝,其核心是就是类图中的原型类Prototype,其一般是一个java抽象类或是一个接口,定义了具体原型类所需要的实现的方法。


0x02:原型模式的实现


原型类Prototype


public abstract class Prototype {
private String name;
private String sex;
public Prototype() {
    }
public Prototype(String name, String sex) {
super();
this.name = name;
this.sex = sex;
    }
public String getName() {
return name;
    }
public void setName(String name) {
this.name = name;
    }
public String getSex() {
return sex;
    }
public void setSex(String sex) {
this.sex = sex;
    }
//声明一个实现克隆自身的方法
public abstract Prototype clone();
}


具体实现类ConcretePrototype


public class ConcretePrototype extends Prototype{
    public ConcretePrototype(String name, String sex) {
super(name, sex);
    }
@Override
    public ConcretePrototype clone() {
return new ConcretePrototype(this.getName(), this.getSex());
    }
}


原型模式测试代码


public class Client {
public static void main(String[] args) {
ConcretePrototype p = new ConcretePrototype("Hello", "gril");
ConcretePrototype c = (ConcretePrototype) p.clone();
        System.out.println(c.getName() + ", " + c.getSex());
    }
}


0x03:JDK中浅拷贝、深拷贝与原型模型


Java赋值是复制对象引用,如果想要得到一个对象的副本,使用赋值操作是无法达到目的的


普通pojo对象Person


public class Person {
private String name;
private int age;
//省略getter  setter
}


测试代码


public static void main(String[] args) {
Person p1 = new Person();
      p1.setAge(31);
      p1.setName("java乐园");
Person p2 = p1;
      System.out.println(p1==p2);// true
}


如果创建一个对象的新的副本,也就是说他们的初始状态完全一样,但以后可以改变各自的状态,而互不影响,就需要用到Java中对象的复制,如原生的clone()方法。如何进行对象克隆呢?


Object对象有个clone()方法,实现了对象中各个属性的复制,但它的可见范围是protected的,所以实体类使用克隆的前提是:


实现Cloneable接口,这是一个标记接口,自身没有任何方法;


覆盖clone()方法,可见性提升为public;


public class Person implements Cloneable{
private String name;
private int age;
//省略getter  setter
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
    }
}


测试代码


public static void main(String[] args) throws Exception{
        Person p1=new Person();
p1.setAge(31);
p1.setName("java乐园");
          Person p2=(Person) p1.clone();
          System.out.println(p1 == p2);//false
p2.setName("架构师知音");
          System.out.println("p1 = " + p1.getName() + ", " + p1.getAge()); // p1 = java乐园, 31
          System.out.println("p2 = " + p2.getName() + ", " + p2.getAge()); //p2 = 架构师知音, 31
    }


该测试用例只有两个基本类型的成员变量,测试目的达到。好像没有任何问题?为Person增加一个地址的引用类型成员变量Address


public class Address {
private String city;
private String street;
//省略getter  setter
}


Person增加Address成员对象


public class Person implements Cloneable{
private String name;
private int age;
private Address address;
//省略 getter  setter
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
    }
}


测试代码


public static void main(String[] args) throws Exception{
          Address address=new Address();
          address.setStreet("天庭88号");
          address.setCity("魔都");
          Person p1=new Person();
p1.setAge(31);
p1.setName("java乐园");
p1.setAddress(address);
          Person p2=(Person) p1.clone();
          System.out.println(p1 == p2); //false
p2.getAddress().setCity("广东");
          System.out.println("p1=" + p1.getAddress().getCity());
          System.out.println("p2="+  p2.getAddress().getCity());
    }


遇到了麻烦,只修改了p2的城市,两个地址城市都变成了“广东”。这个实例是浅拷贝和深拷贝的典型用例。


浅拷贝:被复制对象的所有值属性都含有与原来对象的相同,而所有的对象引用属性仍然指向原来的对象。


深拷贝:在浅拷贝的基础上,所有引用其他对象的变量也进行了clone,并指向被复制过的新对象。


也就是说,一个默认的clone()方法实现机制,仍然是赋值。


如果一个被复制的属性都是基本类型,那么只需要实现当前类的cloneable机制就可以了,此为浅拷贝;如果被复制对象的属性包含其他实体类对象引用,那么这些实体类对象都需要实现Cloneable接口并覆盖clone()方法。对Address做以下修改:


public class Address implements Cloneable {
private String city;
private String street;
//省略 getter  setter
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
    }
}


通过以上案例可以看出通过Cloneable接口并覆盖Object类的clone()方法就很方便的实现了原型模式。


参考:https://www.cnblogs.com/xuanxufeng/p/6558330.html
相关文章
|
3月前
|
设计模式 Java 关系型数据库
23种设计模式 —— 原型模式【克隆羊、浅拷贝、深拷贝】
23种设计模式 —— 原型模式【克隆羊、浅拷贝、深拷贝】
|
6月前
|
存储 Cloud Native Linux
C++ 深拷贝浅拷贝
C++ 深拷贝浅拷贝
|
2月前
|
JavaScript 前端开发
浅拷贝和深拷贝
浅拷贝和深拷贝
18 2
|
4月前
|
存储 Java Apache
【面试问题】深拷贝和浅拷贝的区别?
【1月更文挑战第27天】【面试问题】深拷贝和浅拷贝的区别?
|
10月前
面试官:深拷贝与浅拷贝有啥区别?
面试官:深拷贝与浅拷贝有啥区别?
|
6月前
|
JavaScript
一文弄懂浅拷贝和深拷贝
一文弄懂浅拷贝和深拷贝
28 0
|
6月前
|
C++
22 C++ - 深拷贝和浅拷贝
22 C++ - 深拷贝和浅拷贝
20 0
|
9月前
浅说一下深拷贝和浅拷贝,如何自己实现一个深拷贝?
浅说一下深拷贝和浅拷贝,如何自己实现一个深拷贝?
50 0
|
10月前
|
JSON 数据格式
深拷贝和浅拷贝、及实现方式
深拷贝和浅拷贝、及实现方式
68 0
|
10月前
|
C++
C++学习笔记_06 深拷贝和浅拷贝 2021-04-19
C++学习笔记_06 深拷贝和浅拷贝 2021-04-19