java深拷贝与浅拷贝

在正式的进入主题之前,我们先来了解下深拷贝和前拷贝的概念:

浅拷贝:会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝,如果属性是基本类型,拷贝的是基本类型的值;如果属性是内存地址,拷贝的就是内存地址,因此如果一个对象改变了这个地址就会影响到另一个对象;

深拷贝:不仅要复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建新的实例,并且初始化为形式参数实例值;
了解完概念之后,我们来测试下普通的对象赋值操作属于深拷贝还是浅拷贝:
测试代码:
public class DepthCopy {
	public static void main(String[] args) {
		Copy first = new Copy("hzw", 24);
		Copy second = first;
		second.name = "shanxi";
		System.out.println(first.name);//输出shanxi
	}
}
class Copy
{
	public String name;
	public int age;
	public Copy(String name,int age) {
		this.name = name;
		this.age = age;
	}
}
可以发现,在second将name属性值修改为shanxi之后,first的name属性值也变成了shanxi,这点就可以看出普通的对象赋值属于浅拷贝;
明白了对象之间赋值是浅拷贝之后,接下来我们来看看克隆到底是深拷贝还是浅拷贝,测试代码是让上面的Copy对象实现Cloneable接口里面的clone方法:
public class DepthCopy {
	public static void main(String[] args) {
		Copy first = new Copy("hzw", 24);
		Copy second = null;
		try {
			second = (Copy) first.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		second.name = "shanxi";
		System.out.println(first.name);//输出: hzw
		System.out.println(first);//输出: com.hzw.day33.Copy@7f39ebdb
		System.out.println(second);//输出: com.hzw.day33.Copy@33abb81e
	}
}
class Copy implements Cloneable
{
	public String name;
	public int age;
	public Copy(String name,int age) {
		this.name = name;
		this.age = age;
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}
可以看出原先创建出的对象first和克隆创建出的对象second是两个实例,因此对于second中name属性的修改并不会影响first中的name属性;
但是,我们并不能单纯的认为克隆就是深拷贝的,比如下面这个例子:
public class DepthCopy {
	public static void main(String[] args) {
		Student student = new Student(95);
		Copy first = new Copy("hzw", 24,student);
		Copy second = null;
		try {
			second = (Copy) first.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		second.name = "shanxi";
		second.student.score = 60;
		System.out.println(first == second);//false
		System.out.println(first.student == second.student);//true
		System.out.println(first.student.score);//60
	}
}
class Copy implements Cloneable
{
	public String name;
	public int age;
	public Student student;
	public Copy(String name,int age,Student student) {
		this.name = name;
		this.age = age;
		this.student = student;
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}
class Student 
{
	public int score;
	public Student(int score) {
		this.score = score;
	}
}
看到没有呢?我们通过克隆的方式创建了second,很明显发现first和second是两个实例,因为first == second输出为false,但是first和second里面的student对象却是一样的,通过second修改了student的score值之后,first里面student的score也发生了改变,这也就是说first和second里面的student是相同的,这也就说明了克隆是浅拷贝的,我们要想实现克隆的深拷贝,必须让Copy对象里面的Student对象也要实现Cloneable接口里面的clone方法,并且在Copy里面的克隆方法返回Student的一个克隆即可,这样就可以保证Student的唯一啦,修改之后的代码如下:
public class DepthCopy {
	public static void main(String[] args) {
		Student student = new Student(95);
		Copy first = new Copy("hzw", 24,student);
		Copy second = null;
		try {
			second = (Copy) first.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		second.name = "shanxi";
		second.student.score = 60;
		System.out.println(first == second);//false
		System.out.println(first.student == second.student);//false
		System.out.println(first.student.score);//95
		System.out.println(second.student.score);//60
	}
}
class Copy implements Cloneable
{
	public String name;
	public int age;
	public Student student;
	public Copy(String name,int age,Student student) {
		this.name = name;
		this.age = age;
		this.student = student;
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		Copy copy = (Copy)super.clone();
		copy.student = (Student) student.clone();
		return copy;
	}
}
class Student implements Cloneable
{
	public int score;
	public Student(int score) {
		this.score = score;
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}
可以看到此时first和second和first.student和second.student都不是相同的,因此我们修改second的student的score之后并没有影响到first里的student的score值,达到了深拷贝的目的;
但是,仔细一想问题就出来了,假如我们上面例子的Student类中也存在引用类型的属性,比如College类,那么我们必须让College类实现Cloneable接口,然后在Student类里面的clone方法里面调用College类的clone方法,在Copy类的clone方法中调用Student类的clone方法,发现没有了,这个过程好复杂,必须让类中的有关引用类型全部实现Cloneable接口,感觉好麻烦是不是,好的,接下来就该牛人登场了;
解决深拷贝问题最好的方式就是采用序列化方式,这样各种类均不用实现Cloneable接口的,直接序列化反序列化就可以啦,我们来见识下吧。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class DepthCopy {
	public static void main(String[] args) {
		College school = new College("nongda");
		Student student = new Student(95, school);
		Copy copy = new Copy("hzw",23, student);
		Copy another = null;//表示反序列化出来的类实例
		//进行序列化操作
		try {
			FileOutputStream fos = new FileOutputStream(new File("d:/copy.txt"));
			ObjectOutputStream oos = new ObjectOutputStream(fos);
			oos.writeObject(copy);
		} catch (Exception e) {
			e.printStackTrace();
		}
		//进行反序列化操作
		FileInputStream fis;
		try {
			fis = new FileInputStream(new File("d:/copy.txt"));
			ObjectInputStream ois = new ObjectInputStream(fis);
			another = (Copy) ois.readObject();
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println(copy == another);//false
		System.out.println(copy.student == another.student);//false
		System.out.println(copy.student.school == another.student.school);//false
		another.student.school.schoolName = "wuda";
		System.out.println(copy.student.school.schoolName);//nongda
	}
}
class Copy implements Serializable
{
	public String name;
	public int age;
	public Student student;
	public Copy(String name,int age,Student student) {
		this.name = name;
		this.age = age;
		this.student = student;
	}
}
class Student implements Serializable
{
	public int score;
	public College school;
	public Student(int score,College school) {
		this.score = score;
		this.school = school;
	}
}
class College implements Serializable
{
	public String schoolName;
	public College(String schoolName) {
		this.schoolName = schoolName;
	}
}
从输出就可以看出来,反序列化之后生成的对象完全就是对原对象的一份拷贝,除了属性值相同之外并不和原对象有任何关系,因此当我们修改反序列化生成对象的schoolName为"wuda"的时候并没有修改原来实例的schoolName值,还是输出"nongda",因此达到了真正的深拷贝效果,但是要想实现序列化,所有的有关类都必须实现Serializable接口,这总也比既实现Cloneable接口又实现clone方法更方便吧;





  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 中,深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是用于描述对象拷贝的概念。 浅拷贝是指创建一个新对象,新对象的属性值和原对象的属性值相同,但是对于引用类型的属性,新旧对象将共享同一个引用。也就是说,新旧对象的引用类型属性指向同一个内存地址。 深拷贝则是在拷贝对象时,不仅会复制对象本身,还会递归复制对象的引用类型属性,使得新对象和原对象的引用类型属性指向不同的内存地址。 Java 中的默认拷贝行为是浅拷贝。可以通过以下两种方式实现深拷贝: 1. 实现 `Cloneable` 接口并重写 `clone()` 方法:这是一种较为简单的实现深拷贝的方式。在需要进行深拷贝的类中,实现 `Cloneable` 接口,并重写 `clone()` 方法,在 `clone()` 方法中递归复制引用类型的属性。 ```java class MyClass implements Cloneable { private int number; private MyObject myObject; // constructors, getters, setters @Override protected Object clone() throws CloneNotSupportedException { MyClass cloned = (MyClass) super.clone(); cloned.myObject = (MyObject) myObject.clone(); // 实现 MyObject 的 clone() 方法 return cloned; } } ``` 通过调用 `clone()` 方法来创建一个新的深拷贝对象:`MyClass clonedObject = (MyClass) originalObject.clone();` 2. 使用序列化和反序列化:这是另一种实现深拷贝的方式。通过将对象序列化为字节流,然后再将字节流反序列化为新的对象,可以实现深拷贝。这种方式需要确保对象及其引用类型属性都是可序列化的。 ```java import java.io.*; class MyClass implements Serializable { // class definition } ``` ```java // 实现深拷贝的方法 public static <T extends Serializable> T deepCopy(T object) throws IOException, ClassNotFoundException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); objectOutputStream.writeObject(object); ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); return (T) objectInputStream.readObject(); } ``` 通过调用 `deepCopy()` 方法来创建一个新的深拷贝对象:`MyClass clonedObject = deepCopy(originalObject);` 需要注意的是,为了实现深拷贝,引用类型的类也需要实现 `Cloneable` 接口并重写 `clone()` 方法,或者是可序列化的。 希望这些信息对你有所帮助。如果你还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值