在Java中,除了基本数据类型之外,还存在类的实例对象这个引用数据类型。而一般使用“=”号做赋值操作时,对于基本数据类型,实际上就是拷贝他的值,但对于对象来说,其实赋值的是这个对象的引用,将原对象的引用传递过去,他们实际上还是指向同一个对象。
如果在拷贝这个对象时,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用传递,而没有真实的创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建一个新的对象,并且复制其内的成员变量,则认为是深拷贝。
clone会创建一个对象
https://blog.csdn.net/u014727260/article/details/55003402
clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象。所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象。那么在java语言中,有几种方式可以创建对象呢?
- 使用new操作符创建一个对象
- 使用clone方法复制一个对象
那么这两种方式有什么相同和不同呢? new操作符的本意是分配内存。程序执行到new操作符时, 首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。而clone在第一步是和new相似的, 都是分配内存,调用clone方法时,分配的内存和源对象(即调用clone方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域, 填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部
引用的拷贝
//引用拷贝
private static void copyReferenceObject(){
Person p = new Person(23, "zhang");
Person p1 = p;
System.out.println(p);
System.out.println(p1);
}
这里打印的结果:
com.yaolong.clone.Person@3654919e
com.yaolong.clone.Person@3654919e
可以看到,打印的结果是一样的,也就是说,二者的引用是同一个对象,并没有创建出一个新的对象。
实现浅拷贝
将类实现Cloneable()接口,并且重写clone()方法。
public class Subject {
private String name;
public Subject(String s) {
name = s;
}
public String getName() {
return name;
}
public void setName(String s) {
name = s;
}
}
public class Student implements Cloneable {
// 对象引用
private Subject subj;
private String name;
public Student(String name, String subj) {
this.name = name;
this.subj = new Subject(subj);
}
public Subject getSubj() {
return subj;
}
public String getName() {
return name;
}
public void setName(String s) {
name = s;
}
/**
* 重写clone()方法
* @return
*/
public Object clone() {
//浅拷贝
try {
// 直接调用父类的clone()方法
// super.clone()其实是浅拷贝
return super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
}
public class Test {
public static void main(String[] args) {
// 原始对象
Student stud = new Student("杨充", "潇湘剑雨");
System.out.println("原始对象: " + stud.getName() + " - " + stud.getSubj().getName());
// 拷贝对象
Student clonedStud = (Student) stud.clone();
System.out.println("拷贝对象: " + clonedStud.getName() + " - " + clonedStud.getSubj().getName());
// 原始对象和拷贝对象是否一样:
System.out.println("原始对象和拷贝对象是否一样: " + (stud == clonedStud));
// 原始对象和拷贝对象的name属性是否一样
System.out.println("原始对象和拷贝对象的name属性是否一样: " + (stud.getName() == clonedStud.getName()));
// 原始对象和拷贝对象的subj属性是否一样
System.out.println("原始对象和拷贝对象的subj属性是否一样: " + (stud.getSubj() == clonedStud.getSubj()));
stud.setName("小杨逗比");
stud.getSubj().setName("潇湘剑雨大侠");
System.out.println("更新后的原始对象: " + stud.getName() + " - " + stud.getSubj().getName());
System.out.println("更新原始对象后的克隆对象: " + clonedStud.getName() + " - " + clonedStud.getSubj().getName());
}
}
原始对象: 杨充 - 潇湘剑雨
拷贝对象: 杨充 - 潇湘剑雨
原始对象和拷贝对象是否一样: false
原始对象和拷贝对象的name属性是否一样: true
原始对象和拷贝对象的subj属性是否一样: true
更新后的原始对象: 小杨逗比 - 潇湘剑雨大侠
更新原始对象后的克隆对象: 杨充 - 潇湘剑雨大侠
-
对原始对象stud的"name"属性所做的改变并没有影响到拷贝对象clonedStud,这其实是因为String是不可变对象final,每次修改都 产生一个新的String对象。
-
但是对引用对象subj的"name"属性所做的改变影响到了拷贝对象clonedStud。
实现深拷贝:
- 继续利用clone()方法,既然clone()方法是让我们来重写的,实际上我们可以对其内的引用类型的变量,在进行一次clone().
public class Student implements Cloneable {
// 对象引用
private Subject subj;
private String name;
public Student(String s, String sub) {
name = s;
subj = new Subject(sub);
}
public Subject getSubj() {
return subj;
}
public String getName() {
return name;
}
public void setName(String s) {
name = s;
}
/**
* 重写clone()方法
*
* @return
*/
public Object clone() {
// 深拷贝,创建拷贝类的一个新对象,这样就和原始对象相互独立
Student s = new Student(name, subj.getName());
return s;
}
}
- 序列化(Serialization)这个对象,在反序列化回来,就可以得到这个新的对象,序列化的规则由我们自己来写。
public class Address {
private String city;
private String country;
}
public class User {
private String name;
private Address address;
}
@org.junit.Test
public void serializableCopy() {
Address address = new Address("杭州", "中国");
User user = new User("大山", address);
System.out.println("user: " + user);
// 使用序列化进行深拷贝
Gson gson = new Gson();
User copyUser = gson.fromJson(gson.toJson(user), User.class);
System.out.println("copyUser: " + copyUser);
// 修改源对象的值
user.getAddress().setCity("深圳");
System.out.println("user: " + user);
System.out.println("copyUser: " + copyUser);
}
user: User{name='大山', address=Address{city='杭州', country='中国'}}
copyUser: User{name='大山', address=Address{city='杭州', country='中国'}}
user: User{name='大山', address=Address{city='深圳', country='中国'}}
copyUser: User{name='大山', address=Address{city='杭州', country='中国'}}
https://juejin.im/post/5c988a7ef265da6116246d11
https://juejin.im/entry/5bc3db04f265da0aaa053baa