原型模式(Prototype Pattern)也很好理解,就是复制原来的对象来创建新的对象。类似于复制粘贴这种操作,将对象复制一份并返还给调用者。原型模式用于创建重复的对象,同时又能保证不浪费应用的系统资源。
形象的理解:火影忍者中的鸣人的影分身之术。
基本使用
//实现Cloneable接口,重新Object类的clone()方法
public class Sheep implements Cloneable {
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
private String name;
private int age;
private String color;
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 String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
//克隆该实例,使用默认的clone方法来完成
@Override
protected Object clone() throws CloneNotSupportedException{
return (Sheep) super.clone();
}
}
测试:
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheep = new Sheep("tom",1,"白色");
Sheep clone1 = (Sheep)sheep.clone(); //克隆
Sheep clone2 = (Sheep)sheep.clone(); //克隆
System.out.println("原先对象:" + sheep.toString());
System.out.println("克隆第一个对象:" + clone1.toString());
System.out.println("克隆第二个对象:" + clone2.toString());
}
}
运行结果:
深拷贝和浅拷贝
浅拷贝
浅拷贝只会拷贝对象本身相关的基本类型数据,如果是引用类型(对象,数组)浅拷贝会进行引用传递(将该成员变量的引用值复制一份给新对象,实际上两个对象的这个成员变量都是指向的同一个实例),直接看示例代码
第一步,先创建一个dog对象
public class Dog {
public Dog(String name, int age, String breed) {
this.name = name;
this.age = age;
this.breed = breed;
}
private String name;
private int age;
private String breed;
}
第二步:还是上面的Sheep对象,只是将dog引入
//实现Cloneable接口,重新Object类的clone()方法
public class Sheep implements Cloneable {
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
private String name;
private int age;
private String color;
private Dog dog;
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 String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
//克隆该实例,使用默认的clone方法来完成
@Override
protected Object clone() throws CloneNotSupportedException{
return (Sheep) super.clone();
}
}
测试:
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheep = new Sheep("tom",1,"白色");
sheep.setDog(new Dog("球球",2,"拉布拉多"));
Sheep clone1 = (Sheep)sheep.clone(); //克隆
Sheep clone2 = (Sheep)sheep.clone(); //克隆
System.out.println("原先对象:" + sheep.toString() + ",dog.hashCode=" + sheep.getDog().hashCode());
System.out.println("克隆第一个对象:" + clone1.toString()+ ",dog.hashCode=" + clone1.getDog().hashCode());
System.out.println("克隆第二个对象:" + clone2.toString()+ ",dog.hashCode=" + clone2.getDog().hashCode());
}
}
运行结果:
从运行结果看出,三个羊里面的dog的hashCode是一样的,所以认为三个dog是同一个对象。
得到结论:浅拷贝是使用默认的clone()方法来实现的 (Sheep)super.clone();只是拷贝了基本属性,dog是同一个对象。
深拷贝(两种实现方式)
为所有的引用数据类型的成员变量申请存储空间,并复制每一个引用数据类型成员变量所引起的对象,直到该对象可达的所有对象。也就是说深拷贝要对整个对象进行拷贝(包括对象的引用类型)进行拷贝。
1.重写clone()方法实现深拷贝
1)重写dog的clone()方法
public class Dog implements Cloneable {
public Dog(String name, int age, String breed) {
this.name = name;
this.age = age;
this.breed = breed;
}
private String name;
private int age;
private String breed;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
2)重写sheep的clone()方法
public class Sheep implements Cloneable {
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
private String name;
private int age;
private String color;
private Dog dog;
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 String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
//深拷贝,方式1:使用clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
Sheep sheep = null;
sheep = (Sheep) super.clone();
Dog cloneDog = (Dog) sheep.getDog().clone();
sheep.setDog(cloneDog);
return sheep;
}
}
测试:
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheep = new Sheep("tom",1,"白色");
sheep.setDog(new Dog("球球",2,"拉布拉多"));
Sheep clone1 = (Sheep)sheep.clone(); //克隆
Sheep clone2 = (Sheep)sheep.clone(); //克隆
System.out.println("原先对象:" + sheep.toString() + ",dog.hashCode=" + sheep.getDog().hashCode());
System.out.println("克隆第一个对象:" + clone1.toString()+ ",dog.hashCode=" + clone1.getDog().hashCode());
System.out.println("克隆第二个对象:" + clone2.toString()+ ",dog.hashCode=" + clone2.getDog().hashCode());
}
}
运行结果:
2.序列化的方式
首先给dog和sheep不需要实现Cloneable接口,但是都要实现Serializable接口,在给sheep增加sheepClone()方法
public Object sheepClone(){
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);
Sheep sheep = (Sheep)ois.readObject();
return sheep;
}catch (Exception e){
e.printStackTrace();
return null;
}finally {
//关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
测试:
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheep = new Sheep("tom",1,"白色");
sheep.setDog(new Dog("球球",2,"拉布拉多"));
Sheep clone1 = (Sheep)sheep.sheepClone(); //克隆
Sheep clone2 = (Sheep)sheep.sheepClone(); //克隆
System.out.println("原先对象:" + sheep.toString() + ",dog.hashCode=" + sheep.getDog().hashCode());
System.out.println("克隆第一个对象:" + clone1.toString()+ ",dog.hashCode=" + clone1.getDog().hashCode());
System.out.println("克隆第二个对象:" + clone2.toString()+ ",dog.hashCode=" + clone2.getDog().hashCode());
}
}
运行结果:
两种方式实现深复制, 我比较推荐使用第二种, 为什么呢, 如果一个类的引用类型很多, 第一种方式则需要一个一个处理, 第二种方式则是一次性处理了, 第二种方式还是比较方便一点。
总结
1)如果一个对象的属性比较多,创建新的对象比较复杂时,可以利用原型模式简化对象的创建,同时也能提高效率。
2)如果这个对象的属性增多或者减少,其他的克隆对象的也会发生对应的变化,无需更改代码。
3)浅拷贝只是拷贝对象本身的基本类型数据,而深拷贝会拷贝这个对象的所有属性。