学过Spring框架的同志应该都听过原型吧,在创建bean的时候可以指定prototype,这样bean就是原型模式,什么叫原型模式?就是将一个对象拷贝(复制)多份。
比如下面的代码:
public class Test {
public static void main(String[] args) {
A a1 = new A(1,2,3);
A a2 = new A(1,2,3);
A a3 = new A(1,2,3);
A a4 = new A(1,2,3);
}
}
a1、a2、a3、a4虽然属性值都相同,但是却是不同的对象。这种操作很复杂,所以我们想到使用Object的clone方法,来简化这种拷贝(复制)。
浅拷贝:
需要对象类实现Cloneable接口,然后书写Clone方法(其实就是调用父类的clone方法)
Luzelong类:
package Kb;
import java.awt.*;
public class Luzelong implements Cloneable {
int age;
String name;
Friend friend;
public Luzelong(int age, String name) {
this.age = age;
this.name = name;
}
public Luzelong(int age, String name, Friend friend) {
this.age = age;
this.name = name;
this.friend = friend;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Friend getFriend() {
return friend;
}
public void setFriend(Friend friend) {
this.friend = friend;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Friend类:
package Kb;
public class Friend {
int age;
String name;
public Friend(int age, String name) {
this.age = age;
this.name = name;
}
}
测试:
package Kb;
public class Test {
public static void main(String[] args) {
Luzelong longlong = new Luzelong(19, "卢泽龙");
Friend ll = new Friend(20, "张帅");
longlong.setFriend(ll);
try {
Luzelong l1 = (Luzelong) longlong.clone();
Luzelong l2 = (Luzelong) longlong.clone();
Luzelong l3 = (Luzelong) longlong.clone();
Luzelong l4 = (Luzelong) longlong.clone();
System.out.println(longlong+ "*********"+longlong.friend.hashCode());
System.out.println(l1+ "*********"+l1.friend.hashCode());
System.out.println(l2+ "*********"+l2.friend.hashCode());
System.out.println(l3+ "*********"+l3.friend.hashCode());
System.out.println(l4+ "*********"+l4.friend.hashCode());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
运行的结果如下:我们发现!如果直接调用父类的clone方法,默认使用的就是浅拷贝(引用类型的数据其实还是指向之前的地址)
为了解决上述的问题,就引出了深拷贝问题,深拷贝相比较浅拷贝的一个重要特点就是他能将引用类型的数据也复制一份。
深拷贝有两种写法:
方法1:重写父类的clone方法,其核心思想就是让该引用属性也进行克隆,具体实现步骤如下:
1.让friend所代表的Friend类也实现Cloneable接口,并重写clone方法,改进的代码如下:
package Kb;
public class Friend implements Cloneable{
int age;
String name;
public Friend(int age, String name) {
this.age = age;
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
2.重写对象类的clone:
package Kb;
import java.awt.*;
public class Luzelong implements Cloneable {
int age;
String name;
Friend friend;
public Luzelong(int age, String name) {
this.age = age;
this.name = name;
}
public Luzelong(int age, String name, Friend friend) {
this.age = age;
this.name = name;
this.friend = friend;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Friend getFriend() {
return friend;
}
public void setFriend(Friend friend) {
this.friend = friend;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object lzl = null;
//这里完成对基本数据类型(属性)和String的克隆
lzl = super.clone();
//对引用类型的属性,进行单独处理
Luzelong longlong = (Luzelong) lzl;
longlong.friend = (Friend) friend.clone();
// TODO Auto-generated method stub
return longlong;
}
}
再次调用下面的测试类:
package Kb;
public class Test {
public static void main(String[] args) {
Luzelong longlong = new Luzelong(19, "卢泽龙");
Friend ll = new Friend(20, "张帅");
longlong.setFriend(ll);
try {
Luzelong l1 = (Luzelong) longlong.clone();
Luzelong l2 = (Luzelong) longlong.clone();
Luzelong l3 = (Luzelong) longlong.clone();
Luzelong l4 = (Luzelong) longlong.clone();
System.out.println(longlong+ "*********"+longlong.friend.hashCode());
System.out.println(l1+ "*********"+l1.friend.hashCode());
System.out.println(l2+ "*********"+l2.friend.hashCode());
System.out.println(l3+ "*********"+l3.friend.hashCode());
System.out.println(l4+ "*********"+l4.friend.hashCode());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
运行结果如下,发现hashcode值不一样了,说明引用对象也进行了拷贝,但是这样写对项目的改动较大,违反了ocp原则,所以这里推荐下面的写法。
方法二:利用序列化和反序列化实现深拷贝
注:该方法已经不需要实现Cloneabel接口
实现步骤如下:让两个pojo类都实现Serializable接口:
Friend:
package kb2;
import java.io.Serializable;
public class Friend implements Serializable {
int age;
String name;
public Friend(int age, String name) {
this.age = age;
this.name = name;
}
}
Luzelong:注意观察下面我自己写的deepclone方法:
package kb2;
import java.io.*;
public class Luzelong implements Serializable {
int age;
String name;
Friend friend;
public Luzelong(int age, String name) {
this.age = age;
this.name = name;
}
public Luzelong(int age, String name, Friend friend) {
this.age = age;
this.name = name;
this.friend = friend;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Friend getFriend() {
return friend;
}
public void setFriend(Friend friend) {
this.friend = friend;
}
public Object 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);
Luzelong copyObj = (Luzelong) ois.readObject();
return copyObj;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return null;
} finally {
//关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
// TODO: handle exception
System.out.println(e2.getMessage());
}
}
}
}
测试时,注意调用的是deepClone(),毕竟没有实现Cloneable接口嘛。
package kb2;
public class Test {
public static void main(String[] args) {
Luzelong longlong = new Luzelong(19, "卢泽龙");
Friend ll = new Friend(20, "张帅");
longlong.setFriend(ll);
try {
Luzelong l1 = (Luzelong) longlong.deepClone();
Luzelong l2 = (Luzelong) longlong.deepClone();
Luzelong l3 = (Luzelong) longlong.deepClone();
Luzelong l4 = (Luzelong) longlong.deepClone();
System.out.println(longlong+ "*********"+longlong.friend.hashCode());
System.out.println(l1+ "*********"+l1.friend.hashCode());
System.out.println(l2+ "*********"+l2.friend.hashCode());
System.out.println(l3+ "*********"+l3.friend.hashCode());
System.out.println(l4+ "*********"+l4.friend.hashCode());
} catch (Exception e){
e.printStackTrace();
}
}
}
运行结果也能说明深拷贝成功: