前言
浅拷贝和深拷贝都是对一个已有对象的拷贝,拷贝的结果是一个新的对象,那么他们的区别是什么呢?我们知道对象都是有属性的,属性又分为基本数据类型比如int,和实例引用类型这两种。
针对基本数据类型没什么好说的,直接复制过去就好。关键之处就在于引用类型,浅拷贝生成的对象的引用类型的属性和原对象是同一个,而深拷贝则反之。
浅拷贝
我们都知道java中的对象都会有clone方法,这是一个用来实现拷贝的native方法。
public class User implements Cloneable{
private int id;
private MyTest myTest;
// setter getter constructor
public User(int id,MyTest myTest){
this.id=id;
this.myTest=myTest;
}
public static void main(String[] args) throws Exception {
User user=new User(3,new MyTest());
User user1=(User) user.clone();
System.out.println(user==user1);
System.out.println(user.getMyTest()==user1.getMyTest());
}
}
false
true
需要注意的是要想使用clone方法必须使用Cloneable 接口,不然会抛出 CloneNotSupportedException 异常
另外,从代码运行结果我们也能看出浅拷贝确实生成新对象了,但是新对象中的引用类型的属性和原对象还是一样的。
深拷贝
怎么实现深拷贝呢?其实很简单,最简单的就是重写clone方法;还有一种方法是通过Serialize 序列化
重写clone实现深拷贝
@Override
protected Object clone() throws CloneNotSupportedException {
// 这里我新new一个对象,其实也可以让Mytest实现Cloneable,继续使用mytest的clone() 方法
return new User(this.getId(),new MyTest());
}
就这么简单,很明显现在拷贝得到的新对象和原对象不是一个,同时新对象中的引用属性和原对象也不是同一个。
序列化实现深拷贝
public class User implements Cloneable,Serializable{
private String name;
transient private MyTest myTest;
// setter getter constructor
@Override
protected Object clone() {
try {
OutputStream outputStream=new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream=new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(this);
InputStream inputStream=new ByteArrayInputStream(((ByteArrayOutputStream)outputStream).toByteArray());
ObjectInputStream objectInputStream=new ObjectInputStream(inputStream);
return objectInputStream.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) throws Exception {
User user=new User("fbb",new MyTest());
User user1=(User) user.clone();
System.out.println(user==user1);
System.out.println(user.getMyTest()==user1.getMyTest());
}
}
false
false
我们把原对象写入流中,再从流中读出,也可以实现对象的深拷贝,不过这种方法要求属性中的引用对象也实现序列化或者使用transient修饰