As we all know Java有两种拷贝,浅拷贝和深拷贝,高大上的叫法也叫浅克隆和深克隆。
深克隆有时会碰到一个有趣的现象,被克隆的对象中存在引用类型A但引用类型A中又存在引用类型B不断的递归下去。进入了“从前有座山,山里有个庙....”模式。
在递归栈不是很深的情况下还能应付(每个引用类型都实现Cloneable这个接口,并重写Object类的clone方法),但到了很深的情况下这个问题该如何解呢?
温故一下
- 浅克隆步骤
1)被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常) 该接口为标记接口(不含任何方法);
2)覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象,
class Student implements Cloneable{
private int number;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
@Override
public Object clone() {
Student stu = null;
try{
stu = (Student)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return stu;
}
}
public class Test {
public static void main(String args[]) {
Student stu1 = new Student();
stu1.setNumber(12345);
Student stu2 = (Student)stu1.clone();
System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());
stu2.setNumber(54321);
System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());
}
}
- 深克隆步骤
1)被克隆对象中的引用类型同样实现Cloneable接口并重写Object类中的clone方法即可;
2)接下来就和浅克隆操作一样了。
package abc;
class Address implements Cloneable {
private String add;
public String getAdd() {
return add;
}
public void setAdd(String add) {
this.add = add;
}
@Override
public Object clone() {
Address addr = null;
try{
addr = (Address)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return addr;
}
}
class Student implements Cloneable{
private int number;
//引用类型
private Address addr;
public Address getAddr() {
return addr;
}
public void setAddr(Address addr) {
this.addr = addr;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
@Override
public Object clone() {
Student stu = null;
try{
//浅复制
stu = (Student)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
//深度复制
stu.addr = (Address)addr.clone();
return stu;
}
}
public class Test {
public static void main(String args[]) {
Address addr = new Address();
addr.setAdd("北京市");
Student stu1 = new Student();
stu1.setNumber(123);
stu1.setAddr(addr);
Student stu2 = (Student)stu1.clone();
System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
addr.setAdd("朝阳区");
System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
}
}
解决办法--序列化与反序列实现对象复制
public class Dog implements Serializable{
private static final long serialVersionUID = 1L;
private String dogName;
public String getDogName() {
return dogName;
}
public void setDogName(String dogName) {
this.dogName = dogName;
}
}
public class User implements Serializable{
private static final long serialVersionUID = 1L;
private String username;
private Dog dog;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
}
public class ObjCloner {
@SuppressWarnings("unchecked")
public static <T>T cloneObj(T obj){
T retVal = null;
try{
// 将对象写入流中
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
// 从流中读出对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
retVal = (T)ois.readObject();
}catch(Exception e){
e.printStackTrace();
}
return retVal;
}
}
public class CloneTest {
public static void main(String[] args) {
User user = new User();
user.setUsername("张三");
Dog dog = new Dog();
dog.setDogName("小狗1");
user.setDog(dog);
User user2 = ObjCloner.cloneObj(user);
System.out.println("user username : "+user.getUsername());
System.out.println("user dogname : " + user.getDog().getDogName());
System.out.println("user2 username : "+user2.getUsername());
System.out.println("user2 dogname : " + user2.getDog().getDogName());
System.out.println(" -------------------------------------");
user2.setUsername("李四");
user2.getDog().setDogName("小狗2");;
System.out.println("user username : "+user.getUsername());
System.out.println("user dogname : " + user.getDog().getDogName());
System.out.println("user2 username : "+user2.getUsername());
System.out.println("user2 dogname : " + user2.getDog().getDogName());
}
}