当前位置: 移动技术网 > IT编程>开发语言>Java > 如何理解clone对象

如何理解clone对象

2020年07月16日  | 移动技术网IT编程  | 我要评论

为什么要用 clone?

在实际编程过程中,我们常常要遇到这种情况:有一个对象 A,在某一时刻 A 中已经包含了一些有效值,此时可能会需要一个和A完全相同新对象B,并且此后对B 任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。在Java语中,用简单的赋值语句是不能满足这种需求的。要满足这种需求虽然有很多途径,但实现clone()方法是其中最简单,也是最高效的手段。

new 一个对象的过程和 clone 一个对象的过程区别

new操作符的本意是分配内存。程序执行到new操作符时,首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。

clone在第一步是和new相似的,都是分配内存,调用clone方法时,分配的内存和原对象(即调用clone方法
的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域,填充完成之后,clone方法返回,一个新的相同
的对象被创建,同样可以把这个新对象的引用发布到外部。

clone 对象的使用

复制对象和复制引用的区别

 Person p = new Person(23, "zhang"); 
 Person p1 = p;  
 System.out.println(p); 
 System.out.println(p1); 

当Person p1 = p;执行之后, 是创建了一个新的对象吗? 首先看打印结果:
1.com.cs.Person@2f9ee1ac
2.com.cs.Person@2f9ee1ac
可以看出,打印的地址值是相同的,既然地址都是相同的,那么肯定是同一个对象。p和p1只是引用而已,他们
都指向了一个相同的对象Person(23, “zhang”) 。 可以把这种现象叫做引用的复制。上面代码执行完成之后, 内
存中的情景如下图所示:
在这里插入图片描述
而下面的代码是真真正正的克隆了一个对象。

1.Person p = new Person(23, "zhang");   
2.Person p1 = (Person) p.clone();    
3.System.out.println(p); 
4.System.out.println(p1); 

从打印结果可以看出,两个对象的地址是不同的,也就是说创建了新的对象, 而不是把原对象的地址赋给了一个新的引用变量:

  1. com.itheima.Person@2f9ee1ac
  2. com.itheima.Person@67f1fba0
    以上代码执行完成后, 内存中的情景如下图所示:
    在这里插入图片描述3.3.2 深拷贝和浅拷贝
    上面的示例代码中,Person 中有两个成员变量,分别是 name 和 age, name 是 String 类型, age 是 int 类
    型。代码非常简单,如下所示:
1public class Person implements Cloneable{   
2.privatint age ;   
3private String name;   
4public Person(int age, String name) {   
5this.age = age;   
6this.name = name;   
7}    
8public Person() {}    
9public int getAge() {   
10return age;   
11}    
12public String getName() {
13return name;   
14}    
15@Override  
16protected Object clone() throws CloneNotSupportedException {   
17return (Person)super.clone();   
18}   
19}

由于age是基本数据类型, 那么对它的拷贝没有什么疑议,直接将一个4字节的整数值拷贝过来就行。但是name 是 String 类型的, 它只是一个引用, 指向一个真正的 String 对象,那么对它的拷贝有两种方式: 直接将原对象中 的 name 的引用值拷贝给新对象的 name 字段, 或者是根据原 Person 对象中的 name 指向的字符串对象创建一个 新的相同的字符串对象,将这个新字符串对象的引用赋给新拷贝的 Person 对象的 name 字段。这两种拷贝方式分别 叫做浅拷贝和深拷贝。深拷贝和浅拷贝的原理如下图所示在这里插入图片描述
下面通过代码进行验证。如果两个 Person 对象的 name 的地址值相同, 说明两个对象的 name 都指向同一个 String 对象,也就是浅拷贝, 而如果两个对象的 name 的地址值不同, 那么就说明指向不同的 String 对象, 也就 是在拷贝Person对象的时候, 同时拷贝了name引用的String对象, 也就是深拷贝。验证代码如下:

Person p = new Person(23, "zhang"); 
Person p1 = (Person) p.clone(); 
String result = p.getName() == p1.getName() 
? "clone 是浅拷贝的" : "clone 是深拷贝的"; 
System.out.println(result);

打印结果为:
clone 是浅拷贝的
所以,clone方法执行的是浅拷贝, 在编写程序时要注意这个细节。

如何进行深拷贝:
由上一节的内容可以得出如下结论:如果想要深拷贝一个对象,这个对象必须要实现Cloneable接口,实现clone方法,并且在 clone 方法内部,把该对象引用的其他对象也要 clone 一份,这就要求这个被引用的对象必须也要实现Cloneable接口并且实现clone方法。那么,按照上面的结论,实现以下代码 Body类组合了Head类,要想深拷贝Body类,必须在Body类的clone方法中将Head类也要拷贝一份。代码如下:

static class Body implements Cloneable{   
    public Head head;   
    public Body() {}   
    public Body(Head head) {this.head = head;}   
    @Override  
    protected Object clone() throws CloneNotSupportedException {   
    Body newBody =  (Body) super.clone();   
    newBody.head = (Head) head.clone();   
    return newBody;   
    }   
    }   
    static class Head implements Cloneable{   
    public  Face face;    
    public Head() {}   
    @Override  
    protected Object clone() throws CloneNotSupportedException {   
    return super.clone();   
    }  }      
    public static void main(String[] args) throws CloneNotSupportedException {    
    Body body = new Body(new Head(new Face()));   
    Body body1 = (Body) body.clone();   
    System.out.println("body == body1 : " + (body == body1) );  
    System.out.println("body.head == body1.head : " +  (body.head == body1.head));   
    } 

打印结果为:

  1. body == body1 : false
  2. body.head == body1.head : false

本文地址:https://blog.csdn.net/qq_41620020/article/details/107352165

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网