Java 克隆

Java的参数传递包括:值传递、引用传递

如果我们希望用B对象保存A对象的数据,但又不希望B对象数据改变时影响到A,实现clone方法是最简单也是最高效的手段

如果A对象中只有基本类型或String类型时,只需A 实现Cloneable,然后覆盖clone方法,即简单克隆(影子克隆)

但当A对象中有复杂变量,如list,或其他类的对象是,则需深度克隆,前提是应用的其他对象也实现了cloneable接口,也支持clone方法


转载自:http://www.cnblogs.com/hdzsyp/archive/2010/10/27/1863020.html

我们知道,Java是纯面向对象的程序设计语言。Java里,所有的类的顶级父类都是java.lang.Object类,也就是说,如果一个类没有显示 申明继承关系,它的父类默认就是java.lang.Object。 
                                                                                    
   有一个很简单的方法可以证明这一点,我们写一个Test类,如下: 
public class Test { 
    public void someMethod() { 
        super.clone(); 
    } 

里面调用了super.clone(),编译时并不报错。其实clone()方法为java.lang.Object类提供的一个 protected型方法。 



对象克隆 
本文通过介绍java.lang.Object#clone()方法来说明Java语言的对象克隆特性。 
java.lang.Object#clone()方法由java.lang.Object加以实现,主要对对象本身加以克隆。 
首先我们看看下面的例子: 

public class TestClone { 
    public static void main(String[] args) { 
        MyClone myClone1 = new MyClone("clone1"); 
        
        MyClone myClone2 = (MyClone)myClone1.clone(); 
        
        if (myClone2 != null) { 
            System.out.println(myClone2.getName()); 
            System.out.println("myClone2 equals myClone1: " + myClone2.equals(myClone1)); 
        } else { 
            System.out.println("Clone Not Supported"); 
        } 
    } 

class MyClone { 
    private String name; 
    public MyClone(String name) { 
        this.name = name; 
    } 
    
    public String getName() { 
        return name; 
    } 
    public void setName(String name) { 
        this.name = name; 
    } 
    
    public Object clone() { 
        try { 
            return super.clone(); 
        } catch (CloneNotSupportedException e) { 
            return null; 
        } 
    } 
编译执行TestClone,打印出: 

C:\clone>javac *.java 
C:\clone>java TestClone 
Clone Not Supported 
C:\clone> 

说明MyClone#clone()方法调用super.clone()时抛出了CloneNotSupportedException异常,不 支持克隆。 

为什么父类java.lang.Object里提供了clone()方法,却不能调用呢? 

原来,Java语言虽然提供了这个方法,但考虑到安全问题, 一方面将clone()访问级别设置为protected型,以限制外部类访问; 
另一方面,强制需要提供clone功能的子类实现java.lang.Cloneable接口,在运行期,JVM会检查调用clone()方法的 类,如果该类未实现java.lang.Cloneable接口,则抛出CloneNotSupportedException异常。 

java.lang.Cloneable接口是一个空的接口,没有申明任何属性与方法。该接口只是告诉JVM,该接口的实现类需要开放“克隆”功 能。 

我们再将MyClone类稍作改变,让其实现Cloneable接口: 
class MyClone implements Cloneable { 
    ...//其余不做改变 


编译执行TestClone,打印出: 

C:\clone>javac *.java 
C:\clone>java TestClone 
clone1 
myClone2 equals myClone1: false 
C:\clone> 

根据结果,我们可以发现: 
1,myClone1.clone()克隆了跟myClone1具有相同属性值的对象 
2,但克隆出的对象myClone2跟myClone1不是同一个对象(具有不同的内存空间) 

小结: 
如果要让一个类A提供克隆功能,该类必须实现java.lang.Cloneable接口,并重载 java.lang.Object#clone()方法。 

public class A extends Cloneable { 
    public Object clone() { 
        try { 
            return super.clone(); 
        } catch (CloneNotSupportedException e) { 
            //throw (new InternalError(e.getMessage())); 
            return null; 
        } 
    } 



对象的深层次克隆 
  上例说明了怎么样克隆一个具有简单属性(String,int,boolean等)的对象。 


但如果一个对象的属性类型是List,Map,或者用户自定义的其他类时,克隆行为是通过怎样的方式进行的? 
很多时候,我们希望即使修改了克隆后的对象的属性值,也不会影响到原对象,这种克隆我们称之为对象的深层次克隆。怎么样实现对象的深层次克隆呢? 

验证对象的克隆方式 
为了验证对象的克隆方式,我们对上面的例子加以改进,如下(为了节省篇幅,我们省略了setter与getter方法): 

public class TestClone { 
    public static void main(String[] args) { 
        //为克隆对象设置值 
         MyClone myClone1 = new MyClone("clone1"); 
         myClone1.setBoolValue(true); 
         myClone1.setIntValue(100); 
        
        //设置List值 
         List <Element>listValue = new ArrayList<Element>(); 
         listValue.add(new Element("ListElement1")); 
         listValue.add(new Element("ListElement2")); 
         listValue.add(new Element("ListElement3")); 
         myClone1.setListValue(listValue); 
        
        //设置Element值 
         Element element1 = new Element("element1"); 
         myClone1.setElement(element1); 
        
        
        //克隆 
         MyClone myClone2 = (MyClone)myClone1.clone(); 
        
        if (myClone2 != null) { 
            
            //简单属性 
             System.out.println("myClone2.name=" + myClone2.getName() 
                     + " myClone2.boolValue=" + myClone2.isBoolValue() 
                     + " myClone2.intValue=" + myClone2.getIntValue() ); 
            
            //复合属性(List<Element>与Element) 
             List clonedList = myClone2.getListValue(); 
             Element element2 = myClone2.getElement(); 
            
             System.out.println("myClone2.listValue.size():" + clonedList.size()); 
             System.out.println("myClone2.element.equals(myClone1.element):" + element2.equals(element1)); 
             System.out.println("myClone2.element.name:" + element2.getName()); 
            
            //下面我们测试一下myClone2.element是否等于myClone1.element 
            //以及myClone2.listValue是否等于myClone1.listValue 
            //为此,我们修改myClone2.element与myClone2.listValue,如果myClone1的相应值也跟着被修改了,则它们引用 的是同一个内存空间的变量,我们认为它们相等 
            
             clonedList.add("ListElement4"); 
            
             System.out.println("myClone1.listValue.size():" + listValue.size()); 
            
             element2.setName("Element2"); 
             System.out.println("myClone1.element.name:" + element1.getName()); 
            
         } else { 
             System.out.println("Clone Not Supported"); 
         }        
        
     } 




class MyClone implements Cloneable { 
    private int intValue; 
    private boolean boolValue; 
    private String name; 
    private List <Element>listValue; 
    private Element element; 

    public MyClone(String name) { 
         this.name = name; 
     } 

     ...//setter与getter方法(略) 


class Element implements Cloneable   { 
    private String name; 
    
    public Element (String name) { 
         this.name = name; 
     } 

     ...//setter与getter方法(略) 


编译执行TestClone,打印出: 

C:\clone>javac *.java 
C:\clone>java TestClone 
myClone2.name=clone1 myClone2.boolValue=true myClone2.intValue=100 
myClone2.listValue.size():3 
myClone2.element.equals(myClone1.element):true 
myClone2.element.name:element1 
myClone1.listValue.size():4 
myClone1.element.name:Element2 
myClone2 equals myClone1: false 
C:\clone> 
我们发现,对于对象里的List,Element等复合属性,super.clone()只是简单地赋值,没有采取克隆手段。也就是说,修改被克 隆后的对象值,会影响到原对象。 

怎么进行深层次的克隆呢? 
答案是,我们只能手动在重载的clone()方法里,对属性也分别采用克隆操作。当然条件是,属性类也得支持克隆操作 

class MyClone implements Cloneable { 
     ... 
    public Object clone() { 
        try { 
             MyClone myClone = (MyClone)super.clone(); 
            //分别对属性加以克隆操作 
             myClone.element = this.element.clone(); 
            
             myClone.listValue = new ArrayList(); 
            for (Element ele:this.listValue) { 
                 myClone.listValue.add(ele.clone()); 
             } 
                        
            return myClone; 
         } catch (CloneNotSupportedException e) { 
            return null; 
         } 
     } 
     ... 


//让Element类也支持克隆操作 
class Element implements Cloneable   { 
     ... 
    public Element clone() { 
        try { 
            return (Element)super.clone(); 
         } catch (CloneNotSupportedException e) { 
            return null; 
         } 
     } 


深层次的克隆操作往往存在效率问题,尤其是需要让List,Map等集合类也支持深层次的克隆操作时。 

总结: 
本文结合范例,比较深入地介绍了Java语言的克隆属性,以及克隆的实现方法等。同时分析了深层次克隆的概念,实现,以及存在的问题等。 但是有没有更好的方法呢?当然,是有的,串行化来实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值