等号赋值
在 Java 中,除了基本数据类型(数值)之外,还存在 类的实例对象 这个引用数据类型。而一般使用“ = ”号做赋值操作的时候,对于基本数据类型,拷贝的它的值。对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去,他们实际上还是指向的同一个对象。
下面来看例子:
输出对应的信息:
直接使用等号=进行赋值操作,可以看出两个对象的hashcode码是相同的,代表他们指向同一个对象的地址,改变其中一个对象的属性值,另一个对象的值也会跟着改变
浅拷贝和深拷贝
浅拷贝和深拷贝就是在这个基础之上做的区分
对基本数据类型进行拷贝,对引用数据类型进行引用传递,没有创建一个新的对象,则认为是浅拷贝。
反之,对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝
浅拷贝
JAVA中的所有类都继承自Object类,在Object类中存在一个clone()方法,被声明为protect(只允许在子类中调用)。
只要子类实现了Cloneable()接口,就可以直接调用该方法。clone()方法可以在拷贝过程中生成一个新的对象实例,实现浅拷贝操作
下面来看例子:
运行结果:
直接通过实现父类的clone()方法就可以实现对象的浅拷贝,从hashcode可以看出,拷贝过程生成了两个不同的对象
但是!
问题出在了这一行
我们在Father类中新建了一个Child实例作为该类的成员,那么在执行同样的浅拷贝操作之后,会生成两个不同的父类+两个不同的子类吗?
不会! 通过hashcode可以看出,拷贝生成了两个不同的父类,但是子类依然指向了同一个对象的地址,这就是”浅拷贝“名称的由来,只能进行浅层(一层)对象的拷贝。那么什么是深拷贝呢?
深拷贝
两种方法:1、重写子类对象的Clone()方法实现深拷贝2、使用序列化和反序列化进行深拷贝操作
1、重写子类对象的Clone()方法实现深拷贝
简单来说就是对Father类中的child子类 ,再进行了一次 clone() 操作
对于Child()子类来说,这是一次浅拷贝,但是对于Father()类来说,就是一次深拷贝
输出结果:
2、使用序列化和反序列化进行深拷贝操作
把对象写到流里的过程是序列化过程(Serialization),而把对象从流中读出来的过程则叫做反序列化过程(Deserialization)
在Java中实现深拷贝操作,首先先实现Serializable接口,然后把对象写到一个字节流里,再从字节流里读出来,便可以重建对象。该新对象与旧对象之间并不存在引用共享的问题,从而真正实现了对象的深拷贝
来看运行结果:
通过hashCode可以看到该操作生成了不同的父类和子类,的确实现了深拷贝
对于更深层次的子类,只要在最外层父类中实现序列化和反序列化操作,并在每一层的子类中实现serializable接口,就可以方便地完成深拷贝操作!
By the way,代码中还进行了子类的Name属性修改操作,所以会生成不同的名字
附测试代码:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class CloneTest3 {
// 通过序列化的方式实现对象的深拷贝
public static void main(String[] args) throws Exception {
Father f1 = new Father();
f1.setName("张三");
f1.child.setName("张小三");
//Father f2 =f1;
//Father f2 = (Father) f1.clone();//实现Cloneable接口实现浅拷贝&重写子类对象的Cloneable接口实现深拷贝
Father f2 = (Father) f1.deepClone();//使用序列化和反序列化进行深拷贝操作
// 将复制后的对象信息修改一下
f2.setName("李四");
System.out.println("原来对象的姓名:" + f1.getName());
System.out.println("原来对象的hashCode:" + f1.hashCode());
System.out.println("拷贝对象的姓名:" + f2.getName());
System.out.println("拷贝对象的hashCode:" + f2.hashCode());
// 将复制后的对象的孩子信息修改一下
f2.child.setName("李小四");
System.out.println("原来对象的孩子姓名:" + f1.child.getName());
System.out.println("原来对象的孩子hashCode:" + f1.child.hashCode());
System.out.println("拷贝对象的孩子姓名:" + f2.child.getName());
System.out.println("拷贝对象的孩子hashCode:" + f2.child.hashCode());
}
}
///*****************Cloneable接口实现浅拷贝*****************/
//class Father implements Cloneable{//实现Cloneable接口
// public String name;
// public Child child = new Child();
//
// public String getName() {
// return name;
// }
//
// public void setName(String name) {
// this.name = name;
// }
//
// public Object clone(){
// try{
// //浅拷贝
// return super.clone();
// } catch (CloneNotSupportedException ignore){
// }
// return null;
// }
//}
//
//class Child implements Cloneable {
// private String name;
//
// public String getName() {
// return name;
// }
//
// public void setName(String name) {
// this.name = name;
// }
//}
///*****************重写子类对象的Cloneable接口实现深拷贝*****************/
//class Father implements Cloneable{
// public String name;
// public Child child = new Child();
//
// public String getName()
// {
// return name;
// }
//
// public void setName(String name)
// {
// this.name = name;
// }
//
// public Object clone(){
// try{
// //深拷贝
// Father cloneFather = (Father) super.clone();
// cloneFather.child = (Child) this.child.clone();
// return cloneFather;
// } catch (CloneNotSupportedException ignore){
// }
// return null;
// }
//}
//
//class Child implements Cloneable {
// private String name;
//
// public String getName() {
// return name;
// }
//
// public void setName(String name) {
// this.name = name;
// }
//
// public Object clone(){
// try{
// //重写clone()方法是实现深拷贝
// return super.clone();
// } catch (CloneNotSupportedException ignore){
// }
// return null;
// }
//}
/*****************使用序列化和反序列化进行深拷贝操作*****************/
class Father implements Serializable
{
private String name;
public Child child = new Child();//在Father类中,还有一个Child类的对象child
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object deepClone() throws Exception {
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}
class Child implements Serializable
{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}