定义:
原型模式是用于创建重复的对象,同时又能保证性能,它提供了一种创建对象的最佳方式。
优点:
1、性能提高
2、逃避构造函数的约束
缺点:
1、配备clone()方法需要对类的功能进行全盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类的引用不支持串行化的间接对象,或者引用含有循环结构的时候。
2、必须实现Cloneable接口
组成部分:
1、抽象原型类(Prototype):它是声明克隆方法的接口,是所有具体原型类的公共父类,也可以是抽象类或者接口,甚至还可以是具体实现类。
2、具体原型类(Concrete Prototype):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
3、客户类(Client):使用具体原型类中的clone()方法来复制新的对象。
注意事项:
如果要克隆一个类的实例,那么这个类:
1、必须实现Cloneable接口
2、使用public访问修饰符重新定义object类的clone方法
实现方式:
1、浅复制:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅复制。
2、深复制:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
当需要复制的类不包含复杂的对象类时
步骤一: 被复制的类需要实现Clonenable接口(不实现的话在调用clone()方法会抛出CloneNotSupportedException异常) 该接口为标记接口(不含任何方法)。
步骤二:覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象,(native为本地方法)。
1、实现Clonenable接口并且覆盖clone()方法:
public class Student implements Cloneable{
private String number;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
@Override
protected Student clone() throws CloneNotSupportedException {
Student stu = null;
try{
stu = (Student) super.clone();
}catch(Exception e) {
e.printStackTrace();
}
return stu;
}
}
2、测试代码如下:
public class TestMain {
public static void main(String[] args) throws CloneNotSupportedException {
Student s1 = new Student();
s1.setNumber("12345");
Student s2 = s1.clone();
System.out.println("学生1学号为"+s1.getNumber());
System.out.println("学生2学号为"+s2.getNumber());
s2.setNumber("54321");
System.out.println("学生1学号为"+s1.getNumber());
System.out.println("学生2学号为"+s2.getNumber());
}
}
3、输出结果如下:
s1和s2并不是同一个对象,如下所示,说明我们复制成功了,这里面采用的是深拷贝。
System.out.println(stu1 == stu2); // false
当需要复制的类包含复杂的对象类时:
1、我们新建一个Address类:
public class Address {
private String detailsAddress;
public String getDetailsAddress() {
return detailsAddress;
}
public void setDetailsAddress(String detailsAddress) {
this.detailsAddress = detailsAddress;
}
}
2、在Student类里面引入Address类:
public class Student implements Cloneable{
private String number;
private Address address;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
protected Student clone() throws CloneNotSupportedException {
Student stu = null;
try{
stu = (Student) super.clone();
}catch(Exception e) {
e.printStackTrace();
}
return stu;
}
}
3、 测试代码如下:
public class TestMain {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address();
address.setDetailsAddress("天津");
Student s1 = new Student();
s1.setNumber("12345");
s1.setAddress(address);
Student s2 = s1.clone();
System.out.println("学生1学号为"+s1.getNumber()+",地址为:"+s1.getAddress().getDetailsAddress());
System.out.println("学生2学号为"+s2.getNumber()+",地址为:"+s1.getAddress().getDetailsAddress());
}
}
4、输出结果如下:
乍一看没什么问题,真的是这样吗?我们在main方法中试着改变s2实例的detailsAddress。
public class TestMain {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address();
address.setDetailsAddress("天津");
Student s1 = new Student();
s1.setNumber("12345");
s1.setAddress(address);
Student s2 = s1.clone();
s2.getAddress().setDetailsAddress("北京");
System.out.println("学生1学号为"+s1.getNumber()+",地址为:"+s1.getAddress().getDetailsAddress());
System.out.println("学生2学号为"+s2.getNumber()+",地址为:"+s1.getAddress().getDetailsAddress());
}
}
输出结果:
这就奇怪了,怎么两个学生的地址都改变了?原因是浅复制只是复制了address变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象。所以,为了达到真正的复制对象,而不是纯粹引用复制。我们只需要将Address类可复制化,并且修改clone方法,完整代码如下:
public class Address implements Cloneable{
private String detailsAddress;
public String getDetailsAddress() {
return detailsAddress;
}
public void setDetailsAddress(String detailsAddress) {
this.detailsAddress = detailsAddress;
}
@Override
protected Address clone() throws CloneNotSupportedException {
return (Address) super.clone();
}
}
public class Student implements Cloneable{
private String number;
private Address address;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
protected Student clone(){
Student stu = null;
try{
stu = (Student) super.clone();
/*深度复制*/
stu.address =(Address)address.clone();
}catch(Exception e) {
e.printStackTrace();
}
return stu;
}
}
测试代码如下:
public class TestMain {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address();
address.setDetailsAddress("天津");
Student s1 = new Student();
s1.setNumber("12345");
s1.setAddress(address);
Student s2 = s1.clone();
s2.getAddress().setDetailsAddress("北京");
System.out.println("学生1学号为"+s1.getNumber()+",地址为:"+s1.getAddress().getDetailsAddress());
System.out.println("学生2学号为"+s2.getNumber()+",地址为:"+s2.getAddress().getDetailsAddress());
}
}
输出结果如下: