原文地址:https://blog.csdn.net/u010661782/article/details/52900068
前言:
我们知道,在java的object类中,有这么一个方法clone(),这个方法有什么用呢?怎样才能正确地使用这个方法呢?
下面一一来进行阐述一下
clone()方法详解:
1>clone()方法的作用
顾名思义,clone()方法的作用就是克隆的意思,引入这个方法,这样就便于我们构建属于自己的一些本地对象副本。
这样我们就不用担心因为副本对象的引用而使原生的对象发生改变。这就是克隆所能带给我们的好处。
默认情况下,类是不具备克隆的能力的,需要再额外添加一些代码使其具备克隆能力。
2>如何使用clone()方法
I. 实现Cloneable 接口
II. 覆盖clone()
III. 在自己的clone()中调用super.clone()
IV. 在自己的clone()中捕获异常
参考示例如下:
package com.example.example_01;
/**
*
* 版权:XXX公司 版权所有
*
* 作者:will smith
*
* 版本:1.0
*
* 创建日期:2016/10/23
*
* 描述:
*
* 修订历史:
*
*/
//实现Cloneable接口
public class CloneSample implements Cloneable{
private int i;
/**
* 代码测试部分
* @param args
*/
public static void main(String[] args){
CloneSample sample = new CloneSample(10);
System.out.println("i: " + sample.i);
CloneSample sampleCopy = (CloneSample) sample.clone();
sampleCopy.i++;
System.out.println("copy i: " + sampleCopy.i);
System.out.println("i: " + sample.i);
}
public CloneSample(int i) {
this.i = i;
}
//重写clone
@Override
public Object clone(){
Object o = null;
try {
o = super.clone(); //调用父类的clone
} catch (CloneNotSupportedException e) { //异常捕获
e.printStackTrace();
}
return o;
}
}
运行结果如下:
代码分析:
由于CloneSample重写了clone()方法并实现了Cloneable接口,所以当调用CloneSample对象的clone()方法时,
就会克隆出一份CloneSample对象的副本,在克隆时,属性和方法都对照着拷贝,由于在拷贝属性时,其属性的
类型为 int ,所以当然是值传递,所以拷贝之后的副本和之前的副本就没有任何联系了。所以在对副本的属性 i 进行
++时,自然就不会影响原对象的属性 i 的值了。
若原对象的属性中含有引用类型,则其拷贝的就是句柄,这样会导致原对象属性的引用和副本对象属性的引用都指向
同一个对象,这样当副本对象修改引用类型属性的某个属性值时,就间接会影响原对象的引用类型属性的属性值,那么
如何才能真正达到两者完全分离呢?你看第三部分:深拷贝和浅拷贝。
3>深拷贝和浅拷贝
我们再来看一组调用clone()类的方法
package com.example.example_02;
/**
*
* 版权:XXX公司 版权所有
*
* 作者:will smith
*
* 版本:1.0
*
* 创建日期:2016/10/23
*
* 描述:用来比较深拷贝和浅拷贝
*
* 修订历史:
*
*/
public class CloneSampleDiff implements Cloneable{
Shallow shallow;
Deep deep;
public CloneSampleDiff() {
shallow = new Shallow();
deep = new Deep();
}
public static void main(String[] args){
CloneSampleDiff sampleDiff = new CloneSampleDiff();
CloneSampleDiff sampleDiffCopy = (CloneSampleDiff) sampleDiff.clone();
/**
* 改变之前的输出
*/
System.out.println("sampleDiff.shallow.tempShallow: " + sampleDiff.shallow.tempShallow);
System.out.println("sampleDiff.deep.tempDeep: " + sampleDiff.deep.tempDeep);
System.out.println("sampleDiffCopy.shallow.tempShallow: " + sampleDiffCopy.shallow.tempShallow);
System.out.println("sampleDiffCopy.deep.tempDeep: " + sampleDiffCopy.deep.tempDeep);
/***
* 改变tempClass和tempClone属性
*/
sampleDiffCopy.shallow.tempShallow++;
sampleDiffCopy.deep.tempDeep++;
System.out.println("====================================");
/**
* 改变之后的输出
*/
System.out.println("sampleDiff.shallow.tempShallow: " + sampleDiff.shallow.tempShallow);
System.out.println("sampleDiff.deep.tempDeep: " + sampleDiff.deep.tempDeep);
System.out.println("sampleDiffCopy.shallow.tempShallow: " + sampleDiffCopy.shallow.tempShallow);
System.out.println("sampleDiffCopy.deep.tempDeep: " + sampleDiffCopy.deep.tempDeep);
}
@Override
public Object clone() {
CloneSampleDiff o = null;
try {
o = (CloneSampleDiff) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
/**
* 注意观察这里
*/
o.deep = (Deep) o.deep.clone();
return o;
}
}
/**
* 实现了Cloneable
*/
class Shallow implements Cloneable{
int tempShallow;
public Shallow() {
tempShallow = 10;
}
@Override
public Object clone() {
Object o = new Object();
try {
o = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
}
/**
* 实现了Cloneable
*/
class Deep implements Cloneable{
int tempDeep;
public Deep() {
tempDeep = 20;
}
@Override
public Object clone(){
Object o = new Object();
try {
o = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
}
输出结果:
代码分析:
在分析之前,我们先来大概看一下上述这个类:
其包含了两个属性,分别为:shallow和deep,而shallow和deep都分别具有了克隆的功能,
两者唯一的区别就是在CloneSampleDiff类的clone()方法中,对于deep属性,增加了一条语句:
o.deep = (Deep) o.deep.clone();
也就是这条语句造就了两者中的tempShallow和tempDeep属性值的变化有所差异,
也就是当对副本sampleDiff执行
sampleDiffCopy.shallow.tempShallow++;
其原来的对象sampleDiff.shallow.tempShallow也会一同变化,这就是我们所说的浅拷贝。
而当副本sampleDiff执行
sampleDiffCopy.deep.tempDeep++;
其原来的对象sampleDiffCopy.deep.tempDeep就没有发生变化,这也就真正达到了我们
拷贝的目的,这就是我们所说的深拷贝。
深拷贝和浅拷贝出现的原因在于:
拷贝时,若属性为主类型,则由于其是值传递,所以自然不会有影响。
若属性为对象,我们知道,其实质是句柄,那么在拷贝时,自然也就
拷贝句柄,这样拷贝的结果是:两个相同属性的句柄指向了一个对象。
自然,当我们用一个句柄去改变对象的值时,自然另一个句柄的所
指向的值也就发生变化。
为了真正达到深拷贝,需要满足两点:
第一:这个属性是具有克隆功能的
第二:在clone()方法中,再去调用对应属性的clone()方法予以赋值,如本示例中的deep属性:
@Override
public Object clone() {
CloneSampleDiff o = null;
try {
o = (CloneSampleDiff) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
/**
* 注意观察这里
*/
o.deep = (Deep) o.deep.clone();
return o;
}
4>继承具有克隆功能的类
在第三部分中,我们知道,一个属性若要实现深拷贝,则需要让这个属性具有克隆的功能,
而要让这个属性具有克隆的功能,我们从第二部分可以知道:需要实现Cloneable接口及重写clone()方法。
但倘若一个类是继承一个已具有克隆功能的类的时候,此时,我们是否还有必要再去实现克隆功能呢?
下面,我们来看一下下面这段代码:
package com.example.example_03;
/**
*
* 版权:XXX公司 版权所有
*
* 作者:will smith
*
* 版本:1.0
*
* 创建日期:2016/10/23
*
* 描述:参考Java编程思想示例
*
* 修订历史:
*
*/
class Person {}
class Hero extends Person {}
class Scientist extends Person
implements Cloneable {
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
// this should never happen:
// It's Cloneable already!
throw new InternalError();
}
}
}
class MadScientist extends Scientist {}
public class HorrorFlick {
public static void main(String[] args) {
Person p = new Person();
Hero h = new Hero();
Scientist s = new Scientist();
MadScientist m = new MadScientist();
// p = (Person)p.clone(); // Compile error
// h = (Hero)h.clone(); // Compile error
s = (Scientist)s.clone();
m = (MadScientist)m.clone();
}
}
代码分析:
若新建一个类,它的基础类会默认为Object,并默认为不具备克隆能力。只要不明确地添加克隆能力,这种能力便不会自动产生。
但我们可以在任何层添加它,然后便可从那个层开始向下具有克隆能力。
5>序列化和克隆
我们知道,其实序列化从某种意义上也可是实现克隆的一种方式,那有了序列化,为何还要克隆呢?
因为克隆性能更高,所需消耗的时间更短。
---------------------
作者:drinkingcode
来源:CSDN
原文:https://blog.csdn.net/u010661782/article/details/52900068
版权声明:本文为博主原创文章,转载请附上博文链接!