- 前言
拷贝操作,在我们的日常使用电脑的过程中的一种十分常见的情况.但在计算机语言里,拷贝的操作其实是分为两种的.那就是深拷贝与浅拷贝.
而其实在任何编程语言里,都存在着深浅拷贝这两种概念的.java语言也不例外.今天就从java语言出发,记录一下关于深浅拷贝的学习.
- 什么是深浅拷贝
首先我们因当搞明白这两个名词的概念,在讲深浅拷贝之前,我们先来说一说拷贝.什么是拷贝,拷贝干了什么.
以我们直观的理解,拷贝就是复制,就是将一个对象由一份变为两份
但是这样直观的方式的实现,缺失有不同的方式的,那就是深浅拷贝两种不同的方式.
我们一java语言的来理解它们,深浅拷贝必然是针对一个已有的对象进行的操作,而这个操作的目的,或者说实现的东西就如上面所说的直观解释.
我们知道,JAVA语言是一门面向对象的编程语言,它除了基本数据类型之外,还存在着引用数据类型(类的实例对象).而在我们通常使用=来进行赋值操作的时候,对于基本数据类型,实际上是拷贝它的值,而对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去.他们实际上是指向同一个对象的.
而深浅拷贝就是以这为基础而进行区分的即:
- 浅拷贝
浅拷贝在拷贝操作的时候,只对基本数据类型进行拷贝,而在拷贝实例化对象(引用数据类型)的时候,知识进行了引用的传递,而不会新建一个对象来拷贝并保存原对象.实际上他们都指向的是同一个原对象即:.
只对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,则为浅拷贝
- 深拷贝
深拷贝则正好与浅拷贝相反,当在拷贝引用数据类型(实例化对象)时,新建了一个对象,并复制原对象中的所有属性和方法,将原对象保存在新对象中.使原对象和新建对象成为两个相同但完全独立的两个对象.
对基本数据类型进行值传递,对引用数据类型,创建一个新对象,并复制原对象的内容,则为深拷贝
因此,其实在某种意义意义上拷贝操作并不是绝对的,因为他们在对基本数据类型拷贝的时候,操作都是一样的,所以都可称之为深拷贝,或浅拷贝.其主要区别在于后面关键的对象的拷贝上.
并且值得我们注意的是,因为浅拷贝都是指向同一个原对象的,所以它是不会耗费储存空间的,但它具有一定的联动性,当原对象有所改变时,拷贝的对象(其实就是同一个)也一样会改变.(就和桌面快捷方式一样)
而深拷贝会新建对象将原对象完全复制一份保存在新对象中,所以它是需要占用存储空间的.但它的好处就是两者相互不影响,都是相对独立的.(我们经常做的备份工作就是一种深拷贝操作)
- Java中的clone()方法
我们知道在java语言中,所有的类都继承自Object类,在Object类中,存在着一个clone方法.它被声明为protected(同一个类或子类中可用)所以我们可以在子类中使用它.
而无论深浅拷贝,都需要重写clone方法来实现拷贝操作.
我们可以看到该方法十分简单.简单到我基本没怎么明白它的意思.
我们再看看关于该方法的注释信息:
这段注释告诉我们如果调用这个方法的对象没有实现Cloneable接口将会报CloneNotSupportedException异常
.
这段注释的意思我们可以大概明白,表示Object类自身不能实现接口,所以在对象上直接调用该方法时,会抛出一个运行时异常.
我们在看看这段注释,它给我们说明了,这个clone方法是默认的是一个浅拷贝的方法,它会新建一个实例对象并初始化其中的所有字段的内容,但该字段内容的本身是不会被克隆的.因此该方法是浅拷贝操作,而不是深拷贝操作.
我们使用java语言来具体看看.
先来看看object类的clone方法(浅拷贝)的例子.
/**
* object类中的clone方法.
* 它实际上是一个浅拷贝操作.
* 但它在拷贝一个对象的时候,却确实新建了一个实例化对象.
* 但它是实际却是一次浅拷贝操作.
*/
class ShallowCopy implements Cloneable{
private String name;
private int age;
public String getName() {
return name;
}
public int getAge() {
return age;
}
// 构造方法.
ShallowCopy(String name,int age){
this.name=name;
this.age=age;
}
// 创建一个输出方法
void toimString(){
System.out.println(this.name+this.age);
}
// clone方法是Object基类中提供的方法,修饰为protected,
// 我们可以在任何继承它的类中重写实现该方法来完成clone操作.
@Override
// 重写object类中的clonr方法,使异常处理在方法体中进行.
protected Object clone(){
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
main方法中:
public static void main(String[] args) {
ShallowCopy xiaoshe = new ShallowCopy("xiaoshe", 3);
xiaoshe.toimString();
System.out.println(xiaoshe.hashCode());
ShallowCopy xiaoshe2 = (ShallowCopy) xiaoshe.clone();
xiaoshe2.toimString();
System.out.println( xiaoshe2.hashCode());
System.out.println((xiaoshe==xiaoshe2));
System.out.println((xiaoshe.getName().equals(xiaoshe2.getName())));
}
我们观察下结果:
我们可以发现,clone方法确实新建了一个实例化对象来拷贝原对象(哈希值不同,==为false,equlas方法为true).那为什么会说object类中的clone方法实际是浅拷贝操作呢,创建一个新对象来储存原对象不是深拷贝的性质吗.我们再来看一个例子:
在ShallShallowCopy类中添加一个inner成员变量(属性).并使用成员内部类的方式构建一个innerClass类.
public innerClass inner;
public class innerClass{
innerClass(String inname,int inage){
name=inname;
age=inage;
}
void imString(){
toimString();
}
void getname(){
getName();
}
void getage(){
getAge();
}
}
main方法中(加入成员inner的构建和copy):
xiaoshe.inner = xiaoshe.new innerClass("小舍",4);
ShallowCopy xiaoshe3 = (ShallowCopy) xiaoshe.clone();
输出测试:
xiaoshe.inner.imString();
System.out.println(xiaoshe.inner.hashCode());
xiaoshe3.inner.imString();
System.out.println(xiaoshe3.inner.hashCode());
System.out.println((xiaoshe.inner == xiaoshe3.inner));
结果:
我们从inner的输出可以发现在这个时候,这里只对ShallShallowCopy类拷贝了一次.而其中的inner对象和copy后的inner对象实际上还是指向的一个同一个对象,只是对它的引用进行了传递而没有进行拷贝操作.
所以说深浅拷贝实际上并不是一个绝对的定义.而是根据情况来进行判断的,clone方法在copy基本数据类型时,就相当于深拷贝.只有在拷贝实例化对象时(引用数据类型)才会有两种不同的处理方式(深浅拷贝).
我们再来看看深拷贝的实现.
按照object类的clone方法的思路来考虑,我们只要让内部类innerClass也重写clone方法,让它自己也clone一份,那么实际上就解决了浅拷贝的问题.
//让内部类继承Cloneable接口并重写clone方法.
public class innerClass implements Cloneable{
innerClass(String inname,int inage){
name=inname;
age=inage;
}
void imString(){
toimString();
}
void getname(){
getName();
}
void getage(){
getAge();
}
@Override
protected Object clone(){
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
在类中重写clone方法,实例一个对象clone原对象,并调用其inner成员的clone方法对其inner对象进行拷贝.
@Override
protected Object clone(){
try {
ShallowCopy copy = (ShallowCopy) super.clone();
copy.inner = (innerClass) this.inner.clone();
return copy;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
结果:
可以看到,这里对inner也进行了一次拷贝,这里实际上对innerClass类来说是进行了一次浅拷贝,而对ShallShallowCopy类来说就是一次深拷贝.
- 总结:
现在我们大概搞清楚了深浅拷贝之间的概念了.在JAVA语言中,实际上深浅拷贝只是相对的,如果一个对象内部只有基本数据类型,那用clone()方法获取的就是这个对象的深拷贝,而如果其内部还有引用数据类型,那使用clone()方法就是一次浅拷贝的操作.
更新时间:
2019-5-5
17:09