1、 原型模式概念
该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。java中复制通过clone()实现的。clone中涉及深、浅复制。深、浅复制的概念如下:
⑴浅复制(浅克隆)
型对象的成员变量是值类型,将复制一份给克隆对象值,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。 Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址
方法:调用 java.lang.Object的clone()方法
⑵深复制(深克隆)
被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
方法:
1、对象内部所有引用型对象都进行clone。
2、对象序列化
原型模式代码
public class Prototype implements Cloneable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}finally {
return null;
}
}
public static void main ( String[] args){
Prototype pro = new Prototype();
Prototype pro1 = (Prototype)pro.clone();
}
}
2、浅克隆:
/**
* Created by yd on 2019/3/27.
* 浅克隆
*/
public class Student implements Cloneable {
private String name;
private Subject subject;
public Student(String name,Subject subject){
this.name = name;
this.subject = subject;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
public String toString(){
return "name:"+name+"; subject:{"+subject+"}";
}
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException{
Subject subject = new Subject("100","100");
Student student = new Student("YYY",subject);
System.out.println(student);
Student st = (Student)student.clone();
st.setName("ZZZ");
st.getSubject().setChinese("99");
System.out.println(student);
System.out.println(st);
}
}
class Subject {
private String english;
private String chinese;
public Subject(String chinese,String english){
this.chinese = chinese;
this.english = english;
}
public String toString(){
return "english : "+english+"; chinese:"+chinese;
}
public String getEnglish() {
return english;
}
public void setEnglish(String english) {
this.english = english;
}
public String getChinese() {
return chinese;
}
public void setChinese(String chinese) {
this.chinese = chinese;
}
}
运行结果:
name:YYY; subject:{english : 100; chinese:100}
name:YYY; subject:{english : 100; chinese:99}
name:ZZZ; subject:{english : 100; chinese:99}
浅克隆对于引用类型,只克隆了引用,因此两个对象的subject公共同一个内存地址,一个对象变化,会引起另一个对象响应的变化。
3、深克隆:
import java.io.*;
/**
* Created by yd on 2019/3/28.
* 深克隆
*/
public class Student2 implements Serializable,Cloneable{
private String name;
private Subject2 subject2;
public Student2(String name,Subject2 subject2){
this.name = name;
this.subject2 = subject2;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Subject2 getSubject2() {
return subject2;
}
public void setSubject2(Subject2 subject2) {
this.subject2 = subject2;
}
@Override
public String toString(){
return "name:"+name+"; subject:{"+subject2+"}";
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student2 student2 = (Student2)super.clone();
student2.subject2 = (Subject2) this.subject2.clone();
return student2;
}
/* 深复制 二进制的写法,需要类序列化*/
public Object deepClone() throws IOException, ClassNotFoundException {
//使用序列化和反序列化实现深复制
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
byte[] bytes = bos.toByteArray();
/* 读出二进制流产生的新对象 */
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
public static void main(String[] args) throws CloneNotSupportedException{
Subject2 subject2= new Subject2("100","100");
Student2 student2 = new Student2("YYY",subject2);
System.out.println(student2);
//方法一:对象内部所有引用型对象都进行clone。
//Student2 st2 = (Student2)student2.clone();
//方法二:对象序列化
Student2 st2 = (Student2)student2.clone();
st2.setName("ZZZ");
st2.getSubject2().setChinese("99");
System.out.println(student2);
System.out.println(st2);
}
}
class Subject2 implements Serializable, Cloneable {
private String english;
private String chinese;
public Subject2(String chinese,String english){
this.chinese = chinese;
this.english = english;
}
@Override
public String toString(){
return "english : "+english+"; chinese:"+chinese;
}
@Override
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
public String getEnglish() {
return english;
}
public void setEnglish(String english) {
this.english = english;
}
public String getChinese() {
return chinese;
}
public void setChinese(String chinese) {
this.chinese = chinese;
}
}
运行结果:
name:YYY; subject:{english : 100; chinese:100}
name:YYY; subject:{english : 100; chinese:100}
name:ZZZ; subject:{english : 100; chinese:99}
通过对引用类型值subject添加clone方法,并且对student对象的clone方法改造,实现深克隆。
4、其他:
拷贝还有2个知识点,对象拷贝时,类的构造函数是不会被执行的。一个实现了 Cloneable 并重写了 clone 方法的类 Programmer,有一个无参构造或有参构造 ,通过 new 关键字产生了一个对象 A,再然后通过 A.clone()方式产生了一个新的对象 T,那么在对象拷贝时构造函数是不会被执行的。即拷贝的过程中只执行一次构造方法。
Clone 与 final 两对冤家。对象的 clone 与对象内的 final 属性是由冲突.在上面的Programmer类中修改为private final Address address;去掉get,set方法, proto.address=(Address) address.clone();这一句就会报错: proto.address=(Address) address.clone();final类型不能重新设置值。解决办法就是删除掉fina咯
深拷贝和浅拷贝建议不要混合使用,一个类中某些引用使用深拷贝某些引用使用浅拷贝,这是一种非常差的设计,特别是是在涉及到类的继承,父类有几个引用的情况就非常的复杂,建议深拷贝和浅拷贝分开实现。