设计模式-原型模式
参考资料:尚硅谷设计模式.
萌新码农 摘自网络资源 + 自己的思考 若有错误请大佬们指点
参考资料网上重复篇章多 忘了参考了哪几位博主的文章 若有侵权 请私信 看到会加上链接
设计模式-原型模式
需求
克隆羊例子:现在有一只羊tom,姓名为:tom,年龄为:1,颜色为:白色,请编写程序创建和tom羊 属性完全相同的10只羊。
传统方式
public class CloneSheep {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom","1","white");
Sheep[] sheeps = new Sheep[10];
for(int i=0;i<10;i++) {
sheeps[i] = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
}
}
}
优缺点说明
- 优点是比较好理解,简单易操作
- 在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低。
- 总是需要重新初始化对象,而不是动态地活得对象运行时地状态,不够灵活。
- 对象属性内新增基本类型或引用类型,代码修改交复杂
浅拷贝模式
// 第一步 :实现 Cloneable 接口
public class Sheep implements Cloneable {
/**
一顿属性 + set get
*/
// 第二步:重写 clone() 方法
@Override
protected Objet clone() throws CloneNotSupportedException {
// 第三步:调用 super.clone() 方法
return super.clone();
}
}
说明
如需要克隆的类属性为基本数据类型 byte、short、int、long、char、float、double、boolean(8种) 或 String 类型(值传递)类型。直接使用浅拷贝即可完成需求。
若需要克隆的类属性有引用类型,则需要使用深拷贝。
public class CloneSheep {
public static void main(String[] args) {
Sheep sheep1 = new Sheep("tom",1,"white");
Sheep sheep2 = (Sheep)sheep1.clone();
// 1735600054---1735600054 若修改sheep1的地址
// sheep2 地址也会随之改变
System.out.println(sheep1.getAddress().hashCode()+"---"+sheep2.getAddress().hashCode());
}
}
深拷贝模式
方式一:在重写的 clone() 中直接 clone 引用模式(不推荐)
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
//这里完成对基本数据类型(属性)和 String 的克隆
deep = super.clone();
//对引用类型的属性,进行单独处理
主类型 obj = (主类型)deep;
obj.主类型内部引用类型对象 = (主类型内部引用类型)主类型内部引用类型对象.clone();
return obj;
}
说明
确实可以让主类型进行深克隆,但主类型内部引用类型过多会导致 clone() 代码繁琐,并且引用类型类内部也必须重写 clone() 方法 ,修改较为复杂。
方式二:通过对象的序列化实现(推荐)
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);
主类型 copyObj = (主类型)ois.readObject();
return copyObj;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
说明
主类型及引用类型都需加上implements Serializable
,否则可能会发生空指针异常。总体来说修改代码比第一种方式简便许多。