问题提出
在软件开发过程中,有时候会遇到为一个类创建多个实例的情况,这些实例内部成员变量完全相同或有细微的差异,而且实例的创建开销远大于或者需要输入较多的参数。如果能通过复制一个已创建的对象实例来重复创建
多个相同的对象,就可以大大减少创建对象的开销,这个时候就需要原型设计模式。
原型模式
原型设计模式是一种创建型模式,按功能分为浅复制和深复制两种情况。
浅复制和深复制
- 浅复制:如果原型对象的成员变量是值类型,则将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象。
- 深复制:无论原型对象成员变量是值类型还是引用类型,豆浆复制一份给克隆对象。
实现方法
原型复制常用方法有三种:
- 利用构造函数方法
- 利用Cloneable接口方法
- 利用Serializable接口方法
首先给出要复制的类:
public class Student {
private String name;
private int age;
private Address add;
public Student(String name, int age, Address add) {
this.name = name;
this.age = age;
this.add = add;
}
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 getAdd() {
return add;
}
public void setAdd(Address add) {
this.add = add;
}
}
public class Address {
private String pro;
private String city;
private String zip;
public Address(String pro, String city, String zip) {
this.pro = pro;
this.city = city;
this.zip = zip;
}
public String getPro() {
return pro;
}
public void setPro(String pro) {
this.pro = pro;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
}
利用构造函数方法
浅复制
public class Student {
// 其他代码相同
public Student(Student s) {
name = s.getName();
age = s.getAge();
add = s.getAdd();
}
}
public class Test {
public static void main(String[] args) {
Address add = new Address("liaoning","dalian","116081");
Student s = new Student("zhang",20,add);
Student s2 = new Student(s);
System.out.println("s="+s+"\ts2="+s2);
System.out.println("s.name="+s.getName()+"\ts2.name="+s2.getName());
System.out.println("s.age="+s.getAge()+"\ts2.age="+s2.getAge());
System.out.println("s.add="+s.getAdd()+"\ts2.add="+s2.getAdd());
}
}
OUTPUT
s=Student@41629346 s2=Student@404b9385
s.name=zhang s2.name=zhang
s.age=20 s2.age=20
s.add=Address@58372a00 s2.add=Address@58372a00
两个add变量的地址相同。
深复制
public class Student {
// 其他代码相同
public Student(Student s) {
name = s.getName();
age = s.getAge();
add = new Address(s.getAdd());
}
}
public class Address {
// 其他代码相同
public Address(Address add){
pro = add.getPro();
city = add.getCity();
zip = add.getZip();
}
}
OUTPUT
s=Student@3b07d329 s2=Student@41629346
s.name=zhang s2.name=zhang
s.age=20 s2.age=20
s.add=Address@7699a589 s2.add=Address@58372a00
两个add变量的地址不同。显然深复制要比浅复制复杂。
一般例子
举个更一般的例子,用A(B(C(D)))来表示A包含B类引用,B类包含C类引用,C类包含D类引用。若要完成对A类的深复制,必须对引用包含链中的每一个环节都进行处理。
class A {
// 其他基本成员变量
private B b;
A(A a) {
// 其他基本成员变量的复制
b = new B(a.getB());
}
}
class B {
// 其他基本成员变量
private C c;
B(B b) {
// 其他基本成员变量的复制
c = new C(b.getC());
}
}
class C {
// 其他基本成员变量
private D d;
C(C c) {
// 其他基本成员变量的复制
d = new D(c.getD());
}
}
class D {
// 其他基本成员变量
D(D d) {
// 其他基本成员变量的复制
}
}
利用Cloneable接口方法
Java类都继承自Object类。Object类提供一个clone()方法,可以将Java对象复制一份,因此Java中可以直接使用Clone()方法来实现对象的克隆。但是clone()方法是一个protected保护方法,外部类不能直接调用。
protected native Object clone() throws CloneNotSupportedException;
因此Java语言对对象复制进行了规范:能够实现复制来的Java类必须实现一个标识接口Cloneable。
public interface Cloneable {
}
该接口没有定义任何方法,仅是起到一个标识作用,表达的语义是:该类用到了对象复制功能。
因此抛开本模式而言,空接口有时也很有意义。
浅复制
public class Student implements Cloneable{
// 其他代码相同
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Address add = new Address("liaoning","dalian","116081");
Student s = new Student("zhang",20,add);
Student s2 = (Student) s.clone();
System.out.println("s="+s+"\ts2="+s2);
System.out.println("s.name="+s.getName()+"\ts2.name="+s2.getName());
System.out.println("s.age="+s.getAge()+"\ts2.age="+s2.getAge());
System.out.println("s.add="+s.getAdd()+"\ts2.add="+s2.getAdd());
}
}
OUTPUT
s=Student@3b07d329 s2=Student@41629346
s.name=zhang s2.name=zhang
s.age=20 s2.age=20
s.add=Address@7699a589 s2.add=Address@7699a589
深复制
public class Student implements Cloneable {
// 其他代码相同
@Override
protected Object clone() throws CloneNotSupportedException{
Student s = (Student) super.clone();
s.setAdd((Address) add.clone());
return s;
}
}
public class Address implements Cloneable {
// 其他代码相同
@Override
protected Object clone() throws CloneNotSupportedException {
Address add = (Address) super.clone();
return add;
}
}
OUTPUT
s=Student@41629346 s2=Student@404b9385
s.name=zhang s2.name=zhang
s.age=20 s2.age=20
s.add=Address@58372a00 s2.add=Address@4dd8dc3
一般例子
A(B(C(D)))
class A implements Cloneable {
// 其他基本成员变量
private B b;
@Override
protected Object clone() throws CloneNotSupportedException{
A a = (A) super.clone();
a.setB((B) b.clone());
return a;
}
}
class B implements Cloneable {
// 其他基本成员变量
private C c;
@Override
protected Object clone() throws CloneNotSupportedException{
B b = (B) super.clone();
b.setC((C) c.clone());
return b;
}
}
class C implements Cloneable {
// 其他基本成员变量
private D d;
@Override
protected Object clone() throws CloneNotSupportedException{
C c = (C) super.clone();
c.setD((D) d.clone());
return c;
}
}
class D implements Cloneable {
// 其他基本成员变量
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
利用Serializable序列化接口方法
深复制
利用构造方法、Cloneable接口方法实现对象深复制都稍显复杂,而利用Serializable序列化接口方法实现深复制则要简单的多。Serializable接口同样是一个空接口。
public class Student implements Cloneable, Serializable {
// 其他代码相同
@Override
protected Object clone() throws CloneNotSupportedException{
Object obj = null;
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 从流里读回来
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
obj = ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
public class Address implements Serializable {
// 其他代码相同
}
一般例子
A(B(C(D)))
class A implements Cloneable {
// 其他代码相同
private B b;
@Override
protected Object clone() throws CloneNotSupportedException{
Object obj = null;
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 从流里读回来
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
obj = ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
class B implements Serializable {
// 其他代码相同
}
class C implements Serializable {
// 其他代码相同
}
class D implements Serializable {
// 其他代码相同
}
应用示例
原型管理器
原型管理器是对原型的管理类,可以添加原型对象,也可以获得原型对象。
原型管理器,用单例模式实现,用哈希表来维持各原型元素。
public class PrototypeManager {
private Hashtable ht = new Hashtable();
private static PrototypeManager pm = new PrototypeManager();
public static PrototypeManager getPrototypeManager() {
return pm;
}
public void addPrototype(String key, Object obj) {
ht.put(key, obj);
}
public Object getPrototype(String key){
return ht.get(key);
}
}
学生类(浅复制)
public class Student implements Cloneable{
private String name;
private int age;
private PubInfo info;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
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 PubInfo getInfo() {
return info;
}
public void setInfo(PubInfo info) {
this.info = info;
}
}
公共信息类,对于同个大学的所有学生而言是共享的。
public class PubInfo implements Cloneable {
private String college;
private String city;
private String zip;
public PubInfo(String college, String city, String zip) {
this.college = college;
this.city = city;
this.zip = zip;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getCollege() {
return college;
}
public void setCollege(String college) {
this.college = college;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
}
现在要求创建m各辽宁师大学生对象、n个大连理工大学学生对象。
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
int m = 10, n = 10;
PrototypeManager pm = PrototypeManager.getPrototypeManager();
PubInfo p = new PubInfo("liaoshi", "dalian", "116081");
Student s = new Student();
s.setInfo(p);
pm.addPrototype("liaoshi", s);
PubInfo p2 = new PubInfo("dagong", "dalian", "116023");
Student s2 = new Student();
s2.setInfo(p2);
pm.addPrototype("dagong", s2);
Scanner sc = new Scanner(System.in);
Vector<Student> vec = new Vector<>();
Student t = (Student) pm.getPrototype("liaoshi");
for (int i = 0; i < m; i++) {
Student st = (Student) t.clone();
st.setName(sc.nextLine());
st.setAge(sc.nextInt());
vec.add(st);
}
Vector<Student> vec2 = new Vector<>();
Student t2 = (Student) pm.getPrototype("dagong");
for (int i = 0; i < m; i++) {
Student st = (Student) t.clone();
st.setName(sc.nextLine());
st.setAge(sc.nextInt());
vec2.add(st);
}
}
}