`
yuky1327
  • 浏览: 123120 次
  • 性别: Icon_minigender_2
  • 来自: 广州
社区版块
存档分类
最新评论

Java克隆(Clone)的应用

    博客分类:
  • Java
阅读更多
转载于(http://lavasoft.blog.51cto.com/62575/43480)
简介:

Java克隆(Clone)是Java语言的特性之一,但在实际中应用比较少见。但有时候用克隆会更方便更有效率。

对于克隆(Clone),Java有一些限制:
1、被克隆的类必须自己实现Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。Cloneable 接口实际上是个标识接口,没有任何接口方法。
2、实现Cloneable接口的类应该使用公共方法重写 Object.clone(它是受保护的)。某个对象实现了此接口就克隆它是不可能的。即使 clone 方法是反射性调用的,也无法保证它将获得成功。
3、在Java.lang.Object类中克隆方法是这么定义的:
protected Object clone()
                throws CloneNotSupportedException
创建并返回此对象的一个副本。表明是一个受保护的方法,同一个包中可见。
按照惯例,返回的对象应该通过调用 super.clone 获得。

引题:

举个例子说吧,现在有一个对象比如叫foo,你需要在创建当前对象的一个副本作为存根你能怎么做?

假如你不用Clone,那么你可以先new一个对象foo1:Foo foo1=new Foo(),然后用foo给foo1对象set值,这样就得到foo的副本foo1;除此之外,别无选择。

这样说,也许有人会觉得说的过于绝对了,不过事实如此啊。

要产生一个副本,那副本要不要内存?----当然要了,那就对了!既然需要内存,(不克隆的情况下)你不new还有什么办法呢?请大家时刻铭记对象是Java运行时产生的,驻留在计算机内存中。

常见错误:
下面我澄清几个初学者容易犯迷糊的错误,同样的问题,产生foo对象的副本:
1、Foo foo1=new Foo();
   foo1=foo;
   然后就想当然的认为副本foo1生成了!

错误原因:foo1没错是申请了内存,但是执行foo1=foo后,foo1就不在指向刚申请的内存区域了,转而指向foo对象的内存区域,这时候,foo1、foo指向了同一内存区域。刚才new的操作制造一堆垃圾等着JVM回收。

2、Foo foo1=foo;
错误原因:还是两个变量都指向了同一块内存。

3、有些老鸟更厉害一些:在Foo中定义一个返回自身的方法:
    public Foo getInstance(){
        return this;
    }

    然后,Foo foo1=foo.getInstance();

错误原因:同上,主要还是没有重新开辟内存,this在对象里是什么?----就是对象自己的引用!那么getInstance()自然返回的就是对象自己,反正又是两个对象穿了一条裤子----***,哈哈。错得心服口服吧。为了节省篇幅,我在最后写个例子,留给那些对此有异议的人看。



引入克隆

看了这么多方法都不行,还很麻烦!干脆用克隆吧,简单明了。

废话不说了,看例子:
定义两个类CloneFooA、CloneFooB,然后写个测试类CloneDemo分别克隆这两个类的对象,然后打印测试结果到控制台。

/**
 * Created by IntelliJ IDEA.
 * User: leizhimin
 * Date: 2007-9-20
 * Time: 19:40:44
 * 简单类克隆实现
 * 要实现克隆,必须实现Cloneable接口,这是一个标识接口,没有接口方法
 * 实现了 Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。
 * 按照惯例,实现此接口的类应该使用公共方法重写 Object.clone(它是受保护的)。
 */
public class CloneFooA implements Cloneable {
    private String strA;
    private int intA;
 
    public CloneFooA(String strA, int intA) {
        this.strA = strA;
        this.intA = intA;
    }
 
    public String getStrA() {
        return strA;
    }
 
    public void setStrA(String strA) {
        this.strA = strA;
    }
 
    public int getIntA() {
        return intA;
    }
 
    public void setIntA(int intA) {
        this.intA = intA;
    }
 
    /**
     * @return 创建并返回此对象的一个副本。
     * @throws CloneNotSupportedException
     */
    public Object clone() throws CloneNotSupportedException {
        //直接调用父类的clone()方法,返回克隆副本
        return super.clone();
    }
}


/**
 * Created by IntelliJ IDEA.
 * User: leizhimin
 * Date: 2007-9-20
 * Time: 19:59:55
 * 深度克隆对象,当类存在聚合关系的时候,克隆就必须考虑聚合对象的克隆
 */
public class CloneFooB implements Cloneable {
    private CloneFooA fooA;
    private Double douB;
 
    public CloneFooB(Double douB) {
        this.douB = douB;
    }
 
    public CloneFooB(CloneFooA fooA, Double douB) {
        this.fooA = fooA;
        this.douB = douB;
    }
 
    public CloneFooA getFooA() {
        return fooA;
    }
 
    public void setFooA(CloneFooA fooA) {
        this.fooA = fooA;
    }
 
    public Double getDouB() {
        return douB;
    }
 
    public void setDouB(Double douB) {
        this.douB = douB;
    }
 
    /**
     * 克隆操作
     *
     * @return 自身对象的一个副本
     * @throws CloneNotSupportedException
     */
    public Object clone() throws CloneNotSupportedException {
        //先调用父类的克隆方法进行克隆操作
        CloneFooB cloneFooB = (CloneFooB) super.clone();
        //对于克隆后出的对象cloneFooB,如果其成员fooA为null,则不能调用clone(),否则出空指针异常
        if (this.fooA != null)
            cloneFooB.fooA = (CloneFooA) this.fooA.clone();
 
        return cloneFooB;
    }
}



/**
 * Created by IntelliJ IDEA.
 * User: leizhimin
 * Date: 2007-9-20
 * Time: 19:52:01
 * 测试类:分别克隆CloneFooA和CloneFooB类,并打印克隆前后的结果.
 */
public class CloneDemo {
    public static void main(String args[]) throws CloneNotSupportedException {
        //CloneFooA克隆前
        CloneFooA fooA1 = new CloneFooA("FooA", 11);
        System.out.println("CloneFooA的对象克隆前对象fooA1值为: " + fooA1.getStrA() + "," + fooA1.getIntA());
        //CloneFooA克隆后
        CloneFooA fooA2 = (CloneFooA) fooA1.clone();
        System.out.println("CloneFooA的对象克隆后对象fooA2值为: " + fooA2.getStrA() + "," + fooA2.getIntA());
        //比较fooA1和fooA2内存地址
        if (fooA1 == fooA2) System.out.println("比较fooA1和fooA2内存地址:相等!");
        else System.out.println("比较fooA1和fooA2内存地址:不相等!");
 
        System.out.println("-------------------------");
 
        //CloneFooB克隆前
        CloneFooB fooB1 = new CloneFooB(fooA1, new Double("33"));
        System.out.println("CloneFooB的对象克隆前对象fooB1值为: " + fooB1.getFooA().getStrA() + "," + fooB1.getFooA().getIntA() + " | " + fooB1.getDouB());
        //CloneFooB克隆后
        CloneFooB fooB2 = (CloneFooB) fooB1.clone();
        System.out.println("CloneFooB的对象克隆前对象fooB2值为: " + fooB2.getFooA().getStrA() + "," + fooB2.getFooA().getIntA() + " | " + fooB2.getDouB());
 
        if (fooA1 == fooA2) System.out.println("比较fooB1和fooB2内存地址:相等!");
        else System.out.println("比较fooB1和fooB2内存地址:不相等!");
    }
}


运行结果:

CloneFooA的对象克隆前对象fooA1值为: FooA,11
CloneFooA的对象克隆后对象fooA2值为: FooA,11
比较fooA1和fooA2内存地址:不相等!
-------------------------
CloneFooB的对象克隆前对象fooB1值为: FooA,11 | 33.0
CloneFooB的对象克隆前对象fooB2值为: FooA,11 | 33.0
比较fooB1和fooB2内存地址:不相等!

反面教材:

最后,我给出我上面提出到最后要给出的反面例子。

随便写一个,在CloneFooA 的基础上做了少许改动,内容如下:
public class CloneFooA implements Cloneable {
    private String strA;
    private int intA;
 
    public CloneFooA(String strA, int intA) {
        this.strA = strA;
        this.intA = intA;
    }
 
    public String getStrA() {
        return strA;
    }
 
    public void setStrA(String strA) {
        this.strA = strA;
    }
 
    public int getIntA() {
        return intA;
    }
 
    public void setIntA(int intA) {
        this.intA = intA;
    }
 
    /**
     * @return 创建并返回此对象的一个副本。
     * @throws CloneNotSupportedException
     */
    public Object clone() throws CloneNotSupportedException {
        //直接调用父类的clone()方法,返回克隆副本
        return super.clone();
    }
 
    /**
     * @return 返回运行时的对象
     */
    public CloneFooA getInstance(){
        return this;
    }
 
    public static void main(String args[]){
        CloneFooA fooA=new CloneFooA("aa",11);
        System.out.println(fooA.getStrA()+"  "+fooA.getIntA());
 
        CloneFooA fooA1=fooA.getInstance();
        System.out.println(fooA1.getStrA()+"  "+fooA1.getIntA());
        if(fooA==fooA1) System.out.println("fooA和fooA1内存地址相等!");
 
        System.out.println("-------------------------");
 
        //改变后fooA或者fooA1中任何一个,看看另外一个是否会改变
        fooA1.setStrA("bb");
        System.out.println(fooA.getStrA()+"  "+fooA.getIntA());
        System.out.println(fooA1.getStrA()+"  "+fooA1.getIntA());
 
        if(fooA==fooA1) System.out.println("fooA和fooA1内存地址相等,改变fooA1后,fooA的值也跟着变化了");
    }
}


运行结果:

aa  11
aa  11
fooA和fooA1内存地址相等!
-------------------------
bb  11
bb  11
fooA和fooA1内存地址相等,改变fooA1后,fooA的值也跟着变化了
分享到:
评论

相关推荐

    medium-clone:Medium.com的完整堆栈应用程序克隆

    是一个全栈应用程序,旨在克隆medium.com体验。 随着越来越多的人试图找到一种展现自己诗意的一面的方法,Poedium努力实现自己的梦想。 有关此项目的更多信息,请访问 讲台 诗人的社区 目录 关于该项目 一个完整的...

    Organize-Clone:金融克隆应用组织

    组织克隆金融克隆应用组织

    java编程常见问题

    当没有实现Cloneable接口或者不支持克隆方法时,调用其clone()方法则抛出该异常。 47.java.lang.EnumConstantNotPresentException 枚举常量不存在异常。当应用试图通过名称和枚举类型访问一个枚举对象,但该枚举对象...

    tutorial-java:Rookout使用Java的官方教程

    有用的网址:先决条件Java 8 泊坞窗(可选) - 设置从此存储库克隆示例应用程序: git clone https://github.com/Rookout/tutorial-java.gitcd tutorial-java 将您的退出标记设置为环境变量(对于Windows,请使用set...

    java 编程入门思考

    12.2.5 Object.clone()的效果 12.2.6 克隆合成对象 12.2.7 用Vector进行深层复制 12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 ...

    Java初学者入门教学

    12.2.5 Object.clone()的效果 12.2.6 克隆合成对象 12.2.7 用Vector进行深层复制 12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 ...

    Uber_Clone:优步克隆

    Uber_Clone以Uber com的身份克隆应用程序Uber com

    java联想(中文)

    12.2.5 Object.clone()的效果 12.2.6 克隆合成对象 12.2.7 用Vector进行深层复制 12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 ...

    appointment-reminders-java:演示应用程序使用Spark框架在Java 8中实现约会提醒用例

    Java约会提醒 我们目前正在更新此样本模板。 如果您在样本中遇到任何... 电话号码E.164格式的Twilio电话号码-您可以在此处获得一个 当地发展满足以上要求后: 克隆这个存储库和cd进去git clone git@github.com:TwilioDe

    JAVA_Thinking in Java

    12.2.5 Object.clone()的效果 12.2.6 克隆合成对象 12.2.7 用Vector进行深层复制 12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 ...

    Thinking in Java 中文第四版+习题答案

    12.2.5 Object.clone()的效果 12.2.6 克隆合成对象 12.2.7 用Vector进行深层复制 12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 ...

    Thinking in Java简体中文(全)

    12.2.5 Object.clone()的效果 12.2.6 克隆合成对象 12.2.7 用Vector进行深层复制 12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 ...

    watsonwork-java-starter:演示如何将应用程序与Watson Workspace集成的Java应用程序

    Watsonwork-Java-Starter 这是一个用于Java的应用程序,它演示了与Watson Work Services的基本集成流程。... 现在是时候克隆git clone https://github.com/watsonwork/watsonwork-java-starter.git && cd

    facebook-clone:study Facebook界面的UI克隆,用于研究目的

    Java脚本 后端 Node.js Express.js Nodemon Dotenv 修女 注:后端技术被用来部署到Heroku的,它们并没有反映在应用上直接开发。 :laptop: 项目 出于学习目的,使用html,css和javascript制作的facebook界面的...

    java-ddd-example:using使用SpringBoot的Java项目中的六角结构+ DDD + CQRS

    克隆此存储库: git clone https://github.com/CodelyTV/cqrs-ddd-java-example 执行一些,以检查一切正常: 创建和其他项目工件: ./gradlew assemble --warning-mode all 运行测试和插件验证任务: ./gradlew ...

    android-uber-clone:将克隆作为Principais Funcionalidades做App在Android上的应用

    android-uber-clone:将克隆作为Principais Funcionalidades做App在Android上的应用

    课程设计:基于java8+jsp+mysql+tomcat+javascript实现的在线考试系统【源码+数据库】.zip

    本系统设计综合应用所学知识开发一个基于MVC模式的考试系统 技术栈 java 8 + jsp + mysql + tomcat + javascript + css 相关工具组件 项目开发语言:java 8; Excel工具包:apache-poi 4.1.0; JSP标准标签库:jstl...

    java8源码-gitlg.com:gitlg.com

    java8 源码 快速开始 先决条件 开发环境 克隆代码库: git clone https://gitee.com/toopoo/SpringCloud.git 生成ide配置: mvn idea:idea 并导入对应的ide进行开发,IDE安装lombok插件 编译 & 启动 1.启动基础服务:...

    SignalR-java-client:https

    文献资料请参阅说明执照贡献见建立源 git clone git@github.com:SignalR/java-client.git (or https if you use https)打开Android Studio,单击“导入非Android Studio项目”,然后选择克隆的目录生成项目。...

    java-kubernetes-projeto:Java没有Kubernetes。 进行调试

    git clone https://github.com/sandrogiacom/java-kubernetes.git 构建应用 cd java-kubernetes mvn clean install 启动数据库 make run-db 运行应用程序 java --enable-preview -jar target/java-kubernetes.jar...

Global site tag (gtag.js) - Google Analytics