Java 拷贝

1,158 阅读3分钟
原文链接: zhuanlan.zhihu.com
Java 中的 clone 虽然不推荐使用,但是万一使用到还是有些地方需要注意的。

粗略介绍

Object 类中有个受保护方法 clone,所以很多时候我们都可以在类中重写 clone 方法。调用该方法会将对象复制一份,创造一个新的对象,但是这里就需要注意了。

Object 中存在 clone 方法,由于是受保护的,要使用对象的 clone 方法,还需要重写该方法,但是光是重写该方法并没用,还需要让 Java 虚拟机知道该类是支持 clone的,所以类需要实现 Cloneable 接口。

不实现 Cloneable 接口,而调用 clone 方法,会报错:

<img src="https://pic3.zhimg.com/v2-4c41faa459c5c0bb9c2723649f9502ea_b.png" data-rawwidth="1284" data-rawheight="68" class="origin_image zh-lightbox-thumb" width="1284" data-original="https://pic3.zhimg.com/v2-4c41faa459c5c0bb9c2723649f9502ea_r.png">

关于 clone ,最主要的应该还是浅拷贝和深拷贝。


浅拷贝

ShallowCopy.java

public class ShallowCopy implements Cloneable {
    private String name;
    private ArrayList<String> list = new ArrayList<>();
    public void printlnName() {
        System.out.println(this.name);
    }
    public void setName(String name) {
        this.name = name;
    }
    public void addListValue(String value) {
        this.list.add(value);
    }
    public void printlnList() {
        System.out.println(this.list);
    }
    public ShallowCopy() {
        System.out.println("shallow copy test");
    }
    @Override
    protected ShallowCopy clone() throws CloneNotSupportedException {
        return (ShallowCopy) super.clone();
    }
    public static void main(String[] args) throws CloneNotSupportedException {
        ShallowCopy shallow = new ShallowCopy();
        shallow.setName("yhx");
        shallow.addListValue("Java");
        
        shallow.printlnName();  // 输出 yhx
        shallow.printlnList();  // 输出 Java
        
        ShallowCopy shallowCopy = shallow.clone(); // 克隆
        // 打印出两个对象的地址
        System.out.println(shallow);
        System.out.println(shallowCopy);
        
        shallowCopy.printlnList(); // 输出 Java
        
        shallowCopy.addListValue("Python");
        shallowCopy.printlnList(); // 输出 Java,Python
        shallowCopy.printlnName(); // 输出 yhx
        
        shallowCopy.setName("hello");
        shallow.printlnName(); // 输出 yhx
        shallow.printlnList(); // // 输出 Java,Python
    }
}

&amp;amp;amp;lt;img src="https://pic2.zhimg.com/v2-97fa1832df9d39fb137c085a64a728d1_b.png" data-rawwidth="654" data-rawheight="338" class="origin_image zh-lightbox-thumb" width="654" data-original="https://pic2.zhimg.com/v2-97fa1832df9d39fb137c085a64a728d1_r.png"&amp;amp;amp;gt;从结果中我们可以看到

从结果中我们可以看到

  1. 拷贝时候虽然创建了新的对象,但是并没有调用构造方法
  2. 对象中的引用对象并没有拷贝,引用的地址还是和原对象一致
  3. 基本类型或者 String 默认会拷贝

像这种只拷贝了对象本身,而对象中引用数据类型没有被拷贝的拷贝方式,叫做浅拷贝。

浅拷贝往往存在一定的风险,因为引用对象的地址拷贝前后一致,所以对象的值很容易被更改,不安全。


深拷贝

将上面的代码简单修改下就成了深拷贝
DeepCopy.java

public class DeepCopy implements Cloneable{
    private String name;
    private ArrayList<String> list = new ArrayList<>();
    public void printlnName() {
        System.out.println(this.name);
    }
    public void setName(String name) {
        this.name = name;
    }
    public void addListValue(String value) {
        this.list.add(value);
    }
    public void printlnList() {
        System.out.println(this.list);
    }
    public DeepCopy() {
        System.out.println("deep copy test");
    }
    @Override
    protected DeepCopy clone() throws CloneNotSupportedException {
        DeepCopy clone = (DeepCopy) super.clone();
        clone.list = (ArrayList<String>) this.list.clone();
        return clone;
    }
    public static void main(String[] args) throws CloneNotSupportedException {
        DeepCopy deep = new DeepCopy();
        deep.setName("yhx");
        deep.addListValue("Java");
        deep.printlnName(); 
        deep.printlnList();  
        DeepCopy DeepCopy = deep.clone(); // 克隆
        // 打印出两个对象的地址
        System.out.println(deep);
        System.out.println(DeepCopy);
        DeepCopy.printlnList(); 
        DeepCopy.addListValue("Python");
        DeepCopy.printlnList(); 
        DeepCopy.printlnName(); 
        DeepCopy.setName("hello");
        deep.printlnName(); 
        deep.printlnList(); 
    }
}

打印结果:

&amp;amp;amp;lt;img src="https://pic3.zhimg.com/v2-1fc719bc7db6b68c04fc7b6d9fb80406_b.png" data-rawwidth="588" data-rawheight="342" class="origin_image zh-lightbox-thumb" width="588" data-original="https://pic3.zhimg.com/v2-1fc719bc7db6b68c04fc7b6d9fb80406_r.png"&amp;amp;amp;gt;

可以看到,原先对象中 list 的值并不会因为拷贝对象的 list 值改变而改变,因为它们存于两个不同的内存了,不会共享。

Note:
如果字段上有 final 修饰,就不能实现 clone 方法了,因为 final 变量不能再次被赋值。



应用场景

原型模式中会使用到拷贝。




作者: Be a funny man
转载请注明出处