设计模式 ~ 创建型模式 ~ 原型模式 ~ Prototype Pattern。
what。
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。
结构。
原型模式包含如下角色。
-
抽象原型类。
规定了具体原型对象必须实现的的 clone(); 方法。 -
具体原型类。
实现抽象原型类的 clone(); 方法,ta 是可被复制的对象。 -
访问类。
使用具体原型类中的 clone(); 方法来复制新的对象。
接口类图如下。
原型模式的克隆分为浅克隆
和深克隆
。
-
浅克隆。
创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。 -
深克隆。
创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
浅克隆(原型模式)。
创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
Java 中的 Object 类中提供了 clone(); 方法来实现浅克隆。
Cloneable 接口是上面的类图中的抽象原型类,而实现了 Cloneable 接口的子实现类就是具体的原型类。代码如下。
package com.example.proto.type.pattern.shallow.clone;
import com.sun.org.slf4j.internal.Logger;
import com.sun.org.slf4j.internal.LoggerFactory;
/**
* @author geek
*/
public class RealizeType implements Cloneable {
private static final Logger log
= LoggerFactory.getLogger(RealizeType.class);
public RealizeType() {
System.out.println(" ~ RealizeType ~ RealizeType; ~ ~ {}");
}
@Override
public RealizeType clone() {
try {
System.out.println(" ~ RealizeType ~ clone; ~ ~ {}");
return (RealizeType) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
package com.example.proto.type.pattern.shallow.clone;
/**
* @author geek
*/
public class Client {
private static final org.slf4j.Logger log
= org.slf4j.LoggerFactory.getLogger(Client.class);
public static void main(String[] args) {
// 创建一个原型类对象。
RealizeType realizeType = new RealizeType();
// 调用 RealizeType 类中的 clone(); 方法进行对象的克隆。
RealizeType clone = realizeType.clone();
// 没有执行构造方法。
log.debug(" ~ Client ~ main; ~ clone == realizeType ~ " + (clone == realizeType) + "\r\n ~ clone ~ " + clone + "\r\n ~ realizeType ~ " + realizeType);
/*
~ RealizeType ~ RealizeType; ~ ~ {}
~ RealizeType ~ clone; ~ ~ {}
23:39:37.479 [main] DEBUG com.example.proto.type.pattern.shallow.clone.Client - ~ Client ~ main; ~ clone == realizeType ~ false
~ clone ~ com.example.proto.type.pattern.shallow.clone.RealizeType@724af044
~ realizeType ~ com.example.proto.type.pattern.shallow.clone.RealizeType@4678c730
*/
}
}
eg.
package com.example.proto.type.pattern.use.shallow.clone;
/**
* 奖状类。
*
* @author geek
*/
public class Citation implements Cloneable {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public void show() {
System.out.println(name + " 同学:在 2020 学年第一学期中表现优秀,被评为三好学生。特发此状!");
}
// @Override
// protected Object clone() throws CloneNotSupportedException {
// return super.clone();
// }
@Override
public Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
}
package com.example.proto.type.pattern.use.shallow.clone;
/**
* @author geek
*/
public class CitationTest {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建原型对象。
Citation citation = new Citation();
citation.setName("张三");
// 克隆浅拷贝复制奖状。
Citation citation1 = citation.clone();
// 将奖状的名字修改李四。
citation1.setName("李四");
citation.show();
citation1.show();
/*
张三 同学:在 2020 学年第一学期中表现优秀,被评为三好学生。特发此状!
李四 同学:在 2020 学年第一学期中表现优秀,被评为三好学生。特发此状!
*/
}
}
- 使用场景。
对象的创建非常复杂,可以使用原型模式快捷的创建对象。
性能和安全要求比较高。
package com.example.proto.type.pattern.use.shallow.copy2;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author geek
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
}
package com.example.proto.type.pattern.use.shallow.copy2;
import lombok.Data;
/**
* 奖状类。
*
* @author geek
*/
@Data
public class Citation implements Cloneable {
private Student student;
public void show() {
System.out.println(student.getName() + "同学:在 2020 学年第一学期中表现优秀,被评为三好学生。特发此状!");
}
// @Override
// protected Object clone() throws CloneNotSupportedException {
// return super.clone();
// }
@Override
public Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
}
package com.example.proto.type.pattern.use.shallow.copy2;
/**
* @author geek
*/
public class CitationTest {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建原型对象。
Citation citation = new Citation();
// 创建张三学生对象。
Student student1 = new Student("张三");
citation.setStudent(student1);
// 克隆 ~ 浅拷贝复制奖状。
Citation citation1 = citation.clone();
Student student2 = citation1.getStudent();
student2.setName("李四");
citation.show();
citation1.show();
}
// student1 对象和 student2 对象是同一个对象,就会产生将 student1 对象中 name 属性值改为“李四”,
// 两个 Citation(奖状)对象中显示的都是李四。
// 这就是浅克隆的效果,对具体原型类(Citation)中的引用类型的属性进行引用的复制。
// 这种情况需要使用深克隆,而进行深克隆需要使用对象流。
/*
李四同学:在 2020 学年第一学期中表现优秀,被评为三好学生。特发此状!
李四同学:在 2020 学年第一学期中表现优秀,被评为三好学生。特发此状!
*/
}
深克隆。
创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
package com.geek.prototype.use.deep.copy;
import com.sun.tracing.dtrace.NameAttributes;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author geek
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
}
package com.example.proto.type.pattern.use.deep;
import lombok.Data;
import java.io.Serializable;
/**
* 奖状类。
*
* @author geek
*/
@Data
public class Citation implements Cloneable, Serializable {
private Student student;
public void show() {
System.out.println(student.getName() + " 同学:在 2020 学年第一学期中表现优秀,被评为三好学生。特发此状!");
}
// @Override
// protected Object clone() throws CloneNotSupportedException {
// return super.clone();
// }
@Override
public Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
}
package com.geek.prototype.use.deep.copy;
/**
* @author geek
*/
public class CitationTest {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建原型对象。
Citation citation = new Citation();
// 创建张三学生对象。
Student student1 = new Student("张三");
citation.setStudent(student1);
// 克隆~ 浅复制奖状。
Citation c2 = citation.clone();
Student student2 = c2.getStudent();
student2.setName("李四");
citation.show();
c2.show();
}
// student1 对象和 student2 对象是同一个对象,就会产生将 student1 对象中 name 属性值改为“李四”,
// 两个 Citation(奖状)对象中显示的都是李四。
// 这就是浅克隆的效果,对具体原型类(Citation)中的引用类型的属性进行引用的复制。
// 这种情况需要使用深克隆,而进行深克隆需要使用对象流。
/*
李四同学:在 2020 学年第一学期中表现优秀,被评为三好学生。特发此状!
李四同学:在 2020 学年第一学期中表现优秀,被评为三好学生。特发此状!
*/
}
- 实现深克隆。
package com.example.proto.type.pattern.use.deep;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* @author geek
*/
public class CitationTest {
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
// 创建原型对象。
Citation citation = new Citation();
// 创建张三学生对象。
Student student = new Student("张三");
citation.setStudent(student);
Path path = Paths.get(".\\a.txt");
// 克隆 ~ 深克隆复制奖状。
// 创建对象输出流对象。
ObjectOutputStream objectOutputStream = new ObjectOutputStream(Files.newOutputStream(path));
// 写对象。
objectOutputStream.writeObject(citation);
// 释放资源。
objectOutputStream.close();
// 读取对象。
// 创建对象输入流对象。
ObjectInputStream objectInputStream = new ObjectInputStream(Files.newInputStream(path));
// 读取对象。
Citation citation1 = (Citation) objectInputStream.readObject();
// 释放资源。
objectInputStream.close();
Student student1 = citation1.getStudent();
student1.setName("李四");
citation.show();
citation1.show();
// Exception in thread "main" java.io.NotSerializableException: com.geek.prototype.use.deep.Citation
// public class Student implements Serializable {
}
/*
张三 同学:在 2020 学年第一学期中表现优秀,被评为三好学生。特发此状!
李四 同学:在 2020 学年第一学期中表现优秀,被评为三好学生。特发此状!
*/
}
package com.geek.prototype.use.deep;
import lombok.Data;
import java.io.Serializable;
/**
* 奖状类。
*
* @author geek
*/
@Data
public class Citation implements Cloneable, Serializable {
private Student student;
public void show() {
System.out.println(student.getName() + "同学:在 2020 学年第一学期中表现优秀,被评为三好学生。特发此状!");
}
@Override
public Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
}
package com.geek.prototype.use.deep;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author geek
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student implements Serializable {
private String name;
}
package com.geek.prototype.demo00;
import java.util.Date;
/**
* 浅克隆。
* 实现一个接口。
* 重写一个方法 clone(); ~ 直接 return super.clone();。
*/
public class Video implements Cloneable {
private String name;
private Date createTime;
public Video() {
}
public Video(String name, Date createTime) {
this.name = name;
this.createTime = createTime;
}
// protected native Object clone() throws CloneNotSupportedException;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", createTime=" + createTime +
'}';
}
}
package com.geek.prototype.demo00;
import java.util.Date;
/**
* 客户端。克隆 video 对象。
*/
public class Bilibili {
public static void main(String[] args) throws CloneNotSupportedException {
// 原型对象。
Video v1 = new Video("狂神说 Java", new Date());
Video v2 = (Video) v1.clone();
System.out.println("v1 = " + v1);
System.out.println("v1.hashCode() = " + v1.hashCode());
System.out.println("v2 = " + v2);
System.out.println("v2.hashCode() = " + v2.hashCode());
// v1 克隆出 v2。
Video v2Clone = (Video) v1.clone();
System.out.println("v2Clone = " + v2Clone);
System.out.println("v2Clone.hashCode() = " + v2Clone.hashCode());
// v1 = Video{name='狂神说 Java', createTime=Tue Dec 08 14:39:31 CST 2020}
//v1.hashCode() = 1735600054
//v2 = Video{name='狂神说 Java', createTime=Tue Dec 08 14:39:31 CST 2020}
//v2.hashCode() = 21685669
//v2Clone = Video{name='狂神说 Java', createTime=Tue Dec 08 14:39:31 CST 2020}
//v2Clone.hashCode() = 2133927002
//
//Process finished with exit code 0
}
}
浅克隆。
package com.geek.prototype.demo00;
import java.util.Date;
/**
* 浅克隆。
*/
public class Bilibili02 {
public static void main(String[] args) throws CloneNotSupportedException {
// 原型对象。
Date date = new Date();
Video v1 = new Video("狂神说 Java", date);
Video v2 = (Video) v1.clone();
System.out.println("v1 = " + v1);
System.out.println("v2 = " + v2);
System.out.println("~ ~ ~ ~ ~ ~ ~");
date.setTime(123);
System.out.println("v1 = " + v1);
System.out.println("v2 = " + v2);
// v1 v2 同时指向了 date。(浅克隆)。
// v1 = Video{name='狂神说 Java', createTime=Tue Dec 08 14:42:26 CST 2020}
//v2 = Video{name='狂神说 Java', createTime=Tue Dec 08 14:42:26 CST 2020}
//~ ~ ~ ~ ~ ~ ~
//v1 = Video{name='狂神说 Java', createTime=Thu Jan 01 08:00:00 CST 1970}
//v2 = Video{name='狂神说 Java', createTime=Thu Jan 01 08:00:00 CST 1970}
System.out.println("v1.hashCode() = " + v1.hashCode());
System.out.println("v2.hashCode() = " + v2.hashCode());
// v1.hashCode() = 1735600054
//v2.hashCode() = 21685669
// 哈希值也不一样。
}
}
深克隆。
-
序列化、反序列化。
-
重写 clone();。
package com.geek.prototype.demo01;
import java.util.Date;
/**
* 深克隆。
* 实现一个接口。
* 重写一个方法 clone();。
*/
public class Video implements Cloneable {
private String name;
private Date createTime;
public Video() {
}
public Video(String name, Date createTime) {
this.name = name;
this.createTime = createTime;
}
// 深克隆。
//
//- 序列化、反序列化。
//
//- 重写 clone();。
// 深克隆。
// 重写一个方法 clone();。
// protected native Object clone() throws CloneNotSupportedException;
@Override
protected Object clone() throws CloneNotSupportedException {
Object clone = super.clone();
Video obj = (Video) clone;
// 将对象的属性也进行克隆。
obj.createTime = (Date) this.createTime.clone();
return obj;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", createTime=" + createTime +
'}';
}
}
package com.geek.prototype.demo01;
import java.util.Date;
/**
* 客户端。克隆。
*/
public class Bilibili02 {
public static void main(String[] args) throws CloneNotSupportedException {
// 原型对象。
Date date = new Date();
Video v1 = new Video("狂神说 Java", date);
Video v2 = (Video) v1.clone();
System.out.println("v1 = " + v1);
System.out.println("v2 = " + v2);
date.setTime(123);
System.out.println("v1 = " + v1);
System.out.println("v2 = " + v2);
// v1 = Video{name='狂神说 Java', createTime=Mon Nov 09 12:03:42 CST 2020}
//v2 = Video{name='狂神说 Java', createTime=Mon Nov 09 12:03:42 CST 2020}
//v1 = Video{name='狂神说 Java', createTime=Thu Jan 01 08:00:00 CST 1970}
//v2 = Video{name='狂神说 Java', createTime=Mon Nov 09 12:03:42 CST 2020}
System.out.println("v1.hashCode() = " + v1.hashCode());
System.out.println("v2.hashCode() = " + v2.hashCode());
// v1.hashCode() = 1735600054
//v2.hashCode() = 21685669
// 哈希值也不一样。
}
}