一、原型模式
原型模式与构造器模式、单例模式、工厂方法模式、抽象工厂模式一样,都属于创建型模式。原型模式理解起来,相对简单,来看下其定义:
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式的实例的拷贝包括浅复制和深复制:
浅复制:将一个对象复制后,其基本数据类型的变量都会重新创建,而引用类型的变量指向的还是原对象所指向的,也就是指向的内存堆地址没变。
深复制:将一个对象复制后,不论是基本数据类型还是引用类型,都是重新创建的。
从以上可以看出,浅复制中的引用类型只是复制了变量的值,其地址仍然没变;而深复制完全复制了变量,复制后变量地址会有所变化。
有一个消息类,其属性及方法声明如下:
package com.example.designmode.prototypepattern;
import java.io.*;
import java.util.Date;
/**
* <h3>design-mode</h3>
* <p>消息类</p>
*
* @author : ZhangYuJie
* @date : 2022-01-23 16:39
**/
public class Message implements Cloneable, Serializable {
private String name; //消息名称
private String size; //消息大小
private int type; //消息类型
private Date date; //创建日期
public static final int TEXT = 0x01;
public static final int PIC = 0x02;
public static final int VIDEO = 0x03;
public static final int MIX = 0x04;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
@Override
public Message clone() throws CloneNotSupportedException {
return (Message) super.clone();
}
public Message deepClone() throws CloneNotSupportedException,
IOException, ClassNotFoundException {
//把对象写入到字节流中
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
//把字节流转化为对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return (Message) ois.readObject();
}
@Override
public String toString() {
String tos = "name[" + name + "],size[" + size +
"],type[" + type + "],date[" + date + "]";
return tos;
}
public static void main(String[] a) {
Message msg = new Message();
msg.setName("好友消息");
msg.setSize("123KB");
msg.setType(Message.TEXT);
msg.setDate(new Date());
System.out.println("msg:" + msg.toString());
try {
Message cloneMsg = msg.clone();
System.out.println("msg:" + msg.toString());
System.out.println("cloneMsg:" + cloneMsg.toString());
System.out.println(cloneMsg.getDate() == msg.getDate());
System.out.println(cloneMsg.getName() == msg.getName());
System.out.println(cloneMsg.getType() == msg.getType());
} catch (Exception e) {
e.printStackTrace();
}
}
}
通过实现Cloneable接口,java默认的实现方式是浅复制,而非深复制。由于Object并没有实现Cloneable接口,所以子类必须实现Cloneable,并调用基类的clone方法才能实现浅复制。
要实现深复制,Message类需要实现序列化,通过对象流与字节流之间的转化,达到深复制的目的。
Message类中的deepClone方法就是深复制的实现。
以上main方法的执行结果如下:
"C:\Program Files\Java\jdk-11.0.11\bin\java.exe" "-javaagent:D:\IntelliJ IDEA 2020.1\lib\idea_rt.jar=52369:D:\IntelliJ IDEA 2020.1\bin" -Dfile.encoding=UTF-8 -classpath D:\workspace2021\design-mode\target\classes;D:\Repository\org\springframework\boot\spring-boot-starter-web\2.6.2\spring-boot-starter-web-2.6.2.jar;D:\Repository\org\springframework\boot\spring-boot-starter\2.6.2\spring-boot-starter-2.6.2.jar;D:\Repository\org\springframework\boot\spring-boot-starter-logging\2.6.2\spring-boot-starter-logging-2.6.2.jar;D:\Repository\ch\qos\logback\logback-classic\1.2.9\logback-classic-1.2.9.jar;D:\Repository\ch\qos\logback\logback-core\1.2.9\logback-core-1.2.9.jar;D:\Repository\org\apache\logging\log4j\log4j-to-slf4j\2.17.0\log4j-to-slf4j-2.17.0.jar;D:\Repository\org\apache\logging\log4j\log4j-api\2.17.0\log4j-api-2.17.0.jar;D:\Repository\org\slf4j\jul-to-slf4j\1.7.32\jul-to-slf4j-1.7.32.jar;D:\Repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\Repository\org\yaml\snakeyaml\1.29\snakeyaml-1.29.jar;D:\Repository\org\springframework\boot\spring-boot-starter-json\2.6.2\spring-boot-starter-json-2.6.2.jar;D:\Repository\com\fasterxml\jackson\core\jackson-databind\2.13.1\jackson-databind-2.13.1.jar;D:\Repository\com\fasterxml\jackson\core\jackson-annotations\2.13.1\jackson-annotations-2.13.1.jar;D:\Repository\com\fasterxml\jackson\core\jackson-core\2.13.1\jackson-core-2.13.1.jar;D:\Repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.13.1\jackson-datatype-jdk8-2.13.1.jar;D:\Repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.13.1\jackson-datatype-jsr310-2.13.1.jar;D:\Repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.13.1\jackson-module-parameter-names-2.13.1.jar;D:\Repository\org\springframework\boot\spring-boot-starter-tomcat\2.6.2\spring-boot-starter-tomcat-2.6.2.jar;D:\Repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.56\tomcat-embed-core-9.0.56.jar;D:\Repository\org\apache\tomcat\embed\tomcat-embed-el\9.0.56\tomcat-embed-el-9.0.56.jar;D:\Repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.56\tomcat-embed-websocket-9.0.56.jar;D:\Repository\org\springframework\spring-web\5.3.14\spring-web-5.3.14.jar;D:\Repository\org\springframework\spring-beans\5.3.14\spring-beans-5.3.14.jar;D:\Repository\org\springframework\spring-webmvc\5.3.14\spring-webmvc-5.3.14.jar;D:\Repository\org\springframework\spring-aop\5.3.14\spring-aop-5.3.14.jar;D:\Repository\org\springframework\spring-context\5.3.14\spring-context-5.3.14.jar;D:\Repository\org\springframework\spring-expression\5.3.14\spring-expression-5.3.14.jar;D:\Repository\org\springframework\boot\spring-boot-devtools\2.6.2\spring-boot-devtools-2.6.2.jar;D:\Repository\org\springframework\boot\spring-boot\2.6.2\spring-boot-2.6.2.jar;D:\Repository\org\springframework\boot\spring-boot-autoconfigure\2.6.2\spring-boot-autoconfigure-2.6.2.jar;D:\Repository\org\projectlombok\lombok\1.18.22\lombok-1.18.22.jar;D:\Repository\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;D:\Repository\org\springframework\spring-core\5.3.14\spring-core-5.3.14.jar;D:\Repository\org\springframework\spring-jcl\5.3.14\spring-jcl-5.3.14.jar com.example.designmode.prototypepattern.Message
msg:name[好友消息],size[123KB],type[1],date[Sun Jan 23 16:40:38 CST 2022]
msg:name[好友消息],size[123KB],type[1],date[Sun Jan 23 16:40:38 CST 2022]
cloneMsg:name[好友消息],size[123KB],type[1],date[Sun Jan 23 16:40:38 CST 2022]
true
true
true
进程已结束,退出代码为 0
说明,浅复制只是复制了引用类型的值,并没有改变其地址,指向的仍然是原对象的变量地址
在main方法中调用deepClone方法:
public static void main(String[] a) {
......
Message cloneMsg = msg.deepClone();
......
}
最后输出结果:
msg:name[好友消息],size[123KB],type[1],date[Tue Apr 05 17:54:39 CST 2016]
msg:name[好友消息],size[123KB],type[1],date[Tue Apr 05 17:54:39 CST 2016]
cloneMsg:name[好友消息],size[123KB],type[1],date[Tue Apr 05 17:54:39 CST 2016]
false
false
true
可以看出,深复制下,引用类型变成了完全的复制,所有的引用类型地址都变化了。
二、jdk中的原型模式
jdk中大部分类都提供了克隆的方法,比如Date类:
public class Date
implements java.io.Serializable, Cloneable, Comparable<Date>
{
public Object clone() {
Date d = null;
try {
d = (Date)super.clone();
if (cdate != null) {
d.cdate = (BaseCalendar.Date) cdate.clone();
}
} catch (CloneNotSupportedException e) {} // Won't happen
return d;
}
}