原型模式概念
原型模式是指通过复制一个已经存在的实例来返回新的实例,而不是新建实例,被复制的实例就是我们所称的原型对象。调用者不需要知道任何创建细节,既然是拷贝实例获取新的实例,因此不会直接调用构造方法。
原型模式应用场景
初始化消耗资源较多
实例化一个对象需要繁琐的过程
构造函数比较复杂
循环体中产生大量对象时
Java中的浅克隆
下面介绍浅克隆,通过调用clone()方法实现复制要给对象的效果
- 编写一个顶层接口,里面定义一个克隆的方法
public interface Prototype {
//定义一个克隆的方法
Prototype clone();
}
- 编写一个Pets,作为宠物类,需要在Person类中作为引用对象被声明
public class Pets {
//定义属性,记录宠物的属性
private String kind;
public String getKind() {
return kind;
}
public void setKind(String kind) {
this.kind = kind;
}
}
- 编写一个实现顶层接口的实现类Person
public class Person implements Prototype{
//定义属性
private int age;
private String name;
//定义一个引用类型的对象,这里使用之前定义的宠物类
private Pets pets;
//提供get和set方法
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Pets getPets() {
return pets;
}
public void setPets(Pets pets) {
this.pets = pets;
}
//重写克隆的方法
@Override
public Prototype clone() {
Person person = new Person();
person.setAge(this.age);
person.setName(this.name);
person.setPets(this.pets);
return person;
}
}
- 模拟一个应用程序,假设应用程序需要使用到根据原型对象克隆而来的克隆对象。
public class Application {
//这里是在模拟应用程序在测试类中调用克隆对象的方法
public Prototype getPrototype(Prototype prototype){
//调用接口中定义的克隆方法
return prototype.clone();
}
}
- 测试程序
public class Test {
public static void main(String[] args) {
//创建Person对象,作为原型对象
Person p1 = new Person();
p1.setAge(18);
p1.setName("张三");
//定义一个宠物类对象
Pets pets = new Pets();
pets.setKind("dog");
p1.setPets(pets);
//获取克隆对象
Application app = new Application();
Person p2 = (Person)app.getPrototype(p1);
//将原型对象p1中的地址和克隆对象p2中的地址做比较
System.out.println("原型对象p1中pets的地址:"+p1.getPets());
System.out.println("克隆对象p2中pets的地址:"+p2.getPets());
System.out.println("两者之间地址的比较:"+(p1.getPets()==p2.getPets()));
}
}
- 测试程序运行后的结果
-
结论:根据上面测试结果可以得出,这种方式获取的克隆对象中pets对象引用的地址是没有改变的,克隆对象只是使用了原型对象中引用对象的地址,并没有创建引用对象的地址。
-
通过克隆对象修改属性值,再次验证上述结论
1)验证测试代码如下:
public class Test {
public static void main(String[] args) {
//创建Person对象,作为原型对象
Person p1 = new Person();
p1.setAge(18);
p1.setName("张三");
//定义一个宠物类对象
Pets pets = new Pets();
pets.setKind("dog");
p1.setPets(pets);
//获取克隆对象
Application app = new Application();
Person p2 = (Person)app.getPrototype(p1);
//首先输出原型对象和克隆对象中pets引用中的kind的值
System.out.println("原型对象:"+p1.getPets().getKind());
System.out.println("克隆对象:"+p2.getPets().getKind());
//通过克隆对象修改pets引用中的kind的值
p2.getPets().setKind("cat");
//再次输出原型对象和克隆对象中pets引用中的kind的值
System.out.println("原型对象:"+p1.getPets().getKind());
System.out.println("克隆对象:"+p2.getPets().getKind());
}
}
2)测试结果
- 根据第二次测试结果可以得出,如果有引用对象,那么克隆对象是直接使用原型对象中的引用地址,并不会重新创建新的引用地址,因此,通过克隆对象修改了引用地址中的值之后,原型对象中的属性值也随之修改。
Java中的深克隆
- 使用clone函数嵌套的方式比较简单,这里就不再使用,下面使用序列化的方式实现深克隆
- 还是以上面浅克隆的事例代码演示
1)顶层接口设计
public interface Prototype {
//定义一个克隆的方法
Prototype clone();
}
2)定义一个宠物类,用于后续的Person中用于声明引用对象
public class Pets implements Serializable {
//定义一个宠物种类属性
private String kind;
public String getKind() {
return kind;
}
public void setKind(String kind) {
this.kind = kind;
}
}
3)定义Person类,需要该类实现序列化接口
public class Person implements Serializable,Prototype {
private String name;
private int age;
//声明一个宠物类对象,引用类型
private Pets pets;
//get和set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Pets getPets() {
return pets;
}
public void setPets(Pets pets) {
this.pets = pets;
}
//重写Prototype接口中的clone方法,并用序列化的方式实现
@Override
public Prototype clone() {
//创建一个Person对象
Person person = new Person();
person.setAge(this.age);
person.setName(this.name);
person.setPets(this.pets);
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
//序列化
oos.writeObject(person);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
//反序列化
return (Person)ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
4)创建一个应用程序,用于模拟其他应用程序需要Person对象,使用克隆的方式获取
public class Application {
//这里是在模拟应用程序在测试类中调用克隆对象的方法
public Prototype getPrototype(Prototype prototype){
//调用接口中定义的克隆方法
return prototype.clone();
}
}
5)测试代码
public class Test {
public static void main(String[] args) {
//创建Person对象,作为原型对象
Person p1 = new Person();
p1.setName("张三");
p1.setAge(11);
Pets pets = new Pets();
pets.setKind("dog");
p1.setPets(pets);
//假设application应用程序需要用到Person类,用克隆的方式获取
Application app = new Application();
Person p2 = (Person)app.getPrototype(p1);
//获取原型对象p1和克隆对象p2中引用对象pets的地址
System.out.println("原型对象:"+p1.getPets());
System.out.println("克隆对象:"+p2.getPets());
//输出原型对象p1和克隆对象p2中pets引用对象的kind属性
System.out.println("原型对象:"+p1.getPets().getKind());
System.out.println("克隆对象:"+p2.getPets().getKind());
//使用克隆对象修改pets的属性值
p2.getPets().setKind("cat");
System.out.println("原型对象:"+p1.getPets().getKind());
System.out.println("克隆对象:"+p2.getPets().getKind());
}
}
- 测试结果图
原型模型的总结
- 原型模型总结
虽然上面都是在讲解Java中的深克隆和浅克隆,原型模型的概念是由一个对象作为原型对象,由该对象复制出一个新的对象,称为克隆对象,这种方式就是原型模式,因此克隆是实现原型模式的方式,但是原型模式和单例模式是存在冲突的。