clone_cloneable - CSDN
clone 订阅
克隆是指生物体通过体细胞进行的无性繁殖,以及由无性繁殖形成的基因型完全相同的后代个体组成的种群。通常是利用生物技术由无性生殖产生与原个体有完全相同基因的个体或种群。克隆是英文"clone"或"cloning"的音译,而英文"clone"则起源于希腊文"Klone",原意是指以幼苗或嫩枝插条,以无性繁殖或营养繁殖的方式培育植物,如扦插和嫁接。译为“无性繁殖”,复制,转殖或群殖。 中文也有更加确切的词表达克隆,“无性繁殖”、“无性系化”以及“纯系化”。 展开全文
克隆是指生物体通过体细胞进行的无性繁殖,以及由无性繁殖形成的基因型完全相同的后代个体组成的种群。通常是利用生物技术由无性生殖产生与原个体有完全相同基因的个体或种群。克隆是英文"clone"或"cloning"的音译,而英文"clone"则起源于希腊文"Klone",原意是指以幼苗或嫩枝插条,以无性繁殖或营养繁殖的方式培育植物,如扦插和嫁接。译为“无性繁殖”,复制,转殖或群殖。 中文也有更加确切的词表达克隆,“无性繁殖”、“无性系化”以及“纯系化”。
信息
性    质
生物植物
外文名
clone或cloning
意    指
生物体与体细胞
来    源
希腊的“klone”
中文名
克隆
克隆克隆动物
蛙:1952年,罗伯特·布里格斯和托马斯·J·金成功地克隆了北方豹蛙:35完整的胚胎和27蝌蚪104个核克隆成功。鲤鱼:1963年,中国科学家童第周在1963年通过将一只雄性鲤鱼的遗传物质注入雌性鲤鱼的卵中从而成功克隆了一只雌性鲤鱼。但由于相关论文是发表在一本中文科学期刊,并没有翻译成英文,所以并不为国际上所知晓。绵羊:1996年,多莉(Dolly)鼠:1998年,Cumulina,夏威夷大学。日本茨城县茨城市的理化学研究所理研研究小组,团队召集人是小仓淳郎,从老鼠的尾巴采1滴血液,利用白细胞的核来复制动物,将来或许可运用于复制其他动物。猕猴:2000年1月,Tetra,雌性猪:2000年3月,2000年3月,5只苏格兰PPL小猪;8月,Xena,雌性。2013年1月30日,台湾成功复制迷你猪。台湾地区科学委员会今30日公布,由台湾3所学术机构共同花了3年时间,成功改良新式的胚复制技术“手工卵子分切复制技术”,已复制出两胎花斑迷你猪,其中1只已成功繁衍后代,显示技术已达成熟阶段,猪只现由竹南动科所饲养。牛:2001年,Alpha和Beta,雄性猫:2001年底,CopyCat(CC),雌性兔:2003年3-4月分别在法国和朝鲜独立地实现;骡:2003年5月,爱达荷Gem,雄性;6月,犹他先锋,雄性鹿:2003年,Dewey马:2003年,Prometea,普罗米特亚(普罗米修斯的阴性变格)雌性狗:2005年,韩国首尔大学实验队,史纳比灰狼:2007年,韩国首尔大学动物克隆研究组,Snuwolf和Snuwolffy,雄性骆驼:2015年猴:2017年,“中中” [1] 
收起全文
精华内容
参与话题
  • clone的理解

    2018-08-15 16:23:18
    在Javd中所有的传参都是值传递,并没有引用传递。有些方法参数是一个对象,也是值传递,传递的是对象的引用地址,这个地址指向堆中的一个对象,并没有传递一个对象。但是,拿到这个地址,传入对象的属性就可以被...

    参考地址:https://blog.csdn.net/u011679955/article/details/52736379

    在Javd中所有的传参都是值传递,并没有引用传递。有些方法参数是一个对象,也是值传递,传递的是对象的引用地址,这个地址指向堆中的一个对象,并没有传递一个对象。但是,拿到这个地址,传入对象的属性就可以被改变。不想对象的内容被方法改变,就是创建一个和原来对象一样的临时对象,进行参数传递。可以使用clone方法进行对象的复制。

    clone方法主要分成两步:

    1.对象所属的类实现Cloneable接口,这个接口就像序列化接口一样,标识这是一个可以克隆的对象

    2.方法中使用它, 如person.clone()

    clone分为浅克隆和深克隆:因为克隆的对象和原来的对象属性一样,原对象中属性是引用地址,克隆的也是引用地址,并不是克隆引用地址指向的对象。当这个引用地址指向的对象内容改变,所有的引用这个对象的内容也就改变了,这就是浅克隆。比如 a b c三个对象,b是a的一个对象属性,c是a的克隆对象,当a中b对象发生了改变,c中的b对象也发生改变。与浅克隆相对应的就是深克隆,深克隆是将对象属性也进行克隆,不再是直接复制原对象的引用地址。主要是在属性对象所在的类中也实现Cloneable接口,再在要克隆类的clone方法中指明属性对象也进行克隆,将引用地址改变。比如a b c三个对象,b是a的一个对象属性,c是a的克隆对象。b也实现cloneable接口,覆写clone方法,a在clone中指定 a=super.clone();a.b=B.clone()
      

    public class Person implements  Cloneable {
    	private  int age ;;
    	private String name;
    	private String sex;
    	
    	public Person() {
    		
    	}
    	
    	public Person(int age, String name, String sex) {
    		this.age = age;
    		this.name = name;
    		this.sex = sex;
    	}
    
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public String getSex() {
    		return sex;
    	}
    	public void setSex(String sex) {
    		this.sex = sex;
    	}
    	@Override
    	public String toString() {
    		return "Person [age=" + age + ", name=" + name + ", sex=" + sex + "]";
    	}
    
    	@Override
    	public Object clone()  {
    		try {
    			return super.clone();
    		} catch (CloneNotSupportedException e) {
    			e.printStackTrace();
    		}
    		return null;
    	}
    
    }
    public class Group implements Cloneable{
    	private String groupName;
    	private String groupId;
    	private Person person;
    	
    	public Group() {
    		
    	}
    	public Group(String groupName, String groupId, Person person) {
    		super();
    		this.groupName = groupName;
    		this.groupId = groupId;
    		this.person = person;
    	}
    	public String getGroupName() {
    		return groupName;
    	}
    	public void setGroupName(String groupName) {
    		this.groupName = groupName;
    	}
    	public String getGroupId() {
    		return groupId;
    	}
    	public void setGroupId(String groupId) {
    		this.groupId = groupId;
    	}
    	public Person getPerson() {
    		return person;
    	}
    	public void setPerson(Person person) {
    		this.person = person;
    	}
    	@Override
    	public String toString() {
    		return "Group [groupName=" + groupName + ", groupId=" + groupId + ", person=" + person + "]";
    	}
    	public Object clone() {
    		Group group = null;
    		try {
    			group = (Group)super.clone();
    //            深克隆,将属性person也进行克隆,Person要实现Cloneable
    			group.person = (Person) person.clone();
    			return group;
    		} catch (CloneNotSupportedException e) {
    			e.printStackTrace();
    		}
    		return group;
    		
    	}
    
    }
    
    public class ThinkInJava {
    	
    	/**
    	 * clone()方法,是在堆中复制一个对象,就像new一个对象一样,这个对象属性和原来的对象一样,只是栈中的引用地址变了
    	 * 想使用clone(),必须实现Cloneable接口,Cloneable是个标志,就像序列化一样
    	 * clone()分为浅克隆和深克隆,因为克隆的对象和原来的对象属性一样。如果原对象中属性是引用地址,克隆的也是引用地址,没有克隆引用地址指向的对象
    	 * 当这个引用地址指向的对象内容改变,所有的引用这个对象的内容也就改变了,这就是浅克隆。
    	 * 比如 a b c三个对象,a对象中有b的对象属性,c是a的克隆对象,当a中b对象发生了改变,c中的b对象也发生改变
    	 * 与浅克隆相对应的就是深克隆,深克隆是在引用对象中也实现clone方法,再在对象中指明引用对象也克隆,将引用地址改变
    	 * 如 a = super.clone()  a.b=b.clone()
    	 *  
    	 */
    	@Test
    	public void testBean()  {
    		Person p1 = new Person(12, "p1", "man");
    		Person p2 =(Person) p1.clone();
    		System.out.println("Person的String引用地址是否相同"+p1.getName().equals(p2.getName()));
    		p1.setName("p2");
    		System.out.println("Person的String引用地址是否相同"+p1.getName().equals(p2.getName()));
    		System.out.println(p1.toString());
    		System.out.println(p2.toString());
    	}
    	
    	@Test
    	public void testGroup()  {
    		Person p1 = new Person(12, "p1", "man");
    		Group g1 = new Group("g1", "g1", p1);
    		Group g2 = (Group)g1.clone();
    		System.out.println("Group的Person引用地址是否相同"+g1.getPerson().equals(g2.getPerson()));
    		p1.setName("p2");
    		System.out.println("Group的Person引用地址是否相同"+g1.getPerson().equals(g2.getPerson()));
    		System.out.println(g1.toString());
    		System.out.println(g2.toString());
    	}
    
    
    }

    testBean运行后结果是这个:

    Person的String引用地址是否相同true
    Person的String引用地址是否相同false
    Person [age=12, name=p2, sex=man]
    Person [age=12, name=p1, sex=man]

    好像和我说的引用地址没变冲突,其实没有冲突。主要是string类型的特殊造成的,String类型是final的不可变的,当string 的发生变化时它的引用地址就会发生变化,指向一个新的引用地址。在上面的结果中,第一个true说明刚刚克隆完p1对象,p1和p2的string引用地址是相同的,当p1的name字段发生改变,它的引用地址发生了改变,原来的引用废弃了,结果就是p2,p1的引用还是原来的结果是p1,他们的引用地址不同了

    属性字段是对象的克隆,属性对象没有实现克隆接口,testGroup运行后:

    Group的Person引用地址是否相同true
    Group的Person引用地址是否相同true
    Group [groupName=g1, groupId=g1, person=Person [age=12, name=p2, sex=man]]
    Group [groupName=g1, groupId=g1, person=Person [age=12, name=p2, sex=man]]

    发现g1 g2中p1对象一样,他们的属性person指向同一个对象,p1变了,他们的person属性发生变化,这就是浅克隆

    下面是深克隆,属性对象实现了克隆接口,Group中指出person属性也进行对象克隆,将引用地址改变,结果如下:

    Group的Person引用地址是否相同false
    Group的Person引用地址是否相同false
    Group [groupName=g1, groupId=g1, person=Person [age=12, name=p2, sex=man]]
    Group [groupName=g1, groupId=g1, person=Person [age=12, name=p1, sex=man]]
    

     

    展开全文
  • Object.clone()方法

    千次阅读 2011-05-03 14:57:00
    Object.clone()方法用于对象的拷贝,它会把在堆上的对象所占用的内存空间拷贝一份然后返回,这样就形成一个新的对象(个人理解)。因为每个对象所占的空间内都有一个指向其类数据的指针,也就是指向方法区中类数据...

          Object.clone()方法用于对象的拷贝,它会把在堆上的对象所占用的内存空间拷贝一份然后返回,这样就形成一个新的对象(个人理解)。因为每个对象所占的空间内都有一个指向其类数据的指针,也就是指向方法区中类数据(这个类数据可以通过Class对象进行访问,可以简单、直接的理解为就是指向代表其类的Class对象)。JVM会通过这个指针来判断一个对象的类型,由于把整个对象空间拷贝,所以拷贝的对象的类指针也指向相同的类对象,这就确保了obj.clone().getClass()==obj.getClass(),即它们具有相同的类型。还有一点,因为只是简单的将对象的空间进行复制,所以如果类具有引用类型的实例变量的话,也只是将这个引用进行拷贝,并不复制其引用的对象。这就导致拷贝对象的引用实例变量与原对象的指向相同的对象,这就是传说中的“浅拷贝”。如果实例变量引用的对象是不可变的,类似于String,则拷贝对象与原对象不能互相影响,这样的拷贝是成功的。但是如果引用的是可变对象,它们就会影响彼此,对于成功的拷贝而言,这是不允许的。可以对可变的实例变量对象进行特殊处理,以实现拷贝对象和原对象不能相互影响的“深拷贝”。

          由于Object.clone()方法是protected的,所以它只能在lang包中的类或是其子类的方法内部被调用,所以,如果像下面这样调用,会编译出错,在Person kobe_bak=kobe.clone();报错,说clone只能在Object的protected作用域访问。

     

         既然,clone方法的作用域限制不允许我们在其它地方访问,那我们就重写Object的clone方法,并扩大访问作用域为public,在Person类中添加clone的方法重写,调用super.clone(),也就是Object.clone()。

         然后编译,可以通过,接着运行程序,结果在Person kobe_bak=kobe.clone();抛出CloneNotSupportedException异常。这是因为Java只有一个类实现了Cloneable接口,才表示这个类可以被克隆。所以要想拷贝一个类的对象,必须让它实现Cloneable接口。这个接口只是一个标记接口,没有声明任何方法。

         接下来,让Person类实现Cloneable接口,然后编译,运行,这个对象就被成功的克隆了。所以要想一个类可以被clone,必须满足两点,第一,它必须实现了Cloneable接口,否则会抛出CloneNotSupportedException异常;第二,它必须提供一个public的clone方法,也就是重写Object.clone()方法,否则编译不能通过。第三,对于存在可变域的类,在clone方法中需要对这些可变域进行拷贝。

     

    展开全文
  • JAVA 中的的clone()详解

    千次阅读 多人点赞 2018-03-28 09:03:22
    假如说你想复制一个简单变量。很简单:int apples = 5; int pears = apples; 不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float,double.long)同样适用于该类情况。但是如果你复制的是一个对象...

    假如说你想复制一个简单变量。很简单:

    int apples = 5;  
    int pears = apples;  

    不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float,double.long)同样适用于该类情况。

    但是如果你复制的是一个对象,情况就有些复杂了。

    假设说我是一个beginner,我会这样写:

    复制代码
    class Student {  
        private int number;  
      
        public int getNumber() {  
            return number;  
        }  
      
        public void setNumber(int number) {  
            this.number = number;  
        }  
          
    }  
    public class Test {  
          
        public static void main(String args[]) {  
            Student stu1 = new Student();  
            stu1.setNumber(12345);  
            Student stu2 = stu1;  
              
            System.out.println("学生1:" + stu1.getNumber());  
            System.out.println("学生2:" + stu2.getNumber());  
        }  
    }  
    复制代码

    结果:

    学生1:12345  

    学生2:12345  

     

    这里我们自定义了一个学生类,该类只有一个number字段。

    我们新建了一个学生实例,然后将该值赋值给stu2实例。(Student stu2 = stu1;)

    再看看打印结果,作为一个新手,拍了拍胸腹,对象复制不过如此,

    难道真的是这样吗?

    我们试着改变stu2实例的number字段,再打印结果看看:

    stu2.setNumber(54321);  
      
    System.out.println("学生1:" + stu1.getNumber());  
    System.out.println("学生2:" + stu2.getNumber());  

    结果:

    学生1:54321  

    学生2:54321  

    这就怪了,为什么改变学生2的学号,学生1的学号也发生了变化呢?

    原因出在(stu2 = stu1) 这一句。该语句的作用是将stu1的引用赋值给stu2,

    这样,stu1和stu2指向内存堆中同一个对象。如图:

    那么,怎样才能达到复制一个对象呢?

    是否记得万类之王Object。它有11个方法,有两个protected的方法,其中一个为clone方法。

    在Java中所有的类都是缺省的继承自Java语言包中的Object类的,查看它的源码,你可以把你的JDK目录下的src.zip复制到其他地方然后解压,里面就是所有的源码。发现里面有一个访问限定符为protected的方法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 clone() throws CloneNotSupportedException;
    复制代码

    仔细一看,它还是一个native方法,大家都知道native方法是非Java语言实现的代码,供Java程序调用的,因为Java程序是运行在JVM虚拟机上面的,要想访问到比较底层的与操作系统相关的就没办法了,只能由靠近操作系统的语言来实现。

    1. 第一次声明保证克隆对象将有单独的内存地址分配。
    2. 第二次声明表明,原始和克隆的对象应该具有相同的类类型,但它不是强制性的。
    3. 第三声明表明,原始和克隆的对象应该是平等的equals()方法使用,但它不是强制性的。

    因为每个类直接或间接的父类都是Object,因此它们都含有clone()方法,但是因为该方法是protected,所以都不能在类外进行访问。

    要想对一个对象进行复制,就需要对clone方法覆盖。

    为什么要克隆?

      大家先思考一个问题,为什么需要克隆对象?直接new一个对象不行吗?

      答案是:克隆的对象可能包含一些已经修改过的属性,而new出来的对象的属性都还是初始化时候的值,所以当需要一个新的对象来保存当前对象的“状态”就靠clone方法了。那么我把这个对象的临时属性一个一个的赋值给我新new的对象不也行嘛?可以是可以,但是一来麻烦不说,二来,大家通过上面的源码都发现了clone是一个native方法,就是快啊,在底层实现的。

      提个醒,我们常见的Object a=new Object();Object b;b=a;这种形式的代码复制的是引用,即对象在内存中的地址,a和b对象仍然指向了同一个对象。

      而通过clone方法赋值的对象跟原来的对象时同时独立存在的。

    如何实现克隆

    先介绍一下两种不同的克隆方法,浅克隆(ShallowClone)深克隆(DeepClone)

    在Java语言中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括int、double、byte、boolean、char等简单数据类型,引用类型包括类、接口、数组等复杂类型。浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制,下面将对两者进行详细介绍。

    一般步骤是(浅克隆):

    1. 被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常), 该接口为标记接口(不含任何方法)

    2. 覆盖clone()方法,访问修饰符设为public方法中调用super.clone()方法得到需要的复制对象。(native为本地方法)

    下面对上面那个方法进行改造:

    复制代码
    class Student implements Cloneable{  
        private int number;  
      
        public int getNumber() {  
            return number;  
        }  
      
        public void setNumber(int number) {  
            this.number = number;  
        }  
          
        @Override  
        public Object clone() {  
            Student stu = null;  
            try{  
                stu = (Student)super.clone();  
            }catch(CloneNotSupportedException e) {  
                e.printStackTrace();  
            }  
            return stu;  
        }  
    }  
    public class Test {  
        public static void main(String args[]) {  
            Student stu1 = new Student();  
            stu1.setNumber(12345);  
            Student stu2 = (Student)stu1.clone();  
              
            System.out.println("学生1:" + stu1.getNumber());  
            System.out.println("学生2:" + stu2.getNumber());  
              
            stu2.setNumber(54321);  
          
            System.out.println("学生1:" + stu1.getNumber());  
            System.out.println("学生2:" + stu2.getNumber());  
        }  
    }  
    复制代码

    结果:

    学生1:12345  

    学生2:12345  

    学生1:12345  

    学生2:54321

    如果你还不相信这两个对象不是同一个对象,那么你可以看看这一句:

    System.out.println(stu1 == stu2); // false  

    上面的复制被称为浅克隆。

    还有一种稍微复杂的深度复制:

    我们在学生类里再加一个Address类。

    复制代码
     1 class Address  {  
     2     private String add;  
     3   
     4     public String getAdd() {  
     5         return add;  
     6     }  
     7   
     8     public void setAdd(String add) {  
     9         this.add = add;  
    10     }  
    11       
    12 }  
    13   
    14 class Student implements Cloneable{  
    15     private int number;  
    16   
    17     private Address addr;  
    18       
    19     public Address getAddr() {  
    20         return addr;  
    21     }  
    22   
    23     public void setAddr(Address addr) {  
    24         this.addr = addr;  
    25     }  
    26   
    27     public int getNumber() {  
    28         return number;  
    29     }  
    30   
    31     public void setNumber(int number) {  
    32         this.number = number;  
    33     }  
    34       
    35     @Override  
    36     public Object clone() {  
    37         Student stu = null;  
    38         try{  
    39             stu = (Student)super.clone();  
    40         }catch(CloneNotSupportedException e) {  
    41             e.printStackTrace();  
    42         }  
    43         return stu;  
    44     }  
    45 }  
    46 public class Test {  
    47       
    48     public static void main(String args[]) {  
    49           
    50         Address addr = new Address();  
    51         addr.setAdd("杭州市");  
    52         Student stu1 = new Student();  
    53         stu1.setNumber(123);  
    54         stu1.setAddr(addr);  
    55           
    56         Student stu2 = (Student)stu1.clone();  
    57           
    58         System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  
    59         System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  
    60     }  
    61 }  
    复制代码

    结果:

    学生1:123,地址:杭州市  

    学生2:123,地址:杭州市  

     

    乍一看没什么问题,真的是这样吗?

    我们在main方法中试着改变addr实例的地址。

    addr.setAdd("西湖区");  
      
    System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  
    System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  

    结果:

    学生1:123,地址:杭州市  
    学生2:123,地址:杭州市  
    学生1:123,地址:西湖区  
    学生2:123,地址:西湖区  

    这就奇怪了,怎么两个学生的地址都改变了?

    原因是浅复制只是复制了addr变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象。

    所以,为了达到真正的复制对象,而不是纯粹引用复制。我们需要将Address类可复制化,并且修改clone方法,完整代码如下:

    复制代码
     1 package abc;  
     2   
     3 class Address implements Cloneable {  
     4     private String add;  
     5   
     6     public String getAdd() {  
     7         return add;  
     8     }  
     9   
    10     public void setAdd(String add) {  
    11         this.add = add;  
    12     }  
    13       
    14     @Override  
    15     public Object clone() {  
    16         Address addr = null;  
    17         try{  
    18             addr = (Address)super.clone();  
    19         }catch(CloneNotSupportedException e) {  
    20             e.printStackTrace();  
    21         }  
    22         return addr;  
    23     }  
    24 }  
    25   
    26 class Student implements Cloneable{  
    27     private int number;  
    28   
    29     private Address addr;  
    30       
    31     public Address getAddr() {  
    32         return addr;  
    33     }  
    34   
    35     public void setAddr(Address addr) {  
    36         this.addr = addr;  
    37     }  
    38   
    39     public int getNumber() {  
    40         return number;  
    41     }  
    42   
    43     public void setNumber(int number) {  
    44         this.number = number;  
    45     }  
    46       
    47     @Override  
    48     public Object clone() {  
    49         Student stu = null;  
    50         try{  
    51             stu = (Student)super.clone();   //浅复制  
    52         }catch(CloneNotSupportedException e) {  
    53             e.printStackTrace();  
    54         }  
    55         stu.addr = (Address)addr.clone();   //深度复制  
    56         return stu;  
    57     }  
    58 }  
    59 public class Test {  
    60       
    61     public static void main(String args[]) {  
    62           
    63         Address addr = new Address();  
    64         addr.setAdd("杭州市");  
    65         Student stu1 = new Student();  
    66         stu1.setNumber(123);  
    67         stu1.setAddr(addr);  
    68           
    69         Student stu2 = (Student)stu1.clone();  
    70           
    71         System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  
    72         System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  
    73           
    74         addr.setAdd("西湖区");  
    75           
    76         System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());  
    77         System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());  
    78     }  
    79 }  
    复制代码

    结果:

    学生1:123,地址:杭州市  
    学生2:123,地址:杭州市  
    学生1:123,地址:西湖区  
    学生2:123,地址:杭州市  

    这样结果就符合我们的想法了。

     

    最后我们可以看看API里其中一个实现了clone方法的类:

    java.util.Date:

    复制代码
    /** 
     * Return a copy of this object. 
     */  
    public Object clone() {  
        Date d = null;  
        try {  
            d = (Date)super.clone();  
            if (cdate != null) {  
                d.cdate = (BaseCalendar.Date) cdate.clone();  
            }  
        } catch (CloneNotSupportedException e) {} // Won't happen  
        return d;  
    }  
    复制代码

    该类其实也属于深度复制。

    参考文档:Java如何复制对象


    浅克隆和深克隆

    1、浅克隆

    在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。

    简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。

    在Java语言中,通过覆盖Object类的clone()方法可以实现浅克隆

    2、深克隆

    在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。

    简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。

    在Java语言中,如果需要实现深克隆,可以通过覆盖Object类的clone()方法实现,也可以通过序列化(Serialization)等方式来实现。

    如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。

    序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。

    扩展
    Java语言提供的Cloneable接口和Serializable接口的代码非常简单,它们都是空接口,这种空接口也称为标识接口,标识接口中没有任何方法的定义,其作用是告诉JRE这些接口的实现类是否具有某个功能,如是否支持克隆、是否支持序列化等。


    如何实现克隆

    简单到你不敢相信。直接在你的类的后面声明implements Cloneable。关于这个接口,它的源码如下:

    public interface Cloneable {
    }
    

    可以看到它是一个空的接口,它的作用就是做标记。如果没有实现Cloneable接口就直接使用clone方法,程序会抛出CloneNotSupportedException异常。
    然后是重写clone方法,并修改成public访问级别。举个经典的栗子:

    class Outer implements Cloneable {
        public int name;
        public Inner inner;
          
        @Override
        public Object clone() throws CloneNotSupportedException {
          return super.clone();
        }
    public static void main(String[] args){
        Outer o_one = new Outer();
        o_one.inner = new Inner("zhangsan");
        try {
        Object obj = o_one.clone();
        Outer o_two = (Outer)obj;
        System.out.println(o_one==o_two);    //打印false
        System.out.println(o_one.inner.name.equals(o_two.inner.name)); //打印true
        } catch (CloneNotSupportedException e) {
         e.printStackTrace();
        }
    }
    }
    

    上面的代码实现的其实是浅克隆,浅克隆对于引用类型仅拷贝引用,没有真正地让两个对象独立开来互相之间没有任何关系。由于是浅克隆,使得imp2修改了child的某个属性后会是的imp1中child的属性也跟着改变。或者比较两个对象的地址:imp1.child==imp2.child,返回的结果是true。但是,如果一个对象只包含原始数据域或者不可变对象域(比如String类型),推荐使用浅克隆


    深克隆

    类中的所有引用类型做一些修改,让它也实现Cloneable接口。然后修改本类中的clone方法:

    public class Inner implements  Cloneable{
    
      public String name;
    
      public Child(String name) {
          this.name = name;
      }
    
      @Override
      public String toString() {
          return "Inner的name值为:" + name;
      }
    
      @Override
      protected Object clone() throws CloneNotSupportedException {
          return super.clone();
      }
    }
    

    修改本类的clone方法:

    static class Outer implements Cloneable {
      public int count;
      public Inner inner;
          
          
      @Override
      public Object clone() throws CloneNotSupportedException {
          Outer obj = (Outer)super.clone();
          obj.inner = (Inner) inner.clone();
          return obj;
      }
    }
    

    画重点了:

    • 需要重写clone方法,不仅仅只调用父类的方法,还需调用属性的clone方法;
    • 对象之间100%数据分离
    • 如果是对象存在引用类型的属性,建议使用深克隆
    • 深克隆比浅克隆要更加耗时,效率更低


    作者:Neulana
    链接:https://www.jianshu.com/p/36dfa5457b3e
    來源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    解决多层克隆问题

    如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。

    复制代码
     1 public class Outer implements Serializable{
     2   private static final long serialVersionUID = 369285298572941L;  //最好是显式声明ID
     3   public Inner inner;
     4  //Discription:[深度复制方法,需要对象及对象所有的对象属性都实现序列化] 
     5   public Outer myclone() {
     6       Outer outer = null;
     7       try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
     8           ByteArrayOutputStream baos = new ByteArrayOutputStream();
     9           ObjectOutputStream oos = new ObjectOutputStream(baos);
    10           oos.writeObject(this);
    11       // 将流序列化成对象
    12           ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
    13           ObjectInputStream ois = new ObjectInputStream(bais);
    14           outer = (Outer) ois.readObject();
    15       } catch (IOException e) {
    16           e.printStackTrace();
    17       } catch (ClassNotFoundException e) {
    18           e.printStackTrace();
    19       }
    20       return outer;
    21   }
    22 }
    复制代码

    Inner也必须实现Serializable,否则无法序列化:

    复制代码
     1 public class Inner implements Serializable{
     2   private static final long serialVersionUID = 872390113109L; //最好是显式声明ID
     3   public String name = "";
     4 
     5   public Inner(String name) {
     6       this.name = name;
     7   }
     8 
     9   @Override
    10   public String toString() {
    11       return "Inner的name值为:" + name;
    12   }
    13 }
    复制代码

    这样也能使两个对象在内存空间内完全独立存在,互不影响对方的值。

    总结

    实现对象克隆有两种方式:

      1). 实现Cloneable接口并重写Object类中的clone()方法;

      2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。

    注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总是优于把问题留到运行时。

    展开全文
  • clone()方法

    2017-09-08 11:37:00
    clone()方法: 创建并返回此对象的一个副本。“副本”的准确含义可能依赖于对象的类。这样做的目的是,对于任何对象 x,表达式: 书写注意要点: 1、返回的对象应该通过调用 super.clone 获得 x.clone( ) != x ...

    clone()方法:
    创建并返回此对象的一个副本。“副本”的准确含义可能依赖于对象的类。这样做的目的是,对于任何对象 x,表达式:
    书写注意要点:
    1、返回的对象应该通过调用 super.clone 获得
    x.clone( ) != x //true
    x.clone( ).getClass() == x.getClass( ) //true
    x.clone( ).equals(x) //结果视是否重写equals方法而定
    2、返回的对象应该独立于该对象(正在被复制的对象)
    要复制包含正在被复制对象的内部“深层结构”的所有可变对象,并使用对副本的引用替换对这些对象的引用。如果一个类只包含基本字段或对不变对象的引用,那么通常不需修改 super.clone 返回的对象中的字段。
    3、如果此对象的类不能实现接口 Cloneable,则会抛出异常
    所有的数组都被视为实现接口 Cloneable。
    Object 类本身不实现接口 Cloneable

    重写clone()方法:
    clone方法克隆对象的时候,并没有去执行java类的构造方法,而是直接从堆内存中复制出新内存对象。因此,如果对象网比较大的时候,clone效率要高得多。
    clone三部曲:
    1、声明实现Cloneable接口。
    2、调用super.clone拿到一个对象,如果父类的clone实现没有问题的话,在该对象的内存存储中,所有父类定义的field都已经clone好了,该类中的primitive和不可变类型引用也克隆好了,可变类型引用都是浅copy。
    3、把浅copy的引用指向原型对象新的克隆体。

    //基本数据类型浅度克隆,引用数据类型深度克隆
    //这段代码会出现捆绑,若把其中的注释去掉则不会捆绑
    class User implements Cloneable {
    String name;
    int age;
    public User(String name, int age) {
    super();
    this.name = name;
    this.age = age;
    }
    @Override
    public String toString() {
    return "User [name=" + name + ", age=" + age + "]";
    }
    
    //@Override
    //public User clone() throws CloneNotSupportedException {
    //return (User) super.clone();
    //}
    }
    
    class Account implements Cloneable {
    User user;
    long balance;
    public Account(User user, long balance){
    this.user = user;
    this.balance = balance;
    }
    @Override
    public String toString() {
    return "Account [user=" + user + ", balance=" + balance + "]";
    }
    @Override
    public Account clone() throws CloneNotSupportedException {
    Account account = null;
    account = (Account) super.clone();
    //if (user != null) {
    //account.user = user.clone();
    //}
    return account;
    }
    }
    
    public class CloneDemo {
    public static void main(String[] args) {
    User u = new User("Jack",20);
    Account account = new Account(u,1234567);
    System.out.println(account);
    Account account2=null;
    try {
    account2 = account.clone();
    } catch (CloneNotSupportedException e) {
    e.printStackTrace();
    }
    System.out.println(account2);
    //观察是否捆绑
    account2.user.age=40;
    System.out.println(account);
    System.out.println(account2);
    }
    }
    展开全文
  • 详解Java中的clone方法 -- 原型模式

    万次阅读 多人点赞 2014-03-25 15:01:40
    clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象。所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象。那么在java语言中,有几种方式可以创建对象...
  • Java中的clone和深度clone

    千次阅读 2018-07-11 21:47:05
    Java中对象的创建clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象。所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象。那么在java语言中,有几种...
  • clone()方法详解

    千次阅读 2019-05-31 16:33:15
    详情转载自:https://blog.csdn.net/54powerman/article/details/64920431?locationNum=6&fps=1
  • java对象clone()方法

    万次阅读 多人点赞 2019-05-29 21:27:19
    java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的: @Test public void testassign(){ Person p1=new Person(); p1.setAge(31); p1.setName("Peter");...
  • clone详解

    2018-09-02 00:21:26
    想要复制一个对象的所有属性,简单的方式就是通过Cloneable接口,你还可以使用工具类BeanUtils的copyProperties方法,还...如果一个类实现了Cloneable,那么Object的clone方法就返回该对象的逐域拷贝;没有实现Clon...
  • 系统调用clone()函数

    千次阅读 2019-07-05 22:35:34
    类似于fork()和vfork(),Linux特有的系统调用clone()也能创建一个新线程。与前两者不同的是,后者在进程创建期间对步骤的控制更为准确。其主要用于线程库的实现。其函数原型如下: #define _GNU_SOURCE #include &...
  • git clone 指定历史版本

    万次阅读 2018-06-26 19:42:16
    1.首先git clone 项目至文件夹2.cd 进入clone下来的项目文件夹内3.在git 仓库查看SHA4.git checkout 对应历史版本的SHA
  • git clone 指定分支 拉代码

    万次阅读 多人点赞 2018-06-22 17:28:06
    git clone 指定分支 拉代码
  • Git Clone命令直接使用用户名密码Clone

    万次阅读 2016-12-07 13:08:24
    Git Clone命令,大家都知道这个是克隆项目的. 当我们在服务器部署测试的时候,每次更新都需要输入账户和密码来确认,这样很麻烦,有没有一次性解决的办法呢?有! 在使用git clone 命令的时候我们可以将用户名和密码嵌入...
  • git如何clone所有的远程分支

    万次阅读 2014-09-07 10:49:17
    git clone只能clone远程库的master分支,无法clone所有分支,解决办法如下:
  • git clone 指定分支操作

    万次阅读 2017-07-06 09:39:57
    git clone 指定分支
  • git clone 指定 目录

    万次阅读 2016-11-16 19:53:20
    outputgit clone 指定 目录inputbaidu git clone 指定目录 http://blog.csdn.net/techbirds_bao/article/details/9179853algogit clone xxx.git “指定目录” 例如  git clone ...
  • git clone指定的tag代码git How to git clone a specific taggit clone –branch This command is not supported in git 1.7.9.5.I use git 1.8.3.5 and it works 这样会使当前版本的head指针处于游离状态 所有的...
  • git clone 指定某个分支

    万次阅读 2019-06-14 10:40:05
    最近在搭建Gitblit内网仓库时发现一个问题,git clone 只能clone整个仓库,但是如果我只需要仓库里面的某一个分支,这时还需要clone整个仓库就很头疼,下面用这个命令就可实现clone单个分支,我在gitblit内网上传了...
  • https git clone与ssh git clone之间的区别

    万次阅读 2018-04-10 18:10:08
    首先看一下两种使用方法的面相: ...ssh git clone是长这样的: git clone git@github.com:project/repo.git 区别就是所用的协议不同: https用443端口,可以对repo根据权限进行读写,只要有账号密...
  • 克隆指定的分支:git clone -b 分支名 仓库地址 克隆某分支到本地目录,然后在该目录下push时,对应的就是push到远端的对应分支。
1 2 3 4 5 ... 20
收藏数 427,247
精华内容 170,898
关键字:

clone