dedecms织梦内容管理系统    
首页 | java | C/C++ | PHP | 操作系统 | ajax | 脚本编程 | 安全技术 | 本站下载页 | 专题 | QQ群 | 测试中心 | 会员中心 | 积分规则
  当前位置:主页>java>java基础>文章内容
Java语言的克隆与深层次克隆技术入门与精通
来源:LifevV.COM编辑部 作者:
我们知道,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语言的克隆属性,以及克隆的实现方法等。同时分析了深层次克隆的概念,实现,以及存在的问题等。

上一篇:java学习----理解面向对象的程序设计   下一篇:Java语言的参数传递和处理方法 - 全面掌握Java参数传递与参数
[收藏] [推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
用户名: 新注册) 密码: 匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论
  热点文章
·JUnit单元测试(2)
·JUnit单元测试(1)
·浅谈在Java语言中究竟是传值还是
·Java 5.0 多线程编程实践
·Java的文件 读和写
·对于JAVA基础测试中常见的异常问
·JAVA程序员面试33问,你能回答多
·Java Reflection (JAVA反射)详解
·JAVA中配置环境变量设置方法大全
·讲述java语言中内部类的研究
·详细解析Java中抽象类和接口的区
·Java开发中的事件驱动模型实例详
  相关文章
·java学习----理解面向对象的程序
·Java语言的参数传递和处理方法 -
·Vector & ArrayList 哪一个更好
·Java/J2EE中文问题终极解决之道
·深入 JAVA对象的复制与比较
·怎么向一个命令行Java程序传递参
·java多线程编程
·讨论 Java 技术新手入门
·Java输入输出(Java IO)处理概
·Java开发者需坚守的十大基本准则
·JAVA如何调用DOS命令
·如何使用Static的变量和方法
  相关信息
copy right @ 百家拳软件项目研究室 2007 辽ICP备07011763