对象A指向的是它的内存地址,当我们把对象A赋值给对象B,他俩还是指向的同一处地址,修改A的话,B也跟着变了。(基本数据类型则是直接赋值,String类型虽然是引用类型,但它被final修饰不可改变的,所以你修改它时其实修改的是string的另一个新的指向引用,所以String自带深克隆)对象的重默认clone()方法会调用super.clone(),其实是浅拷贝。
School学校类,给User充当引用类型变量
public class School {
private String name;
public School(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "School{" +
"name='" + name + '\'' +
'}';
}
}
User类,有自带深克隆的String变量和引用类型的School变量
public class User implements Cloneable{
private String name;
private School school;
public User(String name, School school) {
this.name = name;
this.school = school;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public School getSchool() {
return school;
}
public void setSchool(School school) {
this.school = school;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", school=" + school +
'}';
}
}
浅克隆就是直接调用clone()方法,赋值不彻底,基本数据类型不会影响, 但是引用类型影响克隆出来的副本
public static void main(String[] args) throws CloneNotSupportedException {
User user=new User("张三",new School("小学"));
User userClone= (User) user.clone();
userClone.setName("李四");
userClone.getSchool().setName("大学");
System.out.println(user+" "+userClone);//User{name='张三', school=School{name='大学'}} User{name='李四', school=School{name='大学'}}
}
因为他们的school都是指向同一块地址,自然会跟着改变。
深克隆就是彻底克隆一个副本出来,互不影响。目的在于如何实现深克隆
实现深克隆
User类实现Cloneable接口,并重写clone()方法,
@Override
protected Object clone() throws CloneNotSupportedException {
User user= (User) super.clone();//clone()默认是浅拷贝,所以我们先把浅拷贝的基本数据类型,String拷贝一下
// 再格外处理一下引用类型
user.setSchool((School) user.getSchool().clone());
return user;
}
这样就实现了深度拷贝(如果School里面也有引用类型的话,同理去重写clone()方法就行了,直到你的引用类型里只剩下基本数据类型或者String
等)
序列化实现深拷贝
重写Clone方法可太复杂了。复杂点的对象直接俄罗斯套娃。推荐使用序列化
Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。
将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。
原理就是将对象放入别的地方,取过来时直接给他开辟一个新内存地址,自然实现深度拷贝
对象需要实现Serializable接口,然后利用ObjectOutputStream/ObjectInputStream进行中转即可