目标
掌握原型模式的应用场景以及常用写法
原型模式定义
原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
调用者不需要知道任何创建细节,不用调用构造函数。
属于创建型模式。
原型模式适用场景
1,类初始化消耗资源较多
2,new产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)
3,构造函数比较复杂
4,循环体中生产大量对象时
原型模式的创建方式
第一种:简单创建方式
首先创建一个Prototype.java类,代码如下:
package com.packer.partten.factory.prototype;
/**
* Created by lijianfang on 2021/9/25.
*/
public interface Prototype {
public Prototype clone();
}
再新建一个这个类的实现类ConcreatePrototypeA.java,代码如下:
package com.packer.partten.factory.prototype;
import java.util.List;
/**
* Created by lijianfang on 2021/9/25.
*/
public class ConcreatePrototypeA implements Prototype {
private int age;
private String name;
private List<String> hobbies;
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 List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
public Prototype clone() {
ConcreatePrototypeA ca = new ConcreatePrototypeA();
ca.setAge(this.age);
ca.setName(this.name);
ca.setHobbies(this.hobbies);
return ca;
}
}
最后创建测试类PrototypeTest.java
package com.packer.partten.factory.prototype;
import java.util.ArrayList;
import java.util.List;
/**
* Created by lijianfang on 2021/9/25.
*/
public class PrototypeTest {
public static void main(String[] args) {
ConcreatePrototypeA ca = new ConcreatePrototypeA();
ca.setAge(18);
ca.setName("Cat");
List<String> hobbies = new ArrayList<String>();
ca.setHobbies(hobbies);
ConcreatePrototypeA copy = (ConcreatePrototypeA) ca.clone();
System.out.println("原始对象中引用类型地址值=="+ca.getHobbies());
System.out.println("克隆对象中引用类型地址值=="+copy.getHobbies());
System.out.println("比较克隆与原始对象中hobbies的物理地址=" + (ca.getHobbies() == copy.getHobbies()));
}
}
测试结果如下:
原始对象中引用类型地址值==[]
克隆对象中引用类型地址值==[]
比较克隆与原始对象中hobbies的物理地址=true
Process finished with exit code 0
说明我们这种克隆方式只是复制了引用对象的物理地址的值是相等的,说明copy的是地址,不是值。这样修改了原始对象的值,copy的对象的值也会跟着修改,这样会有问题,同时说明这种克隆方式是一种浅克隆。
下面介绍另外一种方式:
第二中克隆方式:深克隆
我们使用齐天大圣来举例子
新建一个mokey.java类
package com.packer.partten.factory.prototype.deep;
import java.util.Date;
/**
* Created by lijianfang on 2021/9/25.
*/
public class Monkey {
/**
* 身高
*/
protected int height;
/**
* 体重
*/
protected int weight;
/**
* 生日
*/
protected Date bithday;
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public Date getBithday() {
return bithday;
}
public void setBithday(Date bithday) {
this.bithday = bithday;
}
}
再新建一个如意金箍棒的类GoldRingedStaff.java
package com.packer.partten.factory.prototype.deep;
import java.io.Serializable;
/**
* Created by lijianfang on 2021/9/25.
*/
public class GoldRingedStaff implements Serializable{
/**
* 长度
*/
private float height = 100;
/**
* 直径
*/
private float diameter = 10;
/**
* 变长
*/
public void growLong(){
this.diameter*= 2;
this.height*=2;
}
/**
* 变短
*/
public void shrinkShort(){
this.diameter/= 2;
this.height/=2;
}
}
接下来是重点,新建一个大圣类QiTianDaSheng.java,大圣有金箍棒,还有分身的能力。
package com.packer.partten.factory.prototype.deep;
import java.io.*;
import java.util.Date;
/**
* Created by lijianfang on 2021/9/25.
*/
public class QiTianDaSheng extends Monkey implements Cloneable,Serializable {
/**
* 孙猴子的金箍棒
*/
private GoldRingedStaff staff;
public GoldRingedStaff getStaff() {
return staff;
}
public void setStaff(GoldRingedStaff staff) {
this.staff = staff;
}
/**
* 构造孙猴子的方法
*/
public QiTianDaSheng(){
this.staff = new GoldRingedStaff();
this.bithday = new Date();
this.height = 150;
this.weight = 45;
System.out.println("孙猴子来了-------------");
}
/**
* 克隆一个孙猴子
* @return
*/
public Object clone() throws CloneNotSupportedException {
return this.deepClone();
}
/**
* 深度克隆
* @return
* @throws CloneNotSupportedException
*/
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);
QiTianDaSheng copyDaSheng = (QiTianDaSheng) ois.readObject();
copyDaSheng.setBithday(new Date());
return copyDaSheng;
}catch (Exception e){
System.out.println("克隆孙猴子失败!"+e);
return null;
}finally {
try{
bos.close();
bis.close();
oos.close();
ois.close();
}catch (Exception e){
System.out.println("克隆孙猴子失败!"+e);
}
}
}
/**
* 比较克隆跟原始的结果
*/
public void change() throws CloneNotSupportedException {
QiTianDaSheng copyDaSheng = (QiTianDaSheng)clone();
System.out.println("原始大圣的生日"+this.getBithday().getTime());
System.out.println("克隆大圣的生日"+copyDaSheng.getBithday().getTime());
System.out.println("克隆的跟原始是都一个对象"+(copyDaSheng==this));
System.out.println("原始大圣跟克隆的金箍棒是否为一个对象"+(copyDaSheng.getStaff()==this.getStaff()));
}
}
最后新建一个测试类deepTest.java
package com.packer.partten.factory.prototype.deep;
/**
* Created by lijianfang on 2021/9/25.
*/
public class deepTest {
public static void main(String[] args) throws CloneNotSupportedException {
QiTianDaSheng dasheng = new QiTianDaSheng();
dasheng.change();
}
}
跑一下测试结果:说明我克隆的大圣跟原来的不是一个大圣,同时两个大圣使用的金箍棒也不是一个。也就是说金箍棒的引用物理地址不是一个。
孙猴子来了-------------
原始大圣的生日1632558468496
克隆大圣的生日1632558468579
克隆的跟原始是都一个对象false
原始大圣跟克隆的金箍棒是否为一个对象false
Process finished with exit code 0