详解java中clone方法

原文地址:http://leihuang.net/2014/11/14/java-clone/

In java, it essentially means the ability to create an object with similar state as the original object.

什么是clone

字典中的意思就是复制(强调跟原来的一模一样)。

*By default, java cloning is ‘field by field copy’ *.因为Object类不知道具体类的结构,无法确定哪个clone方法将被调用。所以JVM规定clone,做如下操作。

  1. 如果这个类只有原始数据类型成员的话,那么需要创建一个一模一样的对象,并且返回这个新对象的引用。
  2. 如果这个类包含了任何类类型成员的话,那么只有这些成员的对象引用被复制,且原始对象和克隆对象的成员的引用将指向同一个对象。

翻译的不好,%>_<%

  1. If the class has only primitive data type members then a completely new copy of the object will be created and the reference to the new object copy will be returned.
  2. If the class contains members of any class type then only the object references to those members are copied and hence the member references in both the original object as well as the cloned object refer to the same object.

除了上面两条默认的操作之外,你也可以重载clone方法,制定一套自己的clone方法。


java中clone规则

每种支持clone的语言都有它自己的规则,java也不例外。java中如果一个类需要支持clone的话,必须要做如下事情:

  1. 你必须继承Cloneable接口---Cloneable interface is broken in java。otherwise throws java.lang.CloneNotSupportedException.
  2. 必须重写Object类中的clone()方法
/*
Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object.
The general intent is that, for any object x, the expression:
1) x.clone() != x will be true
2) x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements.
3) x.clone().equals(x) will be true, this is not an absolute requirement.
*/
protected native Object  [More ...] clone() throws CloneNotSupportedException;
  1. 第一条表明,clone对象分配有独立的内存地址。
  2. 第二条表明,原始的和克隆的对象应该具有相同类型,但这不是必须的
  3. 第三条表明,原始的和克隆的对象调用equals方法的话,应该时相等的,但这不是必须的。

下面我们通过例子来分析:

class Father implements Cloneable{
    private int age ;
    private String name ;
    private Son son ;

    public Father(int age,String name,Son son){
        this.age = age ;
        this.name = name ;
        this.son = son ;
    }

    public Son getSon(){
        return this.son ;
    }

    @Override
    protected Object clone()throws CloneNotSupportedException{
        return super.clone() ;
    }
}
class Son{
    private int age ;
    private String name ;

    public Son(int age ,String name){
        this.age = age ;
        this.name = name ;
    }

    public void setName(String name){
        this.name = name ;
    }
    public String getName(){
        return this.name ;
    }
}
public class CloneDemo {
    public static void main(String args[]) throws CloneNotSupportedException{
        Son s = new Son(10,"Jack") ;
        Father fa = new Father(40,"Tom",s) ;
        Father clonedFa = (Father) fa.clone() ;

        System.out.println(fa!=clonedFa);
        System.out.println(clonedFa.getClass()==fa.getClass());
        System.out.println(clonedFa.equals(fa));

        //now we change the fa‘s son name by the clonedFa‘s son name
        clonedFa.getSon().setName("Jay");
        System.out.println(fa.getSon().getName());
    }
}
/*print:
true
true
false
Jay
*/

上面的代码中可以看出,原始的和克隆的Father类对象,拥有指向同一对象的两个引用,所以可以通过改变clonedFa中的Son来改变fa中Son对象。这就时所谓的浅拷贝.下面我们详细讨论一下浅拷贝和深拷贝。

浅拷贝(Shallow Cloning)

这是java中默认的实现。上面的例子中就是浅拷贝。不创建一个新的克隆拷贝对象Son,而是直接两个引用指向同一对象。

深拷贝

If we want a clone which is independent of original and making changes in clone should not affect original.you can try Deep Cloning.

we change the clone() method in the Father class .

@Override
protected Object clone() throws CloneNotSupportedException {
        Father fa = (Father)super.clone();
        fa.setSon((Son)fa.getSon().clone());
        return fa;
}

and we need Override the clone() method in the Son class. like this.

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

现在我们已经实现深拷贝了。

拷贝构造函数

拷贝构造函数时一种特殊的构造器,它讲自己的类类型作为参数。我们传递一个类的实例给拷贝构造函数,然后它将返回一个新的类实例。lets see this in example

public class PointOne {
    private Integer x;
    private Integer y;

    public PointOne(PointOne point){
        this.x = point.x;
        this.y = point.y;
    }
}

如果要继承它的话,则需要复制子类的参数,和传递参数给父类的构造器。如下:

public class PointTwo extends PointOne{
    private Integer z;

    public PointTwo(PointTwo point){
        super(point); //Call Super class constructor here
        this.z = point.z;
    }
}

下面是测试代码:

class Test
{
    public static void main(String[] args)
    {
        PointOne one = new PointOne(1,2);
        PointTwo two = new PointTwo(1,2,3);

        PointOne clone1 = new PointOne(one);
        PointOne clone2 = new PointOne(two);

        //Let check for class types
        System.out.println(clone1.getClass());
        System.out.println(clone2.getClass());
    }
}
Output:
class corejava.cloning.PointOne
class corejava.cloning.PointOne

你也可以使用静态工厂的方法来实现它。

public class PointOne {
    private Integer x;
    private Integer y;

    public PointOne(Integer x, Integer y)
    {
        this.x = x;
        this.y = y;
    }

    public PointOne copyPoint(PointOne point) throws CloneNotSupportedException
    {
        if(!(point instanceof Cloneable))
        {
            throw new CloneNotSupportedException("Invalid cloning");
        }
        //Can do multiple other things here
        return new PointOne(point.x, point.y);
    }
}

Cloning with serialization

这是例外一种深拷贝的方法。这里就不多讲了,详细见:A mini guide for implementing serializable interface in java

best practices

1) When you don’t know whether you can call the clone() method of a particular class as you are not sure if it is implemented in that class, you can check with checking if the class is instance of “Cloneable” interface as below.

if(obj1 instanceof Cloneable){
    obj2 = obj1.clone();
}
//Dont do this. Cloneabe dont have any methods
obj2 = (Cloneable)obj1.clone();

2) No constructor is called on the object being cloned. As a result, it is your responsibility, to make sure all the members have been properly set. Also, if you are keeping track of number of objects in system by counting the invocation of constructors, you
got a new additional place to increment the counter.

Reference:

  1. A guide to object cloning in java
  2. Effective--Java Item 11: Override clone judiciously(讲的更详细,各种clone方式的优缺点都讲了)


2014-11-14 15:48:12

Brave,Happy,Thanksgiving !

时间: 2024-10-12 10:36:22

详解java中clone方法的相关文章

详解Java中代码块和继承

本文发表于个人GitHub主页,原文请移步详解Java中代码块和继承 阅读. 概念 1.代码块 局部代码块 用于限定变量生命周期,及早释放,提高内存利用率 静态代码块 对类的数据进行初始化,仅仅只执行一次. 构造代码块 把多个构造方法中相同的代码可以放到这里,每个构造方法执行前,首先执行构造代码块. 2.继承 继承是已有的类中派生出新的类,新的类能够吸收已有类的数据属性和行为,并能扩展新的功能. 代码块的执行顺序 public class Test {    public String name

Java中clone方法的使用

什么是clone 在实际编程过程中,我们常常要遇到这种情况:有一个对象object1,在某一时刻object1中已经包含了一些有效值,此时可能会需要一个和object1完全相同新对象object2,并且此后对object2任何改动都不会影响到object1中的值,也就是说,object1与object2是两个独立的对象,但object2的初始值是由object1对象确定的.在Java语言中,用简单的赋值语句是不能满足这种需 求的.要满足这种需求虽然有很多途径,但实现clone()方法是其中最简单

详解Java中的clone方法

转载自:http://blog.csdn.net/zhangjg_blog/article/details/18369201 Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那么在java语言中,有几种方式可以创建对象呢? 1 使用new操作符创建一个对象 2 使用clone方法复制一个对象 那么这两种方式有什么相同和不同呢? new操作符的本意是

详解Java中的clone方法:原型模式

Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那么在java语言中,有几种方式可以创建对象呢? 1 使用new操作符创建一个对象2 使用clone方法复制一个对象 那么这两种方式有什么相同和不同呢? new操作符的本意是分配内存.程序执行到new操作符时, 首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间.分配完内存之

实例详解Java中如何对方法进行调用

原文源自http://www.jb51.net/article/73827.htm 方法调用Java支持两种调用方法的方式,根据方法是否返回值来选择. 当程序调用一个方法时,程序的控制权交给了被调用的方法.当被调用方法的返回语句执行或者到达方法体闭括号时候交还控制权给程序. 当方法返回一个值的时候,方法调用通常被当做一个值.例如: int larger = max(30, 40); 如果方法返回值是void,方法调用一定是一条语句.例如,方法println返回void.下面的调用是个语句: Sy

【Java学习笔记之三十三】详解Java中try,catch,finally的用法及分析

这一篇我们将会介绍java中try,catch,finally的用法 以下先给出try,catch用法: try { //需要被检测的异常代码 } catch(Exception e) { //异常处理,即处理异常代码 } 代码区如果有错误,就会返回所写异常的处理. 首先要清楚,如果没有try的话,出现异常会导致程序崩溃.而try则可以保证程序的正常运行下去,比如说: try { int i = 1/0; } catch(Exception e) { ........ } 一个计算的话,如果除数

详解Java中抽象类与接口的区别

在Java语言中, abstract class 和interface 是支持抽象类定义的两种机制.正是由于这两种机制的存在,才赋予了Java强大的 面向对象能力.abstract class和interface之间在对于抽象类定义的支持方面具有很大的相似性,甚至可以相互替换,因此很多开发者在进行抽象类定义时对于abstract class和interface的选择显得比较随意.其实,两者之间还是有很大的区别的,对于它们的选择甚至反映出对于问题领域本质的理解.对于设计意图的理解是否正确.合理.本

详解Java中的时区类TimeZone的用法

一.TimeZone 简介 TimeZone 表示时区偏移量,也可以计算夏令时. 在操作 Date, Calendar等表示日期/时间的对象时,经常会用到TimeZone:因为不同的时区,时间不同. 下面说说TimeZone对象的 2种常用创建方式.1.获取默认的TimeZone对象使用方法: TimeZone tz = TimeZone.getDefault() 2.使用 getTimeZone(String id) 方法获取TimeZone对象使用方法: // 获取 "GMT+08:00&q

详解Java中Map用法

Map以按键/数值对的形式存储数据,这里要特别说明( Map.Entry,是Map的内部类,它用来描述Map中的键/值对). Map是一个接口,我们平时多用它的实现类HashMap. 用例如下: public static void main(String args[]) { HashMap hashmap = new HashMap(); hashmap.put("Item0", "Value0"); hashmap.put("Item1",