1.什么是原型模式?
原型模式提供了一种创建对象的模式,它是指用原型实例创建对象的种类,并且通过拷贝这些原型,创建新的对象。用一个很生动形象的例子:孙悟空拔出一根猴毛,变出其他和自己一模一样的小孙悟空,在这里,原型实例就是孙悟空,拔出的猴毛通过拷贝孙悟空的外表特征,变成了其他小孙悟空。
如何实现?
在java中通过实现 实现 Cloneable 接口,并且重写clone方法,在创建新对象时候,调用原型实例.clone()
2.简单的代码实现
克隆羊多莉大家一定听说过吧,那么就用多利羊来说明这个原型模式吧
首先创建出一个羊的实例对象,去实现Cloneable 接口
public class Sheep implements Cloneable{
private String name;
private int age;
public Sheep(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone(){
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
return clone;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试一下原型模式创建对象
public class Test {
public static void main(String[] args){
Sheep sheep = new Sheep("Dolly", 1);
Sheep duoli1 = (Sheep) sheep.clone();
Sheep duoli2 = (Sheep) sheep.clone();
Sheep duoli3 = (Sheep) sheep.clone();
System.out.println("duoli1:"+duoli1+"hashcode:"+duoli1.hashCode());
System.out.println("duoli2:"+duoli2+"hashcode:"+duoli2.hashCode());
System.out.println("duoli3:"+duoli3+"hashcode:"+duoli3.hashCode());
}
}
3浅拷贝VS深拷贝
3.1浅拷贝
基本数据类型的成员变量:将属性值复制一份给新的对象,是值传递
引用数据变量的成员变量:将该成员变量的地址值复制一份给新的对象,是地址传递,也就是说,不管在哪个对象中修改这个成员变量,都会影响到另一个对象中该成员变量的值。
浅拷贝默认的实现就是.clone()方法
下面就来进行一个测试,在Sheep类中新增一个person类,代表羊的主人
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
在Sheep类中增加上这个person
测试
public class Test {
public static void main(String[] args){
Person master = new Person("多莉的主人");
Sheep sheep = new Sheep("Dolly", 1,master);
Sheep duoli1 = (Sheep) sheep.clone();
Sheep duoli2 = (Sheep) sheep.clone();
Sheep duoli3 = (Sheep) sheep.clone();
System.out.println("duoli1:"+duoli1+"hashcode:"+duoli1.hashCode()+"master:"+duoli1.getPerson().hashCode());
System.out.println("duoli2:"+duoli2+"hashcode:"+duoli2.hashCode()+"master:"+duoli2.getPerson().hashCode());
System.out.println("duoli3:"+duoli3+"hashcode:"+duoli3.hashCode()+"master:"+duoli2.getPerson().hashCode());
}
}
3.2深拷贝
基本数据类型的成员变量:值传递
引用数据类型的成员变量:对整个对象进行拷贝,包括对象的引用类型,不再是拷贝地址了
clone()默认是浅拷贝,那么如何实现深拷贝呢?
方法一:重写clone方法
方法二:通过对象序列化实现
方式一实现起来非常简答,只需要Person也去实现Cloneable接口,并且重写clone方法,然后在修改Sheep的clone方法即可
Person的clone方法
@Override
protected Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
return clone;
}
重写Sheep的clone方法
@Override
protected Object clone(){
Sheep clone = null;
try {
//转换为Sheep
clone = (Sheep) super.clone();
//person也克隆过来
clone.setPerson((Person) person.clone());
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
return clone;
}
重新运行测试
方式二:通过序列化实现
①:实现序列化接口
Sheep和Person都要,然后不用再实现Cloneable接口了
public class Sheep implements Serializable
②:自定义深拷贝方法
public Object 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);
Sheep sheep = (Sheep) ois.readObject();
return sheep;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
//关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
System.out.println(e2.getMessage());
}
}
}
测试:
public class Test {
public static void main(String[] args){
Person master = new Person("多莉的主人");
Sheep sheep = new Sheep("Dolly", 1,master);
Sheep duoli1 = (Sheep) sheep.deepClone();
Sheep duoli2 = (Sheep) sheep.deepClone();
Sheep duoli3 = (Sheep) sheep.deepClone();
System.out.println("duoli1:"+duoli1+"hashcode:"+duoli1.hashCode()+"master:"+duoli1.getPerson().hashCode());
System.out.println("duoli2:"+duoli2+"hashcode:"+duoli2.hashCode()+"master:"+duoli2.getPerson().hashCode());
System.out.println("duoli3:"+duoli3+"hashcode:"+duoli3.hashCode()+"master:"+duoli3.getPerson().hashCode());
}
}
4.原型模式的优缺点
- 优点:
- 如果创建对象的过程十分复杂,使用原型模式可以提高效率
- 原始对象发生变化,克隆对象也会变化,不用手动修改代码
- 可以动态地获取对象的状态
- 缺点
- 深拷贝代码比较复杂
- 需要对每一个类准备一个克隆方法,如果是创建的新类,问题不大,但是如果是要修改已有的类,就涉及到修改源代码,违背了ocp原则(需求变化时,尽量通过扩展实体而不是修改源代码来实现)
5.原型模式在java中的应用
spring中的:@Scope(“prototype”)注解,默认值是singleton,也就是单例,但是当值为prototype就意味着对象变成了多例,为每一个线程都提供了一个对象。