Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 362|回复: 0

[Java基础知识]java"克隆"入门

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-10-1 17:03:22 | 显示全部楼层 |阅读模式
    经常听到有人说java中没有指针。事实如此吗?no,java是有指针的,只不过换了个名字而已,也就是我们经常提到的引用。我们知道,在java中一切都是对象,那么我们如何操控对象?如何在成千上万的对象中找到我们所需的那个对象呢?又是如何让对象按照我们的意思来完成任务的呢?
       Object o = new Object();  这是java中最常见的语句了,在这句话中做了三件事。首先声明一个Object类型的变量o,在内存中为对象划分一块地址new Object(),将声明的变量指向内存中的对象。如此一来,我们就可以通过o来操纵对象了。就好像孩子们玩的遥控飞机,在空中飞行的是飞机,而使它做出优美动作的却是孩子们手中的摇控器。
      
      
      
    "克隆"是如今听到的较多的词汇,听说已经将某只羊克隆了好几份了。但愿这种技术不要在人身上实验。java中也有"克隆",与现实世界的克隆一样,将一个实际存在的对象拷贝几份。如下:
      //倒霉的羊
    public class Sheep implements Cloneable{
       private String name;
       public void setName(String arg) {
       name = arg;
    }
    public String getName() {
       return name;
    }
    public Object clone() throws CloneNotSupportedException {
       return super.clone();
    }
    }

    //克隆
    public class Main {
       public static void main(String[] args) throws CloneNotSupportedException {
         Sheep sheep = new Sheep(); //先得到那只羊的实例
         sheep.setName("我是真的"); //给它做个记号
         System.out.println("sheep.getName() = " + sheep.getName());
         Sheep sheepClone = (Sheep)sheep.clone(); //开始克隆
         System.out.println("sheepClone.getName() = " + sheepClone.getName());
    }
    }  
    运行程序结果为:
    sheep.getName() = 我是真的
    sheepClone.getName() = 我是真的
    两只羊是一模一样的(哪怕那只羊瘸腿)。让我们来看看代码。首先要注意的是Sheep类实现了Cloneable接口(该接口属于java.lang包,默认已经导入了),该接口中并没有定义要实现的方法,是个空接口,起标志作用。也就是说,实现了这个接口的羊就不再是只普通的羊,它是一只可以被克隆的羊。再往下看,有个clone方法,返回Object类型的对象,并抛出CloneNotSupportedException异常。

    该方法覆写了父类(Object)的clone方法,并在最后调用了super.clone(),这也意味着无论clone类继承结构是什么样的,super.clone()都会直接或间接调用Object类的clone()方法。看看jdk帮助文档会发现,Object类的clone()是一个native方法,我们知道,native方法的效率一般来说都是远高于java中的非native方法。这也说明了new一个对象,然后将原对象中的数据导入到新创建的对象中去的做法是多么愚蠢。必须说明的是Object中的clone方法是protected的,所以要使用clone就必须继承Object类(默认)。并且为了可以使其它类调用该方法,必须将其作用域设置为public。

    以上只是一个简单clone的实现。下面说说"影子clone"和"深度clone"。

    何为影子clone?先看一下例子。
    // 倒霉的羊
    1. class Sheep implements Cloneable{
    2.   private String name;
    3.   public void setName(String arg) {
    4.     name = arg;
    5.   }
    6.    public String getName() {
    7.     return name;
    8.    }
    9.    public Object clone() throws CloneNotSupportedException {
    10.      return super.clone();
    11.    }
    12. }
    13. //羊圈
    14. class Sheepfold implements Cloneable {
    15.     public Sheep sheep;
    16.     public String name;
    17.     public Sheepfold() {
    18.       sheep = new Sheep();
    19.     }
    20.    public Object clone() throws CloneNotSupportedException {
    21.      return super.clone();
    22.    }
    23. }

    24. //克隆
    25. public class Main {
    26.   public static void main(String[] args) throws Exception {
    27.     Sheepfold fold = new Sheepfold();
    28.     fold.name = "小羊圈";
    29.     fold.sheep.setName("小羊");
    30.     Sheepfold fold2 = (Sheepfold)fold.clone();
    31.     System.out.println(" fold2.name = " + fold2.name);
    32.     System.out.println(" fold2.sheep.getName() = " + fold2.sheep.getName());
    33.     fold2.name = "大羊圈";
    34.     fold2.sheep.setName("大羊");
    35.     System.out.println("=====================================");
    36.     System.out.println(" fold2.name = " + fold2.name);
    37.     System.out.println("* fold2.sheep.getName() = " + fold2.sheep.getName());
    38.     System.out.println(" fold.name = " + fold.name);
    39.     System.out.println("* fold.sheep.getName() = " + fold.sheep.getName());
    40.     System.out.println("=====================================");
    41. }
    42. }
    复制代码

    在这个例子中有三个类,Sheep和Sheepflod都实现了Cloneable接口,并且覆写了Object类的clone方法,说明这两个类是具有克隆能力的。注意一点,在Sheepflod中持有一个Sheep的实例,并在Main类中对其进行克隆,结果如下:
      fold2.name = 小羊圈
    fold2.sheep.getName() = 小羊
    =======================
    fold2.name = 大羊圈
    * fold2.sheep.getName() = 大羊
    fold.name = 小羊圈
    * fold.sheep.getName() = 大羊
    ======================  
    请注意一下结果中带有"*"号的两条结果语句。fold2.sheep和fold.sheep的name都变为了"大羊",很奇怪是吗?在此之前,我们只对fold2.sheep的name赋过值。为什么fold.sheep的name也变为了"大羊"呢?原因很简单,因为它们是指向同一个对象的不同引用。从中可以看出,调用Object类中clone()方法时,首先在内存中划分一块同原对象相同的空间,然后将原对象的内容原样拷贝至新对象。

    我们知道,java中有基本数据类型,对于基本数据类型,这样的操作是没有问题的,但对非基本类型变量,它们保存的仅仅是对象的引用,这也是为什么clone后非基本类型变量和原对象中的变量指向同一个对象的原因。可能你已经注意到,程序中用到了String类型,即对象,为什么没有出现引用指向同一地址的情况?

    这是因为String是一个不可更改的类(immutable class),每次给它赋值时,都会产生一个新的String对象。如String str = "a"; str += "b";在这两句代码中,当执行str += "b"时,实际上是重新成生了一个值为"ab"的String对象,即重新分配了一块内存空间。以上clone方法通常被称为"影子clone"。"影子clone"给我们留下了一个问题,即多个引用指向同一个对象。如何解决该问题呢?答案为"深度clone"。把上面的例子改成深度clone很简单,只需将Sheepfold的clone()方法改为如下即可:
      public Object clone() throws CloneNotSupportedException {
    Sheepfold fold = (Sheepfold)super.clone();
    sheep = (Sheep)fold.sheep.clone();
    return fold;
    }  
    至此,clone就基本完成了。当然,在实际使用过程中需要注意一些问题,比如StringBuffer不可以直接clone(当然,也有解决办法)等等。  

      
      
       
       

         
       

         
       
      



    源码下载:http://file.javaxxz.com/2014/10/1/170322391.zip
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-3 17:26 , Processed in 0.319899 second(s), 38 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表