原型模式
定义
原型模式(prototype)是指用原型实例指定创建对象的种类,并通过拷贝这些原型来创建新的对象。
结构
1.抽象原型类(prototype):实现cloneable接口,并重写clone()方法。
2.具体原型类:实现一个克隆自身的操作。
类图
Prortotype类
package com.headfirst.prototypemode.interfaces;
/**
* 1.实现Cloneable接口
* 2.重写clone()方法
*/
public class IPrototype implements Cloneable {
public String id;
public String name;
@Override
protected IPrototype clone() {
IPrototype clone = null;
try {
clone = (IPrototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试类
public static void main(String[] args) {
IPrototype iPrototype = new IPrototype();
iPrototype.setId("1");
iPrototype.setName("lisi");
IPrototype clone = iPrototype.clone();
clone.setId("2");
clone.setName("wangwu");
System.out.println(iPrototype.getName());//lisi
System.out.println(clone.getName());//wangwu
System.out.println(iPrototype == clone);//false
}
浅拷贝与深拷贝的区别
浅拷贝
抽象原型类
package com.headfirst.prototypemode.interfaces;
import java.util.ArrayList;
/**
* 1.实现Cloneable接口
* 2.重写clone()方法
* 3.成员变量为集合类型
*/
public class IPrototype implements Cloneable {
public ArrayList<String> list = new ArrayList<String>();
@Override
protected IPrototype clone() {
IPrototype clone = null;
try {
clone = (IPrototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
public ArrayList<String> getList() {
return list;
}
public void setList(ArrayList<String> list) {
this.list = list;
}
public void display(){
for (String str:list) {
System.out.print(str+",");
}
System.out.println();
}
}
测试类
public static void main(String[] args) {
IPrototype iPrototype = new IPrototype();
IPrototype clone = iPrototype.clone();
iPrototype.getList().add("tmp1");
iPrototype.display();//tmp1,
clone.getList().add("tmp2");
iPrototype.display();//tmp1,tmp2,
clone.display();//tmp1,tmp2,
System.out.println(iPrototype.getList() == clone.getList());//true
}
通过打印的结果不难发现,iPrototype和clone中的list操作的是同一个对象,打印出来都是tmp1,tmp2,这是因为IPrototype中的list为引用类型,浅拷贝只能克隆基本数据类型,而是无法克隆引用数据类型,这就是浅拷贝。
那么怎么办?深拷贝就是将指定的引用数据类型再克隆一份。
深拷贝
IPrototype类
package com.headfirst.prototypemode.interfaces;
import java.util.ArrayList;
/**
* 1.实现Cloneable接口
* 2.重写clone()方法
* 3.成员变量为集合类型
*/
public class IPrototype implements Cloneable {
public ArrayList<String> list = new ArrayList<String>();
@Override
protected IPrototype clone() {
IPrototype clone = null;
try {
clone = (IPrototype) super.clone();
clone.list = (ArrayList<String>) this.list.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
public ArrayList<String> getList() {
return list;
}
public void setList(ArrayList<String> list) {
this.list = list;
}
public void display(){
for (String str:list) {
System.out.print(str+",");
}
System.out.println();
}
}
测试类
public static void main(String[] args) {
IPrototype iPrototype = new IPrototype();
IPrototype clone = iPrototype.clone();
iPrototype.getList().add("tmp1");
iPrototype.display();//tmp1,
clone.getList().add("tmp2");
iPrototype.display();//tmp1,
clone.display();//tmp2,
System.out.println(iPrototype.getList() == clone.getList());//false
}
由于浅拷贝无法克隆引用数据类型,所以需要对这里的list先克隆一份,再将它指向iPrototype.list,注意一点,这里的ArrayList本身就已经实现了Cloneable接口(看源码),所以这里换成自定义类,首先这个类需要先实现Cloneable接口。
从结果可以看出Prototype.getList() == clone.getList()返回结果为false,所以这里的list不会互相影响。
优点
1.提高性能。
2.避免复杂的构造函数
缺点
1.必须实现Cloneable接口
2.对于已有的对象需要增加clone方法,这违背了OCP开放封闭原则
总结
原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype。Prototype类需要具备以下两个条件:
(1)实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。
(2)重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此Prototype类需要将clone方法的作用域修改为public类型。
JDK类库中的原型模式
java.util.ArrayList<E>
/**
* Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
* elements themselves are not copied.)
*
* @return a clone of this <tt>ArrayList</tt> instance
*/
public Object clone() {
try {
@SuppressWarnings("unchecked")
ArrayList<E> v = (ArrayList<E>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
java.util.HashSet<E>
/**
* Returns a shallow copy of this <tt>HashSet</tt> instance: the elements
* themselves are not cloned.
*
* @return a shallow copy of this set
*/
public Object clone() {
try {
HashSet<E> newSet = (HashSet<E>) super.clone();
newSet.map = (HashMap<E, Object>) map.clone();
return newSet;
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
}
java.util.HashMap
/**
* Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and
* values themselves are not cloned.
*
* @return a shallow copy of this map
*/
public Object clone() {
HashMap<K,V> result = null;
try {
result = (HashMap<K,V>)super.clone();
} catch (CloneNotSupportedException e) {
// assert false;
}
if (result.table != EMPTY_TABLE) {
result.inflateTable(Math.min(
(int) Math.min(
size * Math.min(1 / loadFactor, 4.0f),
// we have limits...
HashMap.MAXIMUM_CAPACITY),
table.length));
}
result.entrySet = null;
result.modCount = 0;
result.size = 0;
result.init();
result.putAllForCreate(this);
return result;
}