在设计模式系列(一)单例模式 中详细介绍了单例设计模式,下面一起来看一下原型模式。
一、概述
原型模式是一种创建型设计模式,它通过复制一个已经存在的实例来返回新的实例,而不是新建实例.被复制的实例就是我们所称的原型,这个原型是可定制的。
原型模式多用于创建复杂的或者耗时的实例, 因为这种情况下,复制一个已经存在的实例可以使程序运行更高效,或者创建值相等,只是命名不一样的同类数据。二、深拷贝和浅拷贝
原型模式中的拷贝分为"浅拷贝"和"深拷贝":浅拷贝: 对值类型的成员变量进行值的复制,对引用类型的成员变量只复制引用,不复制引用的对象。
深拷贝: 对值类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制。
三、Java的clone()方法
在Object类中有clone方法
protected native Object clone() throws CloneNotSupportedException;
必须实现Cloneable接口,否则调用clone会返回null
①对任何的对象x,都有x.clone() !=x//克隆对象与原对象不是同一个对象
②对任何的对象x,都有x.clone().getClass()= =x.getClass()//克隆对象与原对象的类型一样
③如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。
⑵Java中对象的克隆
①为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。
②在派生类中覆盖基类的clone()方法,并声明为public。
③在派生类的clone()方法中,调用super.clone()。
④在派生类中实现Cloneable接口。
四、浅复制详解
预先定义好使用的类
package com.designpattern.bean;
/**
* 机器人实例
*
* @author chao
*
*/
public class Robot {
public String name;// 名字
public int age;// 寿命
public Battery battery;// 电池
@Override
public String toString() {
return " Robot hashCode:" + hashCode() + " name:" + name + " age:" + age
+ (battery == null ? "" : battery.toString());
}
/**
* 电池实例
*
* @author chao
*
*/
public static class Battery {
public String name;// 电池名称
public int level;// 电池电量 0-100
@Override
public String toString() {
return " Battery hashCode:" + hashCode() + " name:" + name + " level:" + level;
}
}
}
实现浅复制
package com.designpattern.prototype;
import com.designpattern.bean.Robot;
/**
* 浅克隆
*
* @author chao
*
*/
public class ShallowClone implements Cloneable {
public Robot robot;
public String name;
public int number;
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
System.out.println(e.getMessage());
}
return null;
}
@Override
public String toString() {
return " ShallowClone hashCode:" + hashCode() + " name:" + name + " number:" + number
+ (robot == null ? "" : robot.toString());
}
}
测试
package com.designpattern.prototype;
import com.designpattern.bean.Robot;
import com.designpattern.bean.Robot.Battery;
public class CloneTest {
public static void main(String[] args) {
testShallowClone();
}
private static void testShallowClone() {
ShallowClone shallowClone = new ShallowClone();
shallowClone.name = "1";
shallowClone.number = 1;
shallowClone.robot = new Robot();
shallowClone.robot.name = "1";
shallowClone.robot.battery = new Battery();
shallowClone.robot.battery.name = "1";
System.out.println(shallowClone.toString());
ShallowClone shallowClone2 = (ShallowClone) shallowClone.clone();
System.out.println(shallowClone2.toString());
shallowClone.name = "2";
shallowClone.number = 2;
shallowClone.robot.name = "2";
shallowClone.robot.battery.name = "2";
System.out.println(shallowClone.toString());
System.out.println(shallowClone2.toString());
}
}
结果
ShallowClone hashCode:1704856573 name:1 number:1 Robot hashCode:705927765 name:1 age:0 Battery hashCode:366712642 name:1 level:0
ShallowClone hashCode:1829164700 name:1 number:1 Robot hashCode:705927765 name:1 age:0 Battery hashCode:366712642 name:1 level:0
ShallowClone hashCode:1704856573 name:2 number:2 Robot hashCode:705927765 name:2 age:0 Battery hashCode:366712642 name:2 level:0
ShallowClone hashCode:1829164700 name:1 number:1 Robot hashCode:705927765 name:2 age:0 Battery hashCode:366712642 name:2 level:0
我们对ShallowClone 的对象shallowClone浅复制出对象shallowClone1,这两个对象输出,属性值完全相同,但是这两个对象分配了不同的内存空间。这两个对象的String类型,int类型属性都不相关,改变其中一个对象的值类型成员变量的具体值,另一个对象不会受影响。而这两个对象的Robert属性,分配的是同一个内存空间,一个改变,另一个肯定也会改变。
五、深复制详解
一种简单实现就是在clone方法中,对值类型的成员变量再次clone,但是值类型的成员变量必须实现Cloneable接口,覆盖Object的clone方法。这样对其它实体改动太大。
更好的方法是利用序列化和反序列化来实现深复制。
先将对象写到流里,然后再从流里读出对象。
将对象写到对象流里的前提是对象实现Serializable接口,写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。
具体实现:
package com.designpattern.prototype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import com.designpattern.bean.Robot;
/**
* 深复制
*
* @author chao
*
*/
public class DeepClone implements Serializable {
public Robot robot;
public String name;
public int number;
public Object deepclone() {
// 将对象写到流里
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo;
try {
oo = new ObjectOutputStream(bo);
oo.writeObject(this);
// 从流里读出来
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return (oi.readObject());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
return " ShallowClone hashCode:" + hashCode() + " name:" + name + " number:" + number
+ (robot == null ? "" : robot.toString());
}
}
测试
package com.designpattern.prototype;
import com.designpattern.bean.Robot;
import com.designpattern.bean.Robot.Battery;
public class CloneTest {
public static void main(String[] args) {
testDeepClone();
}
private static void testDeepClone() {
DeepClone deepClone = new DeepClone();
deepClone.name = "1";
deepClone.number = 1;
deepClone.robot = new Robot();
deepClone.robot.name = "1";
deepClone.robot.battery = new Battery();
deepClone.robot.battery.name = "1";
System.out.println(deepClone.toString());
DeepClone deepclone2 = (DeepClone) deepClone.deepclone();
System.out.println(deepclone2.toString());
deepClone.name = "2";
deepClone.number = 2;
deepClone.robot.name = "2";
deepClone.robot.battery.name = "2";
System.out.println(deepClone.toString());
System.out.println(deepclone2.toString());
}
}
}
输出:
ShallowClone hashCode:1704856573 name:1 number:1 Robot hashCode:705927765 name:1 age:0 Battery hashCode:366712642 name:1 level:0
ShallowClone hashCode:1735600054 name:1 number:1 Robot hashCode:21685669 name:1 age:0 Battery hashCode:2133927002 name:1 level:0
ShallowClone hashCode:1704856573 name:2 number:2 Robot hashCode:705927765 name:2 age:0 Battery hashCode:366712642 name:2 level:0
ShallowClone hashCode:1735600054 name:1 number:1 Robot hashCode:21685669 name:1 age:0 Battery hashCode:2133927002 name:1 level:0
可以看到,深度复制的两个对象互不影响,所有属性都单独的分配了内存空间。没有相同的引用
六、JDK和ANDROIDSDK中的使用
JDK中体现:Object.clone;Cloneable
AndroidSDK中体现 Intent Parcelable
public class Intent implements android.os.Parcelable, Cloneable {
@Override
public Object clone() {
return new Intent(this);
}
/**
* Copy constructor.
*/
public Intent(Intent o) {
this.mAction = o.mAction;
this.mData = o.mData;
this.mType = o.mType;
this.mPackage = o.mPackage;
this.mComponent = o.mComponent;
this.mFlags = o.mFlags;
this.mContentUserHint = o.mContentUserHint;
if (o.mCategories != null) {
this.mCategories = new ArraySet<String>(o.mCategories);
}
if (o.mExtras != null) {
this.mExtras = new Bundle(o.mExtras);
}
if (o.mSourceBounds != null) {
this.mSourceBounds = new Rect(o.mSourceBounds);
}
if (o.mSelector != null) {
this.mSelector = new Intent(o.mSelector);
}
if (o.mClipData != null) {
this.mClipData = new ClipData(o.mClipData);
}
}
..........
}
搞懂原型模式,就很容易理解Intent传数据的时候什么情况下传的是引用,修改值会对原值造成影响。
相关代码github地址:https://github.com/robertjc/simpledesignpattern
不断完善中,有问题请多指教
欢迎扫描二维码,关注公众账号