原型模式介绍
原型模式是一种创建型的设计模式,原型模式是指复制现有的实例来创建新的实例,不需要知道实例的创建细节。(在Java中意味使用clone方法或者序列化和反序列化)。
当创建给定类的实例的过程很昂贵或很复杂时,就需要使用原型模式。
原型模式的原理UML类图,如下:
原理结构图说明:
- ProtoType:原型类,实现Cloneable接口,声明克隆自己的
抽象方法。 - ConcreteProtoType:具体的原型类,实现克隆自己的方法。
- Client:其中使用ProtoType,让一个原型对象克隆自己,从而创建一个新的对象。
clone()方法说明:
Object中clone()方法是protected的,是浅拷贝,要使用clone()方法,要重写它,只有实现了Cloneable接口才可以调用该方法,否则会抛出CloneNotSupportedException异常。
clone()方法在实现上是在内存上直接复制二进制流,然后重新分配内存空间,比new出来的效率要高,clone的过程不会调用构造函数,也就是说通过这一方法创造出来的对象不会由构造函数的处理。
clone()方法创建并返回此对象的一个副本。对于任何对象x,满足以下表达式:
- x.clone() != x为true
- x.clone().getClass() == x.getClass()为true
- x.clone().equals(x)一般情况下为true,但这并不是必须要满足的要求
浅拷贝介绍:
- 对于数据类型为基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。无论是浅拷贝还是深拷贝,拷贝出的新的对象指向新的地址空间。
- 对于数据类型为引用数据类型的成员变量,如数组,对象。那么浅拷贝将进行引用传递,也就是只是将成员变量的引用值(内存地址)复制一份给新的对象。在这种情况下,若成员变量是数组或对象,因为两个对象的成员变量指向同一实例,在一个对象中修改该成员变量会影响到另一个对象的成员变量。
实现原型模式示例
场景:现在有一只羊名为li,大小为45斤,颜色为白色。朋友为一个叫kai的羊,编程实现复制十只一模一样的羊。
下面创建一个羊的实体类,实现cloneable接口并重写clone()方法:
public class Sheep implements Cloneable {
private String name;
private String color;
private int size;
public Sheep friend;
public Sheep(String name, String color, int size) {
this.name = name;
this.color = color;
this.size = size;
}
@Override
public String toString() {
return "Sheep [name=" + name + ", color=" + color + ", size=" + size + "]";
}
@Override
protected Sheep clone() {
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return sheep;
}
}
在Client类中完成复制Sheep对象
这里很容易想到使用原型对象的属性来new一个对象,但是这种方法总是需要重新获取原始对象的属性,如果创建的对象复杂就会效率很低,另外如果原型对象有修改,可能会需要修改代码。
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("li", "white", 40);
sheep.friend = new Sheep("kai", "black", 45);
System.out.println(sheep.toString() + sheep.friend.hashCode());
Sheep copySheep = (Sheep) sheep.clone();
System.out.println("复制的羊:" + copySheep.toString() + copySheep.friend.hashCode());
}
}
运行程序查看结果:
可以看到两个对象属性friend的hashcode的值一致,即引用的对象是同一个,这说明上面使用的拷贝方式是浅拷贝。
深拷贝
1.基本介绍
深拷贝也就是复制对象的所有基本数据类型的成员变量,对于所有的引用类型的成员变量,深拷贝会为其申请存储空间,并会复制每个引用类型的成员变量所引用的对象。
也就是说对象进行深拷贝是要对整个对象进行拷贝。
深拷贝的两种实现方式:重写clone()方法和对象序列化。
2.深拷贝应用
1.方式1:重写clone()方法。
新建一个类DeepCloneableTarget 作为成员变量,该类的属性只有String类型,虽然String类型也属于引用类型,但因为String的不可变性(可以使用反射来修改String的值),这里使用Object类的clone即可。
public class DeepCloneableTarget implements Cloneable {
private String cloneName;
public DeepCloneableTarget(String cloneName) {
super();
this.cloneName = cloneName;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
类 DeepProtoType 将 DeepCloneableTarget类对象作为成员变量。
public class DeepProtoType implements Cloneable {
private String name;
public DeepCloneableTarget deepCloneableTarget;
public DeepProtoType(String name, DeepCloneableTarget deepCloneableTarget) {
super();
this.name = name;
this.deepCloneableTarget = deepCloneableTarget;
}
@Override
protected DeepProtoType clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
DeepProtoType deep = (DeepProtoType) super.clone();
// 对引用类型做单独处理,单独进行克隆
deep.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();
return deep;
}
}
Client类测试是否完成成员变量DeepCloneableTarget类对象的拷贝。
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
DeepCloneableTarget deepCloneableTarget = new DeepCloneableTarget("deppTarget");
DeepProtoType deepProtoType = new DeepProtoType("protoType", deepCloneableTarget);
DeepProtoType clone = deepProtoType.clone();
System.out.println(deepProtoType.deepCloneableTarget.hashCode());
System.out.println(clone.deepCloneableTarget.hashCode());
}
}
通过运行结果中的hashcode的值的不同判断两个作为成员变量的对象是不同的即完成了深拷贝。
2.方式2:通过对象的序列化实现
类DeepCloneableTarget改为实现Serializable接口
public class DeepCloneableTarget implements Serializable {
private static final long serialVersionUID = 1L;
private String cloneName;
public DeepCloneableTarget(String cloneName) {
super();
this.cloneName = cloneName;
}
}
类DeepProtoType也实现Serializable 接口,定义deepClone()方法使用序列化和反序列化实现深拷贝。
public class DeepProtoType implements Serializable {
private static final long serialVersionUID = 1L;
public String name;
public DeepCloneableTarget deepCloneableTarget;
public DeepProtoType(String name, DeepCloneableTarget deepCloneableTarget) {
super();
this.name = name;
this.deepCloneableTarget = deepCloneableTarget;
}
public DeepProtoType deepClone() {
// 创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
// 序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoType deepProtoType = (DeepProtoType) ois.readObject();
return deepProtoType;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return null;
} finally {
// 关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
// TODO: handle exception
e2.printStackTrace();
}
}
}
}
测试是否能完成深拷贝。
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
DeepCloneableTarget deepCloneableTarget = new DeepCloneableTarget("deppTarget");
DeepProtoType deepProtoType = new DeepProtoType("protoType", deepCloneableTarget);
DeepProtoType clone = deepProtoType.deepClone();
System.out.println(deepProtoType.name + " " + deepProtoType.deepCloneableTarget.hashCode());
System.out.println(deepProtoType.name + " " + clone.deepCloneableTarget.hashCode());
}
}
运行结果如下:
两个对象的成员变量中的DeepCloneableTarget 对象的hashcode不同,所以两个变量是不同的两个对象,这同样实现了深拷贝。
原型模式在Spring中的应用
在Spring 中我們可以定義Bean的作用域为prototype,下面使用Java配置类的方式来注册一个Bean。
@Configuration
public class SpringConfig {
@Bean(value = "person")
@Scope(value = "prototype")
// 或@Scope("ConfigurableBeanFactory.SCOPE_PROTOTYPE")
public Person getPerson() {
return new Person();
}
}
当使用getBean()方法获取容器中的这个Bean时,下面通过Debug来走一遍该Bean的实例化过程:
首先调用AbstractApplicationContext的getBean()方法
这里的getBeanFactory()是通过子类GenericApplicationContext 中的getBeanFactory()获取DefaultListableBeanFactory。
然后getBean()方法调用AbstractBeanFactory的getBean()
接着调用本类中的doGetBean()方法,最终在这里判断如果是prototype,就调用这个类的createBean()方法。
总结
下面总结一下原型模式:
- 适用场景:
- 当创建新的对象复杂或需要一个类的许多对象时,就可以利用原型模式简化对象的创建过程来提高效率。
- 类初始化需要消耗资源较多,就可以使用原型模式。
- 优点:
- 向客户隐藏创建新实例的复杂性
- 提供让客户能够产生未知类型对象的选项。即客户在不知道要实例化何种特定类的情况下,可以制造出新的实例。
- 不用重新初始化对象,而是动态地获得对象运行时的状态。
- 缺点:实现深克隆时可能需要复杂的代码。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/44326.html