Java中深拷贝和浅拷贝的研究
以这个问题开篇吧:String类型的拷贝到底是浅拷贝还是深拷贝???
准备说写一个程序来测试下,发现String没有实现Cloneable接口,也没有重写clone方法
String str1="wuanghao";
//String str2=str1.clone();//报错
String str3=str1;//复制引用
String不是基本数据类型,String也没有实现Cloneable接口,也就是说只能复制引用。
那么在修改克隆之后的对象str3之后,会不会将原来的值str1也改变了?答案是不会改变
public class StringCopy {
public static void main(String [] args){
String str1="wuanghao";
//String str2=str1.clone();
String str2=str1;
str2=str2+"wo";
System.out.println(str1);
}
}
用以上代码测试,对象str1是没有被改变的。
原因或许大家都知道
因为String是在内存中不可以被改变的对象,就比如说在for大量循环中不推荐使用+的方式来拼凑字符串一样,每次使用+都会新分配一块内存,不在原来上修改,原来的没有指向它的引用,会被回收。所以克隆相当于1个String内存空间有两个引用,当修改其中的一个值的时候,会新分配一块内存用来保存新的值,这个引用指向新的内存空间,原来的String因为还存在指向他的引用,所以不会被回收,这样,虽然是复制的引用,但是修改值的时候,并没有改变被复制对象的值。
就是因为:虽然是复制的引用,但是修改值的时候,并没有改变被复制对象的值
所以在很多情况下,我们可以把String在clone的时候和基本类型做相同的处理,只是在equal时注意一些就行了。
这样关于上面问题的答案就是:String类型是浅拷贝,但是一般把它当深拷贝对待。
1.浅拷贝与深拷贝概念
(1)浅拷贝(浅克隆)
浅拷贝又叫浅复制,将对象中的所有字段复制到新的对象(副本)中。其中,值类型字段(java中8中原始类型)的值被复制到副本中后,在副本中的修改不会影响到源对象对应的值。而引用类型的字段被复制到副本中的还是引用类型的引用,而不是引用的对象,在副本中对引用类型的字段值做修改会影响到源对象本身。
浅拷贝简单归纳就是只复制一个对象,对象内部存在指向其他对象,数组或引用则不复制。
(2)深拷贝(深克隆)
将对象中的所有字段复制到新的对象中。不过,无论是对象的值类型字段,还是引用类型字段,都会被重新创建并赋值,对于副本的修改,不会影响到源对象本身。
深拷贝简单归纳就是对象内部引用的对象均复制。
2.Java中的clone()方法
(1)clone()方法将对象复制了一份并返回给调用者。一般而言,clone()方法满足下面规范:
①对任何的对象x,都有x.clone() != x;//克隆对象与原对象不是同一个对象
②对任何的对象x,都有x.clone().getClass()== x.getClass();//克隆对象与原对象的类型一样
③如果对象x的equals()方法定义恰当,那么x.clone().equals(x);应该成立。
(2)Java中对象的克隆
clone()方法是在Object中定义的,而且是protected的,只有实现了Cloneable接口的类才可以在其实例上调用clone()方法,否则会抛出CloneNotSupportException。
为了获取对象的一份拷贝,我们可以利用Object类的clone()方法,也可以实现Cloneable接口,覆盖基类的clone()方法,在clone()方法中,调用super.clone()。
Cloneable接口是一个标记接口,也就是没有任何内容,定义如下:
package java.lang;
pubilc interface Cloneable{
}
例子1:对象中只含基本数据类型(完成的是深拷贝)
Student类:只含基本数据类型和Sting类型
package com.wrh.copy;
//将一个类实现Cloneable接口,并将Object中clone()方法的权限Protected换成public
//这个类的对象就可以被拷贝
public class Student implements Cloneable{