原型模式--- prototype
Posted 高高for 循环
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原型模式--- prototype相关的知识,希望对你有一定的参考价值。
原型模式
定义;
原型模式属于对象创建模式,GOF 给它的定义为:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
Java中的原型模式:
在 Java 中提供了clone()方法来实现对象的克隆,所以Prototype 模式实现变得简单许 多。
实现条件:
- implements Cloneable接口
- 重写clone()方法
组成:
分类:
原型模式分为
- 浅克隆
- 深克隆
案例 1 :浅克隆
原型:Person
实现条件:
1. implements Cloneable接口
2. 重写clone()方法
class Person implements Cloneable {
int age = 8;
int score = 100;
Location loc = new Location("北京", 22);
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Location {
String street;
int roomNo;
@Override
public String toString() {
return "Location{" +
"street='" + street + '\\'' +
", roomNo=" + roomNo +
'}';
}
public Location(String street, int roomNo) {
this.street = street;
this.roomNo = roomNo;
}
}
测试
public class Test01 {
public static void main(String[] args) throws Exception {
Person p1 = new Person();
Person p2 = (Person)p1.clone();
System.out.println(p2.age + " " + p2.score);
System.out.println(p2.loc);
System.out.println("---------------------------------");
System.out.println("p1==p2: "+ (p1 == p2));
System.out.println("p1.loc==p2.loc: "+(p1.loc == p2.loc));
p1.loc.street = "上海";
System.out.println("p1.loc: "+p1.loc);
System.out.println("p2.loc: "+p2.loc);
}
}
分析:
- 可以看出 p2是有p1克隆过去的,且p1和p2.地址值不同,但成员属性相同
- p1和p2的成员属性loc相同,都是指向堆中同一地址.即 p1.loc==p2.loc为true
- 所以当p1改变属性loc时,p2的属性loc也随之改变,这就是浅克隆
如果想克隆出来的p2不随p1改变而改变 ,则需要进行深克隆
案例 2 :深克隆
原型:Person
条件 :
Person 和Person的属性Location(引用数据类型)
都需要实现Cloneable接口和重写clone()方法
class Person implements Cloneable {
int age = 8;
int score = 100;
Location loc = new Location("北京", 22);
@Override
public Object clone() throws CloneNotSupportedException {
Person p = (Person)super.clone();
p.loc = (Location)loc.clone();
return p;
}
}
class Location implements Cloneable {
String street;
int roomNo;
public Location(String street, int roomNo) {
this.street = street;
this.roomNo = roomNo;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Location{" +
"street='" + street + '\\'' +
", roomNo=" + roomNo +
'}';
}
}
测试:
public class Test01 {
public static void main(String[] args) throws Exception {
Person p1 = new Person();
Person p2 = (Person)p1.clone();
System.out.println(p2.age + " " + p2.score);
System.out.println(p2.loc);
System.out.println("---------------------------------");
System.out.println("p1==p2: "+ (p1 == p2));
System.out.println("p1.loc==p2.loc: "+(p1.loc == p2.loc));
p1.loc.street = "上海";
System.out.println("p1.loc: "+p1.loc);
System.out.println("p2.loc: "+p2.loc);
}
}
分析: 可以看出p1改变自己的属性loc 时,p2并不会随之变化
案例 3 :关于string的克隆
情况1 ,如果是以字面量的方式定义的string
不需要再对string做克隆处理,就是深克隆模式
- 因为通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
package prototype;
public class Test02 {
public static void main(String[] args) throws Exception {
Person p1 = new Person();
Person p2 = (Person) p1.clone();
System.out.println("p1.loc == p2.loc? " + (p1.loc == p2.loc));
System.out.println("p1.loc: " + p1.loc);
System.out.println("p2.loc: " + p2.loc);
System.out.println("------------------p1.loc.street 赋值为深圳------------------------------");
p1.loc.street="深圳";
System.out.println("p1.loc: " + p1.loc);
System.out.println("p2.loc: " + p2.loc);
}
}
class Person implements Cloneable {
int age = 8;
int score = 100;
Location loc = new Location("上海", 22);
@Override
public Object clone() throws CloneNotSupportedException {
Person p = (Person)super.clone();
p.loc = (Location)loc.clone();
return p;
}
}
class Location implements Cloneable {
String street;
int roomNo;
public Location(String street, int roomNo) {
this.street = street;
this.roomNo = roomNo;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Location{" +
"street='" + street + '\\'' +
", roomNo=" + roomNo +
'}';
}
}
- 此时p2不会随着p1的变动而改变,属于深克隆
情况2 ,如果是以new string定义 或者 StringBuilder
不对string做克隆处理的话,则克隆出来的结果是浅克隆
class Person implements Cloneable {
int age = 8;
int score = 100;
Location loc = new Location(new StringBuilder("上海"), 22);
@Override
public Object clone() throws CloneNotSupportedException {
Person p = (Person)super.clone();
p.loc = (Location)loc.clone();
return p;
}
}
class Location implements Cloneable {
StringBuilder street;
int roomNo;
public Location(StringBuilder street, int roomNo) {
this.street = street;
this.roomNo = roomNo;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Location{" +
"street='" + street + '\\'' +
", roomNo=" + roomNo +
'}';
}
}
测试 :
此时 没对StringBuilder再做克隆处理
public class Test02 {
public static void main(String[] args) throws Exception {
Person p1 = new Person();
Person p2 = (Person) p1.clone();
System.out.println("p1.loc == p2.loc? " + (p1.loc == p2.loc));
System.out.println("p1.loc: " + p1.loc);
System.out.println("p2.loc: " + p2.loc);
System.out.println("------------------p1.loc.street 字符串反转------------------------------");
p1.loc.street.reverse();
System.out.println("p1.loc: " + p1.loc);
System.out.println("p2.loc: " + p2.loc);
}
}
分析:
- 此时p2会随着p1的变动而改变,属于浅克隆
- 因为只是,StringBuilder是new出来的,不是字面量的方式定义,会在堆中开辟一块属于自己的空间
案例 4 : 序列化写法深克隆
浅复制:
- 将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
深复制:
- 将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。
public class Prototype implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
private String string;
private SerializableObject obj;
/* 浅复制 */
public Object clone() throws CloneNotSupportedException {
Prototype proto = (Prototype) super.clone();
return proto;
}
/* 深复制 */
public Object deepClone() throws IOException, ClassNotFoundException {
/* 写入当前对象的二进制流 */
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
/* 读出二进制流产生的新对象 */
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
public SerializableObject getObj() {
return obj;
}
public void setObj(SerializableObject obj) {
this.obj = obj;
}
}
class SerializableObject implements Serializable {
private static final long serialVersionUID = 1L;
}
- 方式二:通过对象的序列化实现
//方式二:通过对象的序列化实现
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);
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
}
}
}
要实现深复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象。
原型模式在源码中的应用
ArrayList:
- 实现Cloneable接口
- 重写clone()方法
总结:
适用场景:
- 类初始化消耗资源较多时
- 初始化一个对象时需要非常繁琐的过程时
- 构造函数比较复杂时
- 循环体中生产大量对象时,可读性下降时
优点:
- 当创建一个对象比较复杂时,使用原型对象通常效率会更高
- 还有一个重要的用途就是保护性拷贝,也就是对某个对象对外可能是只读的,为了防止外部对这个只读对象的修改,通常可以通过返回一个对象拷贝的形式实现只读的限制。
缺点:
- 需要为每个对象配备克隆或者可拷贝的方法
- 对克隆复杂对象或者对克隆出来的对象进行复杂改造时,易带来风险,深克隆、浅克隆要运用得当
- 通过实现Cloneable接口的原型模式在调用clone函数构造实例时并不一定比通过new操作速度快,只有当通过new构造对象较为耗时或者说成本较高时,通过clone方法才能够获得效率上的提升
注意;
- 原型模式到底应该采用浅克隆还是深克隆?这个应根据实际业务场景具体分析
- 构造函数不会被执行,需格外注意。
- 由于 clone 方法在java 实现中有着一定的弊端和风险,所以clone 方法是不建议使用的。
因此很少能在java 应用中看到原型模式的使用
以上是关于原型模式--- prototype的主要内容,如果未能解决你的问题,请参考以下文章