原型模式(PrototypePattern):
原型模式是指用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象(原型模式也是一种对象创建模式)。适合原型的情景:程序需要从一个对象出发,得到若干个和其状态相同,并可独立变化其状态的对象时;对象创建需要独立于它的构造方法和表示时;以原型对象为基础,克隆新的对象,并且完善对象实例变量时。
**注意:**在使用clone方法实现复制时,进行的是浅复制 它会在复制对象和原对象之间共享子对象。深复制会包含原对象的所有对象属性。例如 你克隆一个指向B对象的A对象,浅复制会创建一个A‘对象指向B对象,而深复制会创建一个新的A’指向新的
B‘对象。clone方法必须实现Cloneable接口,因为这个接口是标记接口虽然是空接口但是表示支持clone方法。
原型模式分为浅复制和深复制。
1.浅复制:
如果原型对象的成员变量是值类型,则复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象。对于引用类型来说当对象被复制时本身的值并没有得到复制。
【java中一共分为8种基本数据类型:byte、short、int、long、float、double、char、boolean,其中byte、short、int、long是整型。float、double是浮点型,char是字符型,boolean是布尔型。
java为每种基本类型都提供了对应的封装类型,分别为:Byte、Short、Integer、Long、Float、Double、Character、Boolean。引用类型是一种对象类型,它的值是指向内存空间的引用,就是地址。】
2.深复制:
将原型对象的成员变量全部复制一份(引用类型也会被复制一份)
原型复制分为三种:构造函数方法;利用Cloneable接口方法;利用Serializable序列化接口方法。
原型复制的演示:
构造方法演示浅复制:
public class Student {
private String name;
private int age;
Address add; //代表籍贯(引用类型变量)
//有参数构造函数
public Student(String name, int age, Address add) {
this.name = name;
this.age = age;
this.add = add;
}
//无参数构造函数
public Student() {
}
//复制使用的构造函数
public Student(Student s) {
name = s.getName();
age = s.getAge();
add = s.getAdd();
}
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 Address getAdd() {
return add;
}
public void setAdd(Address add) {
this.add = add;
}
}
class Address {
private String pro;//省会
private String city;//城市
private String zip; //邮编
//有参数构造函数
public Address(String pro, String city, String zip) {
this.pro = pro;
this.city = city;
this.zip = zip;
}
//有参数构造函数
public Address() {
}
public String getPro() {
return pro;
}
public void setPro(String pro) {
this.pro = pro;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
}
/**
* 利用构造函数测试浅复制
*/
public class Test_SimpleClone {
public static void main(String[] args) {
Address address = new Address("陕西","西安","710111");
Student s = new Student("张三",18,address);
Student s1 = new Student(s); //利用构造函数s复制新学生s1
System.out.println(s+"------"+s1);//输出两个学生对象
System.out.println(s.getName()+"-"+s.getAge()+"-"+s.getAdd());
System.out.println(s1.getName()+"-"+s1.getAge()+"-"+s1.getAdd());
}
}
结果:
从结果可以看到s和s1输出内容一致 但是是不同的对象。name属性和age属性已经复制但是s和s1的引用类型add地址值一致,如果改变s1的籍贯则会导致s的籍贯也会改变。
构造方法演示深复制:
只需要在Student和Address中添加如下构造方法
测试代码:
/**
* 利用构造方法进行深复制
*/
public class Test_deepClone {
public static void main(String[] args) {
Address address = new Address("陕西","西安","710111");
Student s = new Student("张三",18,address);
Student s1 = new Student(s); //利用构造函数s复制新学生s1
System.out.println(s+"------"+s1);//输出两个学生对象
System.out.println(s.getName()+"-"+s.getAge()+"-"+s.getAdd());
System.out.println(s1.getName()+"-"+s1.getAge()+"-"+s1.getAdd());
}
}
结果:
可以看出s和s1中对象地址不同并且Address中地址也不同;也就是说s对象的姓名年龄籍贯和s1对象的姓名年龄籍贯存储不同的地址空间;实现了全部属性的复制 产生了新的对象和属性。
Cloneable实现必须实现Cloneable接口。调用Object中的clone方法
Cloneable方法实现浅复制:
public class Student implements Cloneable {
private String name;
private int age;
Address add; //代表籍贯(引用类型变量)
//有参数构造函数
public Student(String name, int age, Address add) {
this.name = name;
this.age = age;
this.add = add;
}
//无参数构造函数
public Student() {
}
//重写clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
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 Address getAdd() {
return add;
}
public void setAdd(Address add) {
this.add = add;
}
}
class Address {
private String pro;//省会
private String city;//城市
private String zip; //邮编
//有参数构造函数
public Address(String pro, String city, String zip) {
this.pro = pro;
this.city = city;
this.zip = zip;
}
//有参数构造函数
public Address() {
}
public String getPro() {
return pro;
}
public void setPro(String pro) {
this.pro = pro;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
}
/**
* 利用Cloneable方法测试浅复制
*/
public class Test_SimpleClone {
public static void main(String[] args) throws Exception {
Address address = new Address("陕西","西安","12344");
Student s = new Student("张三",18,address);
Student s1 = (Student) s.clone(); //利用Cloneable方法s复制新学生s1
System.out.println(s+"------"+s1);//输出两个学生对象
System.out.println(s.getName()+"-"+s.getAge()+"-"+s.getAdd());
System.out.println(s1.getName()+"-"+s1.getAge()+"-"+s1.getAdd());
}
}
结果:
输出结果和构造方法浅复制一致。
Cloneable方法实现深复制:
在Student和Address中重写clone方法
结果:
输出结果和构造方法深复制一致。
利用Serializable序列化接口方法
/*
利用Serializable接口方法实现深复制
则需要实现接口Serializable
*/
//在复制对象的源类中为了更好的表达复制功能,所以一般同时实现Serializable,Cloneable接口
public class Student implements Serializable,Cloneable{
private String name;
private int age;
Address add; //代表籍贯(引用类型变量)
//有参数构造函数
public Student(String name, int age, Address add) {
this.name = name;
this.age = age;
this.add = add;
}
//无参数构造函数
public Student() {
}
/**
* 原理:序列化就是将对象写到流中,写到流中的对象是原对象的一个复制,而原对象仍然在内存中
* 通过序列化实现的复制不仅可以复制本身对象而且还可以复制其引用的成员对象。
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Object clone() throws CloneNotSupportedException {
Object object = null;
try {
//写到流中
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);//this 代表Student s = new Student("张三",18,address)
//从流中读出
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
object = ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return object;
}
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 Address getAdd() {
return add;
}
public void setAdd(Address add) {
this.add = add;
}
}
class Address implements Serializable {
private String pro;//省会
private String city;//城市
private String zip; //邮编
//有参数构造函数
public Address(String pro, String city, String zip) {
this.pro = pro;
this.city = city;
this.zip = zip;
}
//有参数构造函数
public Address() {
}
public String getPro() {
return pro;
}
public void setPro(String pro) {
this.pro = pro;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
}
/**
* 利用Serializable方法测试深复制
*/
public class Test_SimpleClone {
public static void main(String[] args) throws Exception {
Address address = new Address("陕西","西安","12344");
Student s = new Student("张三",18,address);
Student s1 = (Student) s.clone(); //利用Cloneable方法s复制新学生s1
System.out.println(s+"------"+s1);//输出两个学生对象
System.out.println(s.getName()+"-"+s.getAge()+"-"+s.getAdd());
System.out.println(s1.getName()+"-"+s1.getAge()+"-"+s1.getAdd());
}
}
结果: