原型模式,可以理解为克隆模式,就是对一个已创建的对象(原型)进行克隆。原型对象必须提供克隆的方法。
我们先看一个简单的例子:
//实现Cloneable ,重写clone方法,使用对象提供克隆的能力
public class Article implements Cloneable {
public Article(){
System.out.println("初始化");
}
public Article clone() {
System.out.println("clone ...");
Article clone = null;
try {
clone = (Article)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
//使用
Article article = new Article();
article.clone();
//打印结果
初始化
clone ...
上面例子可以知道,克隆并不会调用构造函数。实际上这是克隆与new不同的地方,因此当初始化很耗时的时候或要占用过多资源的时候,可以在已有对象上使用克隆。
那除了初始化的原因,我们知道很多对象在new之前需要做很多准备工作,比如需要读取配置文件、查询数据库、访问redis、授权才可以使用等,这些工作重复且耗时,因此这时用克隆更方便且快速。
什么时候使用克隆,总结起来就是两点:
- new初始化很耗时的时候或要占用过多资源
- new之前需要做很多准备工作,比如需要读取配置文件、查询数据库、访问redis、授权才可以使用等
知道何时使用,再来看克隆两个很重要的方式:浅拷贝和深拷贝。
先看浅拷贝例子:
//实现Cloneable,调用Object的clone方法实现浅拷贝
public class Article implements Cloneable {
private String title;
private String content;
//引用其他文章
private Article quotedArticle;
public Article(String title,String content,Article quotedArticle){
this(title,content);
this.quotedArticle = quotedArticle;
}
public Article(String title,String content){
this.title = title;
this.content = content;
}
public Article clone() {
System.out.println("clone ...");
Article clone = null;
try {
clone = (Article)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
public void print(){
System.out.println(title + ":" + content);
}
public Article getQuotedArticle() {
return quotedArticle;
}
public void setContent(String content) {
this.content = content;
}
}
public class Demo {
public static void main(String[] args) {
Article article = new Article("关于原型模式例子","关于原型模式的内容", new Article("rel","被引用文章"));
article.print();
article.getQuotedArticle().print();
Article clone = article.clone();//克隆
System.out.println("----------克隆结果与原型一样----------");
clone.print();//结果与原型一样
clone.getQuotedArticle().print();//结果与原型一样
System.out.println("----------修改克隆----------");
clone.setContent("修改内容");//修改克隆的内容
clone.getQuotedArticle().setContent("修改引用文章");//修改克隆引用文章的内容
article.print(); //修改克隆的内容,原型的内容没有变化
article.getQuotedArticle().print(); //注意:修改克隆引用文章的内容,原型引用文章的内容也跟着变化
}
}
//运行结果
关于原型模式例子:关于原型模式的内容
rel:被引用文章
clone ...
----------克隆结果与原型一样----------
关于原型模式例子:关于原型模式的内容
rel:被引用文章
----------修改克隆----------
关于原型模式例子:关于原型模式的内容
rel:修改引用文章
上面例子可以看出,克隆的对象并没有克隆引用的文章对象,只是克隆引用的文章对象的引用。这就是所谓的浅拷贝,只会拷贝String型、基本数据类型及引用对象引用,并不会拷贝引用对象。
如果要进行深拷贝,直接Object的clone方法实现,可以重写clone方法用对象的序列化和反序列化的方法实现深拷贝。
//实现Serializable 接口,对象才可以被序列化
public class Article implements Serializable {
private String title;
private String content;
//引用其他文章
private Article quotedArticle;
public Article(String title,String content,Article quotedArticle){
this(title,content);
this.quotedArticle = quotedArticle;
}
public Article(String title,String content){
this.title = title;
this.content = content;
}
public Article clone() {
Article clone = null;
ByteArrayOutputStream bo = null;
ObjectOutputStream oo = null;
ByteArrayInputStream bi = null;
ObjectInputStream oi = null;
try {
//先序列化
bo = new ByteArrayOutputStream();
oo = new ObjectOutputStream(bo);
oo.writeObject(this);
//再反序列化
bi = new ByteArrayInputStream(bo.toByteArray());
oi = new ObjectInputStream(bi);
clone = (Article)oi.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
closeStream(bo);
closeStream(oo);
closeStream(bi);
closeStream(oi);
}
return clone;
}
private void closeStream(Closeable stream){
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void print(){
System.out.println(title + ":" + content);
}
public Article getQuotedArticle() {
return quotedArticle;
}
public void setContent(String content) {
this.content = content;
}
}
public class Demo {
public static void main(String[] args) {
Article article = new Article("关于原型模式例子","关于原型模式的内容", new Article("rel","被引用文章"));
article.print();
article.getQuotedArticle().print();
Article clone = article.clone();
System.out.println("----------克隆结果与原型一样----------");
clone.print();//结果与原型一样
clone.getQuotedArticle().print();//结果与原型一样
System.out.println("----------修改克隆----------");
clone.setContent("修改内容");//修改克隆的内容
clone.getQuotedArticle().setContent("修改引用文章");//修改克隆引用文章的内容
article.print(); //修改克隆的内容,原型的内容没有变化
article.getQuotedArticle().print(); //注意:修改克隆引用文章的内容,原型引用文章的内容不跟着变化,引用的文章也被克隆了
}
}
//运行结果
关于原型模式例子:关于原型模式的内容
rel:被引用文章
----------克隆结果与原型一样----------
关于原型模式例子:关于原型模式的内容
rel:被引用文章
----------修改克隆----------
关于原型模式例子:关于原型模式的内容
rel:被引用文章
原型模式的目的,除了创建对像的方便,最重要的是提升创建对象性能。