等号赋值
在 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; } }