先来个原型设计模式的个人理解:属于创建型设计模式(单例模式Singleton、建造者模式Builder、工厂模式Factory、原型模式Prototype)的一种。其中单例、建造者、工厂模式后面学习。原型模式其实就是复制产生对象。和new出对象比起来。性能更好,所以用得也比较广泛。实现方式就是实现Cloneable接口。调用object的clone()方法,进行浅拷贝。下面是详细介绍:
在介绍这个原型模式是什么的时候,我们先看一段代码
package prototypeDesign;
public class Book implements Cloneable{
private String title;
private Author author;
private int pageNum;
public Book clone() throws CloneNotSupportedException{
return (Book)super.clone();//这句话是重点;
}
}
上面这段代码Book类实现了Cloneable接口。Book类的对象可以调用这个clone()方法生成一个新的的对象。让我想到在很多地方需要生成类实例的时候,我们可以不用new的形式来创建对象。因为上文说过,拷贝比new性能更好。我们再需要一个调用入口:
public class TestUtils {
public static void main(String[] args) throws Exception {
//浅复制
Book book = new Book();
book.setPageNum(10);
book.setTitle("原型模式");
Author author = new Author();
author.setAge(25);
book.setAuthor(author);
Book book1 = book.clone();
}
}
就这样,根据book对象,clone一下,就生成了book1了。这就是我理解的原型模式最简单的实现。当然我们可以把book实例私有化一下,放到工具类里面,再写一个静态方法,就可以实现随用随调了。比如:
package prototypeDesign;
public class TargetUtils {
private static Book book = new Book();
public static Book initBook() throws CloneNotSupportedException{
return book.clone();
}
}
public class Test {
public static void main(String[] args) throws Exception {
Book initBook = TargetUtils.initBook();
Book initBook2 = TargetUtils.initBook();
Book initBook3 = TargetUtils.initBook();
}
}
你看,稍微包装一下就可以用工具类的方式来创造一个Book实例了。虽然这样写代码要多一点,但是一个对象多次被new的话,用这种方式,可以提高性能,对象越复杂,差异就越明显。
现在我们再来总结一下,何为原型模式。即我们再创建一个对象的时候需要借助一个原型,而上例中book就是这个原型,当然要想clone出新实例,必须还要实现Cloneable接口。所以这里又有一个知识点需要我们去扩展,那就是Cloneable接口,需要我们去了解。
于是乎我在学习的过程中有去看了一下Cloneable接口,个人理解,这哥们就是一个标识,就像Serializable接口一样。按理说所有类都继承的Object里面就有一个clone()方法。但如果不实现一下Cloneable接口,调用clone()会抛出CloneNotSupportedException异常。没这哥们成不了事。
接下来我们来说说这个clone()方法稍微深一点的东西:关于深拷贝和浅拷贝。先把定义放这:
浅拷贝: 对简单类型的成员变量进行值的复制,对引用类型的成员变量只复制引用,不复制引用的对象.
深拷贝: 对简单类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制.
来来来,上代码加以解释:
package prototypeDesign;
public class Book implements Cloneable{
private String title;
private Author author;
private int pageNum;
public Book clone() throws CloneNotSupportedException{
return (Book)super.clone();//这句话是重点;
}
}
把最前面的代码再搬到这里来。这就是浅拷贝 ,Book中的pageNum是简单类型的成员变量。在克隆时对值进行了复制,Author是引用类型的成员变量,把引用地址复制给了新的实例。也就是说新生成的Book实例和原型实例的author属性所引用的是同一个内存空间。这就是浅拷贝。不知道你理解了吗?
相反深拷贝就是把Author的实例也一样拷贝一份。让新生成实例的author属性和原型实例的author属性指向不同的内存空间。通常我们clone()都是浅拷贝。如果需要实现深拷贝,我们可以对引用类型的属性再拷贝一份,如果遇到里面还有引用类型的,就需要再浅拷贝一次,然后通过set方法装载进去。还有一种就是通过序列化,再反序列化实现。
针对深拷贝,上点代码说明一下
package prototypeDesign;
public class Author implements Cloneable{
private int age;
public Author clone() throws CloneNotSupportedException{
return (Author)super.clone();
}
//省略了set和get方法
}
package prototypeDesign;
public class TargetUtils {
private static Book book = new Book();
public static Book initBook() throws CloneNotSupportedException{
Book book1 = book.clone();
book1.setAuthor(book.getAuthor().clone());
return book1;
}
}
看看上面的两段代码,把Author的实例也浅拷贝了一下。然后装载如Book新实例中。当然如果Author也有引用类型的属性,那也需要像这样clone来装载进去。
再上一段通过序列化反序列化实现深拷贝的代码
public DeepBook DeepClone() throws IOException, ClassNotFoundException{
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(this);
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
ObjectInputStream ois = new ObjectInputStream(is);
return (DeepBook)ois.readObject();
}
这种方式一样也实现了深拷贝的目的。
接下来就要开始记录再深一点东西了。
调用Object的clone()方法。是没有调用构造方法的。打开Object类可以看到
protected native Object clone() throws CloneNotSupportedException;
这是调用了c语音来实现的。是由虚拟机直接复制的内存块。所以速度比new出对象的方式要快很多。
同时又因为没有调用构造函数,所以不受权限的控制。
这个原型原型设计模式比较简单,就不特意去找源码了。这是我写的第一篇文章,很多地方写得不好,还请手下留情,有好的建议或者一些问题可以在下面留言。一起学习进步!