Clone() method in Java

原文:geeks4geeks

Java 中的 clone() 方法

对象 clone 是指创建对象的精确拷贝,它创建了一个当前对象新的实例并根据相应的值域的内容初始化所有的值域。

使用赋值操作符创建引用变量的拷贝

与 C++ 不同,没有运算符能创建对象的拷贝。如果使用赋值运算符它会创建当前对象引用的拷贝。这个可以见下面的例子:

// 简单的演示赋值运算符仅创建对象的引用的拷贝的例子
import java.io.*;

// 测试类
class Test {
  int x, y;
  Test() {
    x = 10;
    y = 20;
  }
}

// 驱动类
class Main{
  public static void main(String[] args) {
    Test ob1 = new Test();

    System.out.println(ob1.x + " " + ob1.y);

    // 创建一个新的引用变量 ob2 指向和 ob1 相同的内存地址
    Test ob2 = ob1;

    // 任何影响 ob2 的操作都会影响 ob1
    ob2.x = 100;

    System.out.println(ob1.x+" "+ob1.y);
    System.out.println(ob2.x+" "+ob2.y);
  }
}

输出:

10 20
100 20
100 20

使用 clone() 方法创建对象的拷贝

需要被创建拷贝的对象的类或父类必须有一个 publicclone() 方法。

  • 每个实现 clone() 方法的类需要通过调用 super.clone() 去获取克隆的对象的引用。
  • 对于想创建对象拷贝的类必须实现 java.lang.Cloneable 接口,否则会在调用对象的 clone 方法时抛出 CloneNotSupportedException 异常。

protected Object clone() throws CloneNotSupportedException

clone() 方法的用法 —— 浅拷贝

// 简单的演示使用 clone() 方法实现浅拷贝的例子
import java.util.ArrayList;

class Test {
  int x, y;
}

// 包含 Test 对象的引用并实现了 clone 方法
class Test2 implements Cloneable {
  int a;
  int b;
  Test c = new Test();
  public Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}

// 驱动类
public class Main {
  public static void main(String args[]) throws CloneNotSupportedException {
    Test2 t1 = new Test2();
    t1.a = 10;
    t1.b = 20;
    t1.c.x = 30;
    t1.c.y = 40;

    // 创建 t1 的拷贝并赋值给 t2
    Test2 t2 = (Test2)t1.clone();

    // 改变 t2 中初始类型变量的值并不会影响 t1 的变量
    t2.a = 100;

    // 改变对象类型的值域会同时影响 t2 和 t1
    t2.c.x = 300;

    System.out.println(t1.a + " " + t1.b + " " + t1.c.x + " " + t1.c.y);
    System.out.println(t2.a + " " + t2.b + " " + t2.c.x + " " + t2.c.y);
  }
}

输出:

10 20 300 40
100 20 300 40

上例中,t1.clone 返回了 t1 对象的浅拷贝。为了获取对象的深拷贝,在获取拷贝之后还要在 clone() 方法中做一些修改。

深拷贝 vs 浅拷贝

  • 浅拷贝 是一种默认情况下通过克隆的方式复制对象的方法,这个方法中,旧的对象值域 X 会被复制到新的对象值域 Y,同样会复制 X 的引用到新的 Y,即对象值域 Y 会指向和对象值域 X 相同的(内存)位置;假如值域是初始类型,则仅复制值域的值。
  • 因此,任何对于对象 X 或对象 Y 的改变都会影响另一方。

clone() 方法的用法 —— 深拷贝

// 简单的演示使用 clone() 方法实现深拷贝的例子
import java.util.ArrayList;

class Test {
  int x, y;
}

// 包含 Test 对象的引用并实现了 clone() 方法
class Test2 implements Cloneable {
  int a, b;
  Test c = new Test();
  public Object clone() throws CloneNotSupportedException {
    // 将浅拷贝赋给引用变量 t
    Test2 t = (Test2)super.clone();
    t.c = new Test();

    // 为了创建深拷贝,为值域 c 创建新的对象并将其赋给浅拷贝
    return t;
  }
}

public class Main {
  public static void main(String args[]) throws CloneNotSupportedException {
    Test2 t1 = new Test2();
    t1.a = 10;
    t1.b = 20;
    t1.c.x = 30;
    t1.c.y = 40;

    Test2 t3 = (Test2)t1.clone();
    // 改变 t2 中初始类型的值域并不会影响 t1 的值域    
    t3.a = 100;

    // 改变 t2 中的对象值域并不会影响 t1 的对象值域(深拷贝)
    t3.c.x = 300;

    System.out.println(t1.a + " " + t1.b + " " + t1.c.x + " " + t1.c.y);
    System.out.println(t3.a + " " + t3.b + " " + t3.c.x + " " + t3.c.y);
  }
}

输出:

10 20 30 40
100 20 300 0

上例中,我们看到新创建的 Test 类的对象被赋给 Test2 类的 clone 方法的返回值。由于 t3 包含对象 t1 的深拷贝。因此任何对于对象 t3 中的对象值域 c 的改变都不会影响 t1。

clone 方法的优势:

  • 如果我们使用赋值操作符将一个对象的引用赋给另一个引用变量,这个引用变量将指向旧的对象,并没有新的对象创建。因此任何对于引用变量的改变将影响旧的对象。
  • 如果我们使用拷贝构造方法,那么我们必须明确地复制所有的数据,例如我们需要在构造方法中给所有的值域重新赋值。但是在 clone 方法内部已经将这种创建新拷贝的方法实现,因此为了避免额外的工作我们使用对象克隆。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,701评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,649评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,037评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,994评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,018评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,796评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,481评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,370评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,868评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,014评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,153评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,832评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,494评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,039评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,437评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,131评论 2 356

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,645评论 18 399
  • java笔记第一天 == 和 equals ==比较的比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量...
    jmychou阅读 1,502评论 0 3
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,152评论 30 470
  • 307、setValue:forKey和setObject:forKey的区别是什么? 答:1, setObjec...
    AlanGe阅读 1,551评论 0 1
  • 用别人的东西要先问可不可以 别人不习惯的事情不要做 每个人习惯不一样 自己的东西自己买 不要随便吹嘘自己 不要图一...
    欣昉阅读 565评论 0 0