美文网首页Java拾遗
Java拾遗:008 - 对象克隆与浅拷贝、深拷贝

Java拾遗:008 - 对象克隆与浅拷贝、深拷贝

作者: ed72fd6aaa3c | 来源:发表于2018-08-04 19:42 被阅读23次

对象克隆

Object类中有一个方法叫clone,完整代码

    protected native Object clone() throws CloneNotSupportedException;

首先它是一个Native方法,而且是受保护的(protected),抛出一个CloneNotSupportedException异常(JDK1.8)。

通常程序员自己定义的类不能直接调用clone方法,如果要在外部调用,需要重写该方法

static class FullName {
    private String firstName;
    private String lastName;

    public FullName(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

static class User implements Cloneable {
    private FullName name;
    private int age;

    @Override
    public User clone() throws CloneNotSupportedException {
        return (User) super.clone();
    }

}

上述示例中直接调用了父类(Object)的clone方法,因为是一个本地方法,所以性能较好,如果没有特殊需求,不需要改写。该方法在重写时可以修改方法访问权限和返回值类型。

Java中还有一个接口java.lang.Cloneable,该接口实际只是一个标记接口(不包含任何方法),与java.io.Serializable接口类似,只是告知外部程序,当前类可以调用clone方法。

浅拷贝

测试一下上述代码中的克隆方法

    @Test
    public void test() throws CloneNotSupportedException {

        // 构造一个对象
        User user = new User();
        user.age = 17;
        user.name = new FullName("Jack", "Jones");

        // 克隆对象
        User other = user.clone();

        // 检查克隆结果
        // 克隆的对象非空
        assertNotNull(other);
        // 克隆的对象与原对象不再是同一个对象
        assertFalse(user == other);
        // 克隆后基本类型被克隆,所以原对象修改该属性值不影响非克隆后的对象属性
        user.age = 18;
        assertEquals(17, other.age);
        // 克隆后引用类型仅克隆了引用,而引用对象没有被克隆,所以两者指向同一个对象
        assertTrue(user.name == other.name);

    }

通过测试可以看出,对象被成功克隆了,克隆后的对象与原对象不是同一个对象(使用==判断),基本类型的属性也被克隆了,修改原对象的属性值,克隆后的属性值并不受影响,但引用类型的属性却没有被克隆(实际克隆了引用,但引用的对象没有克隆),这种克隆方式就叫浅拷贝。
浅拷贝就是只克隆对象本身,对象引用的其它对象则不拷贝。浅拷贝性能较好,对象内部的引用类型如不会修改,则使用浅拷贝足以。

深拷贝

如果克隆对象时,连同其引用类型属性引用的对象也克隆,则叫深拷贝。
下面是针对上述代码实现的深拷贝

    public User deepClone() throws CloneNotSupportedException {
        // 先用Object中的克隆方法(底层方法,性能相对更好)
        User other = (User) super.clone();
        // 将引用类型再克隆一次(注意如果引用类型中还有引用类型,那么同样需要克隆)
        // 多层克隆实现会比较麻烦,简单的做法是先序列化再反序列化一次即可(缺点是序列化的性能通常较差)
        if (other.name != null) {
            // 由于FullName类没有实现克隆方法,所以这里直接重新new一个
            other.name = new FullName(this.name.firstName, this.name.lastName);
        }
        return other;
    }

对其进行相同测试

    @Test
    public void test() throws CloneNotSupportedException {

        // 构造一个对象
        User user = new User();
        user.age = 17;
        user.name = new FullName("Jack", "Jones");

        // 如果要实现深拷贝,需要将引用的对象也拷贝
        User other = user.deepClone();

        // 测试深拷贝效果
        assertFalse(user == other);
        assertEquals(user.age, other.age);
        assertTrue(user.name != other.name);
    }

可以看到引用对象也被拷贝了,克隆后的对象的引用类型属性与原对象同名引用类型属性引用的对象不再是同一个对象了。

深拷贝的一个特殊情形是如果克隆对象的引用类型属性引用的对象内部又包含引用类型,那么就形成了多层拷贝,这种情况下进行深拷贝代码实现会复杂很多,所以有时为了简化实现过程会使得序列化机制,先序列化,再反序列化,得到的新对象与原对象会完全无关,从而实现深拷贝,但如无必要一般不建议使用,因为序列化通常性能比较差。

也可以使用反射实现一个通用的克隆方法,实际就是《Java拾遗:006 - Java反射与内省》一文中的属性拷贝方法。

源码

相关文章

  • Java基础 - 深拷贝和浅拷贝

    Java 的深拷贝和浅拷贝 什么是深拷贝、浅拷贝 (深克隆、浅克隆)? 在 Java 中,数据类型分为 基本数据类...

  • java克隆

    java克隆 java克隆分为浅克隆和深克隆,概念如下: 浅拷贝(浅克隆)克隆出来的对象的所有变量都含有与原来的对...

  • java 对象的拷贝

    拷贝:即复制 对象拷贝:即对象复制 java 对象拷贝分类:浅拷贝、深拷贝 java 对象的浅拷贝和深拷贝针对包含...

  • Java拾遗:008 - 对象克隆与浅拷贝、深拷贝

    对象克隆 Object类中有一个方法叫clone,完整代码 首先它是一个Native方法,而且是受保护的(prot...

  • 五、面试总结(五)

    对象 拷贝(clone) 如何实现对象克隆 深拷贝和浅拷贝区别 深拷贝和浅拷贝如何实现激活机制 写clone()方...

  • Java 之浅拷贝、深拷贝,你到底知多少?

    在 Java 开发中,对象拷贝或者说对象克隆是常有的事,对象克隆最终都离不开直接赋值、浅拷贝、深拷贝 这三种方式,...

  • 深克隆--何时是尽头?

    As we all know Java有两种拷贝,浅拷贝和深拷贝,高大上的叫法也叫浅克隆和深克隆。 深克隆有时会碰...

  • Java 对象克隆、深拷贝、浅拷贝

    Java 对象克隆、深拷贝、浅拷贝 背景 前一阵子在测试的时候,开发小哥因为需要缓存一个比较常用的对象,故此保存了...

  • Java的浅拷贝与深拷贝

    Java的浅拷贝与深拷贝 一、概念 浅拷贝浅拷贝仅仅复制所考虑的对象(包括对象中的基本变量),而不复制它所引用的对...

  • 深浅拷贝,面向对象,Symbol,Set,Map

    一:浅拷贝与深拷贝对象的浅拷贝:浅拷贝是对象共用的一个内存地址,对象的变化相互影响。对象的深拷贝:简单理解深拷贝是...

网友评论

    本文标题:Java拾遗:008 - 对象克隆与浅拷贝、深拷贝

    本文链接:https://www.haomeiwen.com/subject/ivgmvftx.html