概观: 在这篇文章中,我们将讨论在复制Java对象的过程. 复制的目的,可以不同基于应用需求. 但主要有两种类型的复制, 深,浅拷贝. 复制的方法,这两种方法而变化.
介绍:
复制一个对象的属性成另一种数据类型相同的动作被称为对象复制. 在java, 我们有复制一个对象的下列方法到另一
- 浅拷贝: 这里,如果要被复制的场是一个基本类型, 然后该值被复制否则,如果要被复制的场是一个存储器地址 (或对象本身) 然后该地址被复制. 因此,如果地址是由一个对象改变, 变化被处处体现.
- 深复制: 这里的数据在这两个情况下复制. 这种方法是昂贵的和较慢的.
- 懒惰复制: 这是上述两种方法的组合. 最初使用的浅拷贝的方法,然后检查是否该数据是由许多对象共享和程序需要修改对象, 深副本的方法是使用.
因此,我们可以选择拷贝的基于以下两个条件的类型
- 不需要封装时,使用浅拷贝.
- 需要封装时使用深度复制.
浅拷贝:
在浅拷贝, 一个新的对象被创建包含值的原始对象的精确副本. 浅拷贝遵循逐位复制方法. 在浅副本,如果该字段是一个内存地址, 然后该地址被复制. 因此,如果地址是由一个对象改变, 变化被处处体现.
Figure 1: 流程图描述浅拷贝
在该图, 物体 – mainObj1有一个命名为原始类型的字段1场说INT, 和String类型的对象当我们做mainObj1的浅表副本, mainObj2与int类型的字段2包含FIELD1的复制价值,但在mainObj2 String对象创建 – 仍然指向objStr本身. 由于FIELD1是一个原始数据类型, 它的值复制到域2. 但由于objStr是一个对象, mainObj2指向objStr的同一地址. 因此,通过mainObj1到objStr所做的任何更改都会反映在mainObj2.
履行:
[码]
Listing 1: 类SubjectVO.java介绍对象的值对象
package com.home.objectCopy;
公众 类 SubjectVO {
私人 String name;
/**
* @返回 名字
*/
公众 字符串的getName() {
return 名称;
}
/**
* @param 名称
* 名称来设置
*/
公众 无效 的setName(String name) {
this.名称=名称;
}
公众 SubjectVO(String name) {
this.名称=名称;
}
}
[/码]
Listing 2: 类PupilVO.java描述瞳孔值对象
[码]
package com.home.objectCopy;
公众 类 PupilVO 器物 可复制 {
// 包含的对象
私人 SubjectVO SUBJ;
私人 String name;
/**
* @返回 the SUBJ
*/
公众 SubjectVO getSubj() {
return SUBJ;
}
/**
* @param SUBJ
* the SUBJ 设置
*/
公众 无效 setSubj(SubjectVO SUBJ) {
this.SUBJ = SUBJ;
}
/**
* @返回 名字
*/
公众 字符串的getName() {
return 名称;
}
/**
* @param 名称
* 名称来设置
*/
公众 无效 的setName(String name) {
this.名称=名称;
}
公众 PupilVO(String name, 串子) {
this.名称=名称;
this.SUBJ = 新 SubjectVO(子);
}
公众 克隆对象() {
// 浅拷贝
尝试 {
return 非常.克隆();
} 抓 (CloneNotSupportedException的ê) {
return 零;
}
}
}
[/码]
Listing3: 类ShallowCopyTest.java描述复制过程
[码]
package com.home.objectCopy;
公众 类 ShallowCopyTest {
公众 静止 无效 主(串[] 参数) {
// 原始对象
PupilVO螺柱= 新 PupilVO(“乔纳森”, “代数”);
系统.出.调用println(“原始对象: ” + stud.getName() + ” – ”
+ stud.getSubj().的getName());
// 克隆对象
PupilVO clonedStud = (PupilVO) stud.clone();
系统.出.调用println(“克隆的对象: ” + clonedStud.getName() + ” – ”
+ clonedStud.getSubj().的getName());
stud.setName(“丹尼尔”);
stud.getSubj().的setName(“物理”);
系统.出.调用println(“原始对象被更新后: ”
+ stud.getName() + ” – ” + stud.getSubj().的getName());
系统.出.调用println(“更新原来的对象后,克隆的对象: ”
+ clonedStud.getName() + ” – ”
+ clonedStud.getSubj().的getName());
}
}
[/码]
产量: 这个程序的输出是下
原始对象: 乔纳森 – 代数
克隆的对象: 乔纳森 – 代数
原始对象被更新后: 丹尼尔 – 物理
更新原来的对象后,克隆的对象: 乔纳森 – 物理
在这里,我们看到的字段名称的值被拷贝后改变,但因为它是指向同一个内存地址对象主体的值保持不变. 因此,对于乔纳森的主题变成了“物理’ 在那里,它应该是“代数’ 作为克隆对象SubjectVO的对象保持不变.
深复制:
在深拷贝, 不仅所有对象的字段复制, 所有这一切是由对象指向的动态分配的内存地址也被复制.
Figure 2: 下图描述了深拷贝过程
在该图, 对象mainObj1有field1的领域基本数据类型INT说, 和String类型的对象当我们做mainObj1的深层复制, mainObj2与包含域2 FIELD1的复制价值和objStr2创建包含objStr1的复制价值所以mainObj1到objStr1所做的任何更改将不mainObj2反映创建.
履行:
Listing4: 类描述深拷贝
[Code]
package com.home.DeepCopy;
公众 类 PupilVO 器物 可复制 {
// 包含的对象
私人 SubjectVO SUBJ;
私人 String name;
/**
* @返回 the SUBJ
*/
公众 SubjectVO getSubj() {
return SUBJ;
}
/**
* @param SUBJ
* the SUBJ 设置
*/
公众 无效 setSubj(SubjectVO SUBJ) {
this.SUBJ = SUBJ;
}
/**
* @返回 名字
*/
公众 字符串的getName() {
return 名称;
}
/**
* @param 名称
* 名称来设置
*/
公众 无效 的setName(String name) {
this.名称=名称;
}
公众 PupilVO(String name, 串子) {
this.名称=名称;
this.SUBJ = 新 SubjectVO(子);
}
公众 克隆对象() {
// 深拷贝
PupilVO瞳= 新 PupilVO(名称, subj.getName());
return 瞳孔;
}
}
[/码]
这种方法与前面的方法之间的唯一差别是,在PupilVO克隆方法返回一个新创建PupilVO对象. 这确保了无论何时在复制机制启动, 对象SubjectVO也得到改变. 深拷贝有一种替代方法 – 系列化. 在系列化, 整个对象图被写入一个持久存储并在需要时读回. 所以,每当我们从持久存储读取对象, 原始对象被称为.
浅拷贝和深拷贝使用:
没有为浅拷贝和深拷贝,但通常我们应该牢记之间选择确定没有硬性的规定,如果一个对象有唯一的原始场, 那么很明显,我们应该去浅拷贝, 但是,如果该对象具有到其他对象的引用, 然后根据要求, 浅复制或深拷贝应该做的事. 如果没有更新的参考文献,然后没有点发起深度复制.
解释懒副本:
懒副本可以被定义为浅拷贝和深拷贝两者的结合. 该机制遵循一个简单的方法 – 在初始状态下, 浅拷贝的方法是使用. 计数器也被用于保持对许多对象如何共享的数据的磁道. 当程序需要修改原来的对象, 它检查该对象是否是共享的还是不. 如果对象是共享, 那么深拷贝机制启动.
Summary:
在浅拷贝, 只有基本数据类型的字段被复制,而对象的引用不会被复制. 深拷贝包括基本数据类型的副本,以及客体引用. 没有硬性规定何时做浅复制和何时做一次深层副本. 懒惰拷贝是这两种方法的组合.