深拷贝:
不仅要复制对象的所有基本数据类型的成员变量值,还要为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,知道该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象图进行拷贝,简单的说,深拷贝对引用数据类型的成员变量的对象图中所有的对象都开辟了内存空间;而浅拷贝只是传递地址指向,新的对象并没有对引用数据类型创建内存空间。
实现深拷贝的实现方法主要有两种:
- 通过重写clone方法来实现深拷贝
与通过重写clone方法实现浅拷贝的基本思路一样,只需要为对象图的每一层的每一个对象都实现Clonneable接口并重写clone方法,最后在最顶层的类的重写的clone方法中调用所有的clone方法即可实现深拷贝。简单的说就是“每一层的每个对象都进行浅拷贝=深拷贝。
package linearList;
public class DeepCopy {
public static void main(String[] args) {
Age a = new Age(20);
Student stu1 = new Student("宁采臣",a,174);
//通过调用重写后的clone方法进行浅拷贝
Student stu2 = (Student)stu1.clone();
System.out.println(stu1.toString());
System.out.println(stu2.toString());
System.out.println();
//尝试修改stu1中的各属性,观察stu2的属性有没有变化
stu1.setName("聂小倩");
//改变age这个引用类型的成员变量的值
a.setAge(18);
//stu1.setaAge(new Age(99)); 使用这种方式修改age属性值的话,stu2是不会跟着改变的。因为创建了一个新的Age类对象而不是改变原对象的实例值
stu1.setLength(157);
System.out.println(stu1.toString());
System.out.println(stu2.toString());
}
}
*/
/*
* 创建年龄类
*//*
class Age implements Cloneable{
//年龄类的成员变量(属性)
private int age;
//构造方法
public Age(int age){
this.age = age;
}
public int getAge(){
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return this.age+"";
}
//重写Object的clone方法
public Object clone(){
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e){
e.printStackTrace();
}
return obj;
}
}
*/
/*
* 创建学生类
*//*
class Student implements Cloneable{
//学生类的成员变量(属性),其中一个属性为类的对象
private String name;
private Age aage;
private int length;
//构造方法,其中一个参数为另一个参数的对象
public Student(String name,Age a,int length){
this.name = name;
this.aage = a;
this.length = length;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Age getAge() {
return this.aage;
}
public void setAge(Age age) {
this.aage = age;
}
public int getLength() {
return this.length;
}
public void setLength(int length) {
this.length = length;
}
public String toString(){
return "姓名是:"+this.getName()+",年龄为:"+this.getAge().toString()+",长度是:"+this.getLength();
}
//重写Object类的clone方法
public Object clone(){
Object obj = null;
//调用Object类的clone方法---浅拷贝
try {
obj = super.clone();
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
//调用Age类的clone方法进行深拷贝
//先将obj转化为学生类实例
Student stu = (Student)obj;
//学生类实例的Age对象属性,调用其clone方法进行拷贝
stu.aage=(Age)stu.getAge().clone();
return obj;
}
}
分析结果可以验证:进行了深拷贝之后,无论是什么类型的属性值的修改,都不会影响另一个对象的属性值。
- 通过对象序列化实现深拷贝
虽然层次调用clone方法可以实现深拷贝,但是显然代码量实在太大。特别对于属性数量比较多、层次比较深的类而言,每个类都要重写clone方法太过繁琐。
将对象序列化为字节序列后,默认会将该对象的整个对象图进行序列化,再通过反序列即可完美地实现深拷贝。
package linearList;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class DeepCopyBySerialization {
public static void main(String[] args) throws IOException,ClassNotFoundException{
Age a = new Age(20);
Student stu1 = new Student("宁采臣",a,174);
//通过序列化方法实现深拷贝
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(stu1);
oos.flush();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
Student stu2 = (Student)ois.readObject();
System.out.println(stu1.toString());
System.out.println(stu2.toString());
System.out.println();
//尝试修改stu1中的各属性,观察stu2的属性有没有变化
stu1.setName("聂小倩");
//改变age这个引用类型的成员变量的值
a.setAge(18);
stu1.setLenght(156);
System.out.println(stu1.toString());
System.out.println(stu2.toString());
}
}
/*
* 创建年龄类
* */
class Age implements Serializable{
//年龄类的成员变量(属性)
private int age;
//构造方法
public Age(int age) {
this.age=age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return this.age+"";
}
}
/*
* 创建学生类
* */
class Student implements Serializable{
//学生类的成员变量(属性),其中一个属性为类的对象
private String name;
private Age aage;
private int length;
//构造方法,其中一个参数为另一个类的对象
public Student(String name,Age a,int length){
this.name = name;
this.aage = a;
this.length = length;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Age getaAge() {
return this.aage;
}
public void setaAge(Age age) {
this.aage = age;
}
public int getLength() {
return this.length;
}
public void setLenght(int lenght) {
this.length = lenght;
}
//设置输出的字符串形式
public String toString() {
return "姓名是: "+this.getName()+", 年龄为: "+this.getaAge().toString()+", 长度是: "+this.getLength();
}
}
可以通过很简洁的代码即可完美实现深拷贝。不过要注意的是,如果某个属性被transient修饰,那么该属性就无法被拷贝了。