0
点赞
收藏
分享

微信扫一扫

java的深拷贝与浅拷贝


文章目录

  • ​​什么是浅拷贝和深拷贝​​
  • ​​Java 中的 clone()​​
  • ​​Object 上的 clone() 方法​​
  • ​​浅拷贝举例​​
  • ​​深拷贝举例​​
  • ​​总结​​

什么是浅拷贝和深拷贝

首先需要明白,浅拷贝和深拷贝都是针对一个已有对象的操作。那先来看看浅拷贝和深拷贝的概念。

在 Java 中,除了基本数据类型(元类型)之外,还存在 类的实例对象 这个引用数据类型。而一般使用 ​​=​​ 号做赋值操作的时候。对于基本数据类型,实际上是拷贝的它的值,但是对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去,他们实际上还是指向的同一个对象。

而浅拷贝和深拷贝就是在这个基础之上做的区分,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。

所以到现在,就应该了解了,所谓的浅拷贝和深拷贝,只是在拷贝对象的时候,对 类的实例对象 这种引用数据类型的不同操作而已。

总结来说:

1、浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
java的深拷贝与浅拷贝_深拷贝
2、深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。java的深拷贝与浅拷贝_引用数据类型_02

Java 中的 clone()

Object 上的 clone() 方法

在 Java 中,所有的 Class 都继承自 Object ,而在 Object 上,存在一个 ​​clone()​​ 方法,它被声明为了 protected ,所以我们可以在其子类中,使用它。

而无论是浅拷贝还是深拷贝,都需要实现 ​​clone()​​ 方法,来完成操作。

它的实现非常的简单,它限制所有调用 ​​clone()​​​ 方法的对象,都必须实现 ​​Cloneable​​​ 接口,否者将抛出 ​​CloneNotSupportedException​​​ 这个异常。最终会调用 ​​internalClone()​​​ 方法来完成具体的操作。而 ​​internalClone()​​​ 方法,实则是一个 native 的方法。对此我们就没必要深究了,只需要知道它可以 ​​clone()​​ 一个对象得到一个新的对象实例即可

而 ​​Cloneable​​ 接口,它什么方法都不需要实现。对他可以简单的理解只是一个标记,是开发者允许这个对象被拷贝

浅拷贝举例

首先创建一个 class 为 FatherClass ,对其实现 ​​Cloneable​​​ 接口,并且重写 ​​clone()​​ 方法。

public class FatherClass implements Cloneable{
public String name;
public int age;
public ChildClass child;

@NonNull
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

public class ChildClass {
public String name;
public int age;
}

然后先正常 new 一个 FatherClass 对象,再使用 clone() 方法创建一个新的对象

FatherClass fatherClassA = new FatherClass();
fatherClassA.name = "正常张三";
fatherClassA.age = 30;
fatherClassA.child = new ChildClass();
fatherClassA.child.name = "法外狂徒张三";
fatherClassA.child.age = 18;

try {
FatherClass fatherClassB = (FatherClass) fatherClassA.clone();
Log.d("TTT", "fatherClassA == fatherClassB:" + (fatherClassA == fatherClassB));
Log.d("TTT", "fatherClassA hash:" + fatherClassA.hashCode());
Log.d("TTT", "fatherClassB hash:" + fatherClassB.hashCode());
Log.d("TTT", "fatherClassA name:" + fatherClassA.name);
Log.d("TTT", "fatherClassB name:" + fatherClassB.name);
Log.d("TTT", "===============");
Log.d("TTT", "A.child == B.child:" + (fatherClassA.child == fatherClassB.child));
Log.d("TTT", "fatherA.child hash:" + fatherClassA.child.hashCode());
Log.d("TTT", "fatherB.child hash:" + fatherClassB.child.hashCode());

} catch (CloneNotSupportedException e) {
e.printStackTrace();
}

最后看看输出的 Log :

fatherClassA == fatherClassB:false
fatherClassA hash:156329482
fatherClassB hash:175877755
fatherClassA name:正常张三
fatherClassB name:正常张三
===============
A.child == B.child:true
fatherA.child hash:66385048
fatherB.child hash:66385048

分割线之前,使用 ​​clone()​​​ 方法,从 ​​==​​​ 和 ​​hashCode​​​ 的不同可以看出,​​clone()​​ 方法实则是真的创建了一个新的对象。

但这只是一次浅拷贝的操作。

对 child 的输出可以看到,A 和 B 的 child 对象,实际上还是指向了统一个对象,只是对它的引用进行了传递,所以这是一次浅拷贝

深拷贝举例

那么,如何进行一个深拷贝呢?
比较常用的方案有两种:
1、序列化(​​​serialization​​​)这个对象,再反序列化回来,就可以得到这个新的对象,无非就是序列化的规则需要我们自己来写。
2、继续利用 ​​​clone()​​ 方法,既然 clone() 方法,是我们来重写的,实际上我们可以对其内的引用类型的变量,再进行一次 clone()。

继续改写上面的 Demo ,让 ChildClass 也实现 Cloneable 接口。

public class ChildClass implements Cloneable{
public String name;
public int age;

@NonNull
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

然后修改 FatherClass

public class FatherClass implements Cloneable{
public String name;
public int age;
public ChildClass child;

@NonNull
@Override
protected Object clone() throws CloneNotSupportedException {
FatherClass fatherClass = (FatherClass)super.clone();
fatherClass.child = (ChildClass)this.child.clone();
return fatherClass;
}
}

最重要的代码就在 FatherClass.clone() 中,它对其内的 child ,再进行了一次 clone() 操作。

再看log

fatherClassA == fatherClassB:false
fatherClassA hash:156329482
fatherClassB hash:175877755
fatherClassA name:正常张三
fatherClassB name:正常张三
===============
A.child == B.child:false
fatherA.child hash:66385048
fatherB.child hash:123905265

可以看到,对 child 也进行了一次拷贝,这实则是对 ChildClass 进行的浅拷贝,但是对于 FatherClass 而言,则是一次深拷贝。

其实深拷贝的思路都差不多,序列化也好,使用 clone() 也好,实际上都是需要我们自己来编写拷贝的规则,最终实现深拷贝的目的。

如果想要实现深拷贝,推荐使用 clone() 方法,这样只需要每个类自己维护自己即可,而无需关心内部其他的对象中,其他的参数是否也需要 clone() 。

总结

到现在基本上就已经梳理清楚,Java 中浅拷贝和深拷贝的概念了。

实则浅拷贝和深拷贝只是相对的,如果一个对象内部只有基本数据类型,那用 ​​clone()​​​ 方法获取到的就是这个对象的深拷贝,而如果其内部还有引用数据类型,那用 ​​clone()​​ 方法就是一次浅拷贝的操作。

细说 Java 的深拷贝和浅拷贝

举报

相关推荐

0 条评论