1.浅拷贝的介绍
- 1)对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
2)对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
3)浅拷贝是使用默认的 clone()方法来实现
我们来看下面这个例子
新建一个人类,里面有基本数据类型属性name,sex,age和引用数据类型属性friend
public class Person implements Cloneable, {
private String name;
private String sex;
private Integer age;
public Person friend;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
@Override
public Object clone() {
Person person= null;
try {
person = (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return person;
}
测试类
public class NormalClone {
public static void main(String[] args) {
Person p=new Person();
p.setName("mike");
p.setAge(20);
p.setSex("男");
Person friend=new Person();
friend.setName("jeffery");
friend.setAge(19);
friend.setSex("男");
p.friend=friend;
Person p2= (Person)p.clone();
//发现p和p2的friend的hashcode一样,则两个朋友是同一个对象
System.out.println(p.friend.hashCode());
System.out.println(p2.friend.hashCode());
System.out.println("=================");
//改变p的friend的年龄
friend.setAge(21);
//改变p的年龄
p.setAge(22);
//p2的年龄没有变
System.out.println(p);
System.out.println(p2);
//改变p的friend的年龄,p2的friend的值也变了
System.out.println(p.friend+","+p2.friend);
}
}
结果如下:
我们可以看到改变p的friend的年龄,p2的friend的值也变了,改变p的年龄,p2的年龄没有改变。
可以的出结论:
clone方法默认是使用的浅拷贝
2. 深拷贝的介绍
- 1)复制对象的所有基本数据类型的成员变量值
2)为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝
3)深拷贝实现方式 1:重写 clone 方法来实现深拷贝
4)深拷贝实现方式 2:通过对象序列化实现深拷贝(推荐)
代码实现1:通过重写clone方法
public class Person implements Cloneable, Serializable {
private static final long serialVersionUID = -3943427356942430084L;
/**
姓名
*/
private String name;
private String sex;
private Integer age;
public Person friend;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
@Override
public Object clone() {
Person person= null;
try {
person = (Person) super.clone();
if(person.friend!=null) {
person.friend = (Person) friend.clone();
}
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return person;
}
}
结果如下:
可以看出:改变p的朋友的年龄,p2的朋友年龄没有改变,这种方式实现深拷贝是可行的。
但是这样实现有一个问题,假如属性有很多个引用类型,以下这段代码要写很多段。
if(person.friend!=null) {
person.friend = (Person) friend.clone();
}
代码实现2:通过对线的序列化实现(推荐使用这种)**
Person类
public class Person implements Serializable {
private static final long serialVersionUID = -3943427356942430084L;
/**
姓名
*/
private String name;
private String sex;
private Integer age;
public Person friend;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
public Object deepClone(){
ByteArrayInputStream bis=null;
ObjectInputStream ois=null;
ByteArrayOutputStream bos=null;
ObjectOutputStream oos=null;
try {
//序列化
bos=new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//反序列化
bis=new ByteArrayInputStream(bos.toByteArray());
ois=new ObjectInputStream(bis);
Person p= (Person) ois.readObject();
return p;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
测试类
public class DeepClone {
public static void main(String[] args) {
Person p=new Person();
p.setName("mike");
p.setAge(20);
p.setSex("男");
Person friend=new Person();
friend.setName("jeffery");
friend.setAge(19);
friend.setSex("男");
p.friend=friend;
Person p2= (Person)p.deepClone();
//发现p和p2的friend的hashcode不一样,则两个朋友不是同一个对象
System.out.println(p.friend.hashCode());
System.out.println(p2.friend.hashCode());
System.out.println("=================");
//改变p的friend的年龄
friend.setAge(21);
//改变p的年龄
p.setAge(22);
//p2的年龄没有变
System.out.println(p);
System.out.println(p2);
//改变p的friend的年龄,p2的friend的值没有变
System.out.println(p.friend+","+p2.friend);
}
}
结果如下:
这种方式也实现了深拷贝,而且不管有多少个引用类型的属性,代码不会变。