Java浅拷贝和深拷贝

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/peerless_hero/article/details/53861927

浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。
深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。

举例来说:
对象A1中包含对B1的引用,B1中包含对C1的引用。
浅拷贝对象A1得到A2,A2 中依然包含对B1的引用,B1中依然包含对C1的引用。
深拷贝则是对浅拷贝的递归,深拷贝A1得到A2,A2中包含对B2(B1的copy)的引用,B2 中包含对C2(C1的copy)的引用。

浅拷贝

class Professor implements Cloneable {
	String name;
	int age;

	Professor(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

class Student implements Cloneable {
	String name;
	int age;
	Professor p;

	Student(String name, int age, Professor p) {
		this.name = name;
		this.age = age;
		this.p = p;
	}

	public Object clone() {
		Student o = null;
		try {
			// 仅仅拷贝对象本身(包括对象中的基本变量)!!!
			o = (Student) super.clone();
		} catch (CloneNotSupportedException e) {
			System.out.println(e.toString());
		}

		return o;
	}
}

public class ShallowCopy {
	public static void main(String[] args) {
		Professor p = new Professor("Prof.Chen", 36);
		Student s1 = new Student("rookie", 18, p);
		System.out.println("学生s1的姓名:" + s1.name + "\n学生s1教授的姓名:" + s1.p.name
				+ "," + "\n学生s1教授的年纪" + s1.p.age);
		System.out.println("<------------------------>");
		Student s2 = (Student) s1.clone();
		s2.p.name = "Prof.Yang";
		s2.p.age = 30;
		s2.name = "newbie";
		s2.age = 10;
		System.out.println("学生s1的姓名:" + s1.name + "\n学生s1教授的姓名:" + s1.p.name
				+ "," + "\n学生s1教授的年纪" + s1.p.age);
	}
}

运行结果是:
学生s1的姓名:rookie
学生s1教授的姓名:Prof.Chen,
学生s1教授的年纪36
<------------------------>
学生s1的姓名:rookie
学生s1教授的姓名:Prof.Yang,
学生s1教授的年纪30

我们发现:s2的修改导致了s1的同步变化,这就证明了s1的p和s2的p指向的是同一个对象。这就是"浅拷贝"。
显然"浅拷贝"不能让我们满意。我们再看下"深拷贝"如何实现。

深拷贝

class Professor implements Cloneable {
	String name;
	int age;

	Professor(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

class Student implements Cloneable {
	String name;
	int age;
	Professor p;

	Student(String name, int age, Professor p) {
		this.name = name;
		this.age = age;
		this.p = p;
	}

	public Object clone() {
		Student o = null;
		try {
			// 仅仅拷贝对象本身(包括对象中的基本变量)!!!
			o = (Student) super.clone();
			// 和ShallowCopy类唯一的变化就是增加了下面这行代码,拷贝对象包含的引用指向的所有对象!!!
			o.p = (Professor) p.clone();
		} catch (CloneNotSupportedException e) {
			System.out.println(e.toString());
		}

		return o;
	}
}

public class DeepCopy {
	public static void main(String[] args) {
		Professor p = new Professor("Prof.Chen", 36);
		Student s1 = new Student("rookie", 18, p);
		System.out.println("学生s1的姓名:" + s1.name + "\n学生s1教授的姓名:" + s1.p.name
				+ "," + "\n学生s1教授的年纪" + s1.p.age);
		System.out.println("<------------------------>");
		Student s2 = (Student) s1.clone();
		s2.p.name = "Prof.Yang";
		s2.p.age = 30;
		s2.name = "newbie";
		s2.age = 10;
		System.out.println("学生s1的姓名:" + s1.name + "\n学生s1教授的姓名:" + s1.p.name
				+ "," + "\n学生s1教授的年纪" + s1.p.age);
	}
}

运行结果是:
学生s1的姓名:rookie
学生s1教授的姓名:Prof.Chen,
学生s1教授的年纪36
<------------------------>
学生s1的姓名:rookie
学生s1教授的姓名:Prof.Chen,
学生s1教授的年纪36

我们发现:s2的修改并未导致s1的同步变化,这就是"深拷贝",不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象!

深拷贝需要注意:
1、没有必要单独复制primitive
2、原生对象中的所有类成员都必须支持clone方法,在原生对象的成员类中都要调用super.clone()方法
3、如果类成员变量不支持clone,那么它必须创建那个成员类的新的实例,并把它的属性一个一个的复制到新的对象中去,新的类成员对象在clone对象中被设置。

利用序列化实现深拷贝

深拷贝一个对象,还可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里(腌成咸菜),再从流里读出来(把咸菜回鲜),便可以重建对象。应当指出的是,写入流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。代码如下:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.io.Serializable;

public class DeepCopy implements Serializable {

	private static final long serialVersionUID = -1889147729394971115L;

	class Professor implements Serializable {

		private static final long serialVersionUID = -5461233829245479349L;

		String name;
		int age;

		Professor(String name, int age) {
			this.name = name;
			this.age = age;
		}
	}

	class Student implements Serializable {

		private static final long serialVersionUID = -851210316322914041L;

		String name;
		int age;
		Professor p;

		Student(String name, int age, Professor p) {
			this.name = name;
			this.age = age;
			this.p = p;
		}

		public Object deepClone() throws IOException, OptionalDataException,
				ClassNotFoundException {
			// 将对象写到流里
			ByteArrayOutputStream bo = new ByteArrayOutputStream();
			ObjectOutputStream oo = new ObjectOutputStream(bo);
			oo.writeObject(this);// object of student
			// 从流里读出来
			ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
			ObjectInputStream oi = new ObjectInputStream(bi);
			return (oi.readObject());
		}
	}

	public static void main(String[] args) {
		DeepCopy deepCopy = new DeepCopy();
		DeepCopy.Professor p = deepCopy.new Professor("Prof.Chen", 36);
		DeepCopy.Student s1 = deepCopy.new Student("rookie", 18, p);
		System.out.println("学生s1的姓名:" + s1.name + "\n学生s1教授的姓名:" + s1.p.name
				+ "," + "\n学生s1教授的年纪" + s1.p.age);
		System.out.println("<------------------------>");
		try {
			Student s2 = (Student) s1.deepClone();
			s2.p.name = "Prof.Yang";
			s2.p.age = 30;
			s2.name = "newbie";
			s2.age = 10;
			System.out.println("学生s1的姓名:" + s1.name + "\n学生s1教授的姓名:"
					+ s1.p.name + "," + "\n学生s1教授的年纪" + s1.p.age);
		} catch (OptionalDataException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

运行结果是:
学生s1的姓名:rookie
学生s1教授的姓名:Prof.Chen,
学生s1教授的年纪36
<------------------------>
学生s1的姓名:rookie
学生s1教授的姓名:Prof.Chen,
学生s1教授的年纪36

我们发现:s2的修改并未导致s1的同步变化,利用序列化也可以实现"深拷贝"!

要注意,慎用序列化方法实现深拷贝。第一、序列化有高昂的代价,它可能是clone方法的上百倍;第二、并非所有的对象都可以序列化。

SerializationUtils

apche commons也提供了实现深复制的静态方法,本质上也是使用的序列化实现的。

package org.apache.commons.lang;

public class SerializationUtils {
    public static Object clone(Serializable object) {
        return deserialize(serialize(object));
    }

    public static Object deserialize(byte[] objectData) {
        if (objectData == null) {
            throw new IllegalArgumentException("The byte[] must not be null");
        } else {
            ByteArrayInputStream bais = new ByteArrayInputStream(objectData);
            return deserialize((InputStream)bais);
        }
    }
    
    public static byte[] serialize(Serializable obj) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
        serialize(obj, baos);
        return baos.toByteArray();
    }
展开阅读全文

没有更多推荐了,返回首页