1、原型模式的定义
原型模式是 使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。
- 是 创建型的设计模式
- 分为 深度克隆 和 浅克隆
2、原型模式UML
原型模式要求对象实现一个可以“克隆” 自身的 接口 ,这样就可以通过复制一个实例对象本身来创建一个新的实例。这样一来,通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法来获取新的对象,而无须再去通过 new 来创建。
这种形式涉及到三个角色:
(1)抽象原型(Prototype):这是一个抽象角色,通常由一个Java接口或Java抽象类实现。此角色给出所有的具体原型类所需的接口。
(2)具体原型(Concrete Prototype):被复制的对象。此角色需要实现抽象的原型角色所要求的接口。
(3)客户(Client) :客户类提出创建对象的请求。
3、 java 中实现克隆
实现 Cloneable
接口:
Cloneable
接口 就是上面提到的 抽象原型。
Cloneable
接口的作用是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出 CloneNotSupportedException
异常。
重写Object类中的clone方法:
Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,原型类需要将clone方法的作用域修改为public类型。
4、原型模式的优缺点
4.1、优点:
1、使用原型模型创建一个对象比直接new一个对象更有效率,因为它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
2、隐藏了制造新实例的复杂性,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。
4.2、 缺点:
1、由于使用原型模式复制对象时不会调用类的构造方法,所以原型模式无法和单例模式组合使用,因为原型类需要将clone方法的作用域修改为public类型,那么单例模式的条件就无法满足了。
2、使用原型模式时不能有final对象。
3、Object类的clone方法只会拷贝对象中的基本数据类型,对于数组,引用对象等只能另行拷贝。这里涉及到深拷贝和浅拷贝的概念。
5、浅拷贝
package design.prototype;
public class Sheep implements Serializable,Cloneable {
private String name;
private int age;
private String color;
private Sheep friend;
public Sheep(String name, int age, String color) {
super();
this.name = name;
this.age = age;
this.color = 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;
}
public Sheep getFriend() {
return friend;
}
public void setFriend(Sheep friend) {
this.friend = friend;
}
@Override
public String toString() {
return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", friend=" + friend + "]";
}
@Override
public Sheep clone() throws CloneNotSupportedException {
Sheep sheep=null;
try{
sheep=(Sheep)super.clone();
}catch(Exception e){
System.err.println(e.getMessage());
}
return sheep;
}
}
package design.prototype;
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep friend = new Sheep("JOK", 1, "黑色");
Sheep sheep = new Sheep("tom", 2, "白色");
sheep.setFriend(friend);
Sheep sheep2 = sheep.clone();
System.out.println(sheep);
System.out.println(sheep2);
System.out.println(sheep == sheep2);
System.out.println(sheep.hashCode());
System.out.println(sheep2.hashCode());
System.out.println("friend = "+sheep.getFriend().hashCode());
System.out.println("friend2 = "+sheep2.getFriend().hashCode());
}
}
运行结果:
Sheep [name=tom, age=2, color=白色, friend=Sheep [name=JOK, age=1, color=黑色, friend=null]]
Sheep [name=tom, age=2, color=白色, friend=Sheep [name=JOK, age=1, color=黑色, friend=null]]
false
366712642
1829164700
friend = 2018699554
friend2 = 2018699554
分析: Sheep中的基本类型是复制了值,但是引用类型是复制了引用。
6、浅克隆与深克隆 的区别
无论你是自己实现克隆方法,还是采用Java提供的克隆方法,都存在一个浅度克隆和深度克隆的问题。
- 浅度克隆
如果原型对象的成员变量是值类型(八大基本类型:byte、short、in、long、char、double、float、boolean),那么就直接复制。
如果是复杂的类型,(枚举,String,对象)就只复制对应的内存地址。
在换个说法,就是复杂类型的成员变量(String、枚举,对象)用的是一个。修改了克隆对象的原型对象也会变。他们是共用的。而值类型不是共用的。
- 深度克隆
深克隆,我就不用多说了吧,就是什么都是单独的!全部复制,然后各自独立。你修改克隆对象对于原型对象没有丝毫影响,完全的影分身!
7、深度克隆
clone()
方法无法进行深度克隆, 又实现deepclone()
方法,通过字节复制实现深度克隆。
package design.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;
public class Sheep implements Serializable,Cloneable {
private String name;
private int age;
private String color;
private Sheep friend;
public Sheep(String name, int age, String color) {
super();
this.name = name;
this.age = age;
this.color = 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;
}
public Sheep getFriend() {
return friend;
}
public void setFriend(Sheep friend) {
this.friend = friend;
}
@Override
public String toString() {
return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", friend=" + friend + "]";
}
public Sheep clone() throws CloneNotSupportedException {
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (Exception e) {
System.err.println(e.getMessage());
}
return sheep;
}
public Sheep 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);
return (Sheep) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep friend = new Sheep("JOK", 1, "黑色");
Sheep sheep = new Sheep("tom", 2, "白色");
sheep.setFriend(friend);
Sheep sheep2 = sheep.deepclone();
System.out.println(sheep);
System.out.println(sheep2);
System.out.println(sheep == sheep2);
System.out.println(sheep.hashCode());
System.out.println(sheep2.hashCode());
System.out.println("friend = "+sheep.getFriend().hashCode());
System.out.println("friend2 = "+sheep2.getFriend().hashCode());
}
}
运行结果:
Sheep [name=tom, age=2, color=白色, friend=Sheep [name=JOK, age=1, color=黑色, friend=null]]
Sheep [name=tom, age=2, color=白色, friend=Sheep [name=JOK, age=1, color=黑色, friend=null]]
false
1442407170
558638686
friend = 589431969
friend2 = 1149319664
https://blog.csdn.net/qq_40709468/article/details/82316418