Java对象的复制:
1. 浅克隆
拷⻉对象和原始对象的引⽤类型引用同⼀个对象。
方法1:直接赋
Student stu1=new Student();
Student stu2=new Student();
stu2=stu1;
解读:
stu1用new开辟了空间,"stu2=stu1;"意味着把stu1的所指对象的地址传输给stu2;即二者指向同一个对象,所以之后如果改变stu2所指的内容,stu1也改变了。
方法2:重写父类Object的clone()方法–implement cloneable
@Override
public Student clone() throws CloneNotSupportedException {
return (Student) super.clone();
}
2. 深克隆
开辟了一片空间,该空间存的内容与要被拷贝的对象的内容完全一致,新的克隆的引用指向这个新的空间,所以当改变这个空间的内容时,原先要被拷贝的对象并没有变化。
即:拷贝对象和原始对象的引用类型引用不同的对象
方法:
A.get,set挨个儿赋值--------->太慢
B.重写父类Object的clone()方法–implement cloneable
假设在Student 的属性里有一个 Address 类
则:
1.Address类实现Cloneable接口,重写clone方法;
@Override
public Address clone() throws CloneNotSupportedException {
return (Address) super.clone();
}
2.在Student类的clone方法中调用Address类的clone方法。
@Override
public Student clone() throws CloneNotSupportedException {
Student student = (Student) super.clone();
student.address = address.clone();
return student;
}
C.序列化implements Serializable
序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable接口
,否则无法实现序列化操作。
序列化中常见的问题:
问题一:static 属性不能被序列化
原因:序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。
问题二:Transient 属性不会被序列化
问题三:序列化版本号serialVersionUID
当我们没定义的时候JDK工具会按照我们对象的属性生成一个对应的版本号。
D. 工具类BeanUtils和PropertyUtils进行对象复制
Student stu1 = new Student();
stu1.setNumber(12345);
Student stu2 = new Student();
BeanUtils.copyProperties(stu2,stu1);
在实际开发中,BeanUtils使用更普遍一点,犯错的风险更低一点。
总结:
- 浅克隆
只复制基本类型的数据,引用类型的数据只复制了引用的地址,引用的对象并没有复制,在新的对象中修改引用类型的数据会影响原对象中的引用。
-------不同引用存的同一对象的地址,即指向同一对象。
- 深克隆
复制后的对象与原对象之间完全不会影响。
-------不同引用存的不同对象的地址,即指向不同对象,即使对象与对象的内容完全相同,但是内存中开辟的空间是不一样的。
- 使用序列化
使用序列化也能完成深复制的功能:对象序列化后写入流中,此时也就不存在引用什么的概念了,再从流中读取,生成新的对象,新对象和原对象之间也是完全互不影响的。
- 深克隆重写方法与浅克隆的关系
使用clone实现的深克隆其实是浅克隆中嵌套了浅克隆,与toString方法类似