文档章节

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

一别丶经年
 一别丶经年
发布于 2018/08/04 19:42
字数 1127
阅读 53
收藏 9

对象克隆

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反射与内省》一文中的属性拷贝方法。

源码

© 著作权归作者所有

一别丶经年
粉丝 25
博文 36
码字总数 56214
作品 0
徐汇
架构师
私信 提问
Java中如何克隆集合——ArrayList和HashSet深拷贝

编程人员经常误用各个集合类提供的拷贝构造函数作为克隆,,,或者其他集合实现的方法。需要记住的是,Java集合的拷贝构造函数只提供浅拷贝而不是深拷贝,这意味着存储在原始List和克隆List中...

markGao
2014/04/18
1K
0
Java中如何克隆集合——ArrayList和HashSet深拷贝

编程人员经常误用各个集合类提供的拷贝构造函数作为克隆List,Set,ArrayList,HashSet或者其他集合实现的方法。需要记住的是,Java集合的拷贝构造函数只提供浅拷贝而不是深拷贝,这意味着存...

LCZ777
2014/04/14
76
0
Java中的克隆Cloneable

浅克隆与深克隆 当拷贝一个变量时,原始变量与拷贝变量引用了同一个对象。那么当改变一个变量所引用的对象时,就会对另一个变量产生影响。形象化一点说,就像某人A有一把遥控,用来控制电视,...

静水楼台
2013/06/04
219
0
java编程学习:设计模式之原型模式!

Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互...

Java小辰
2018/06/01
0
0
浅谈BeanUtils的拷贝,深度克隆

1、BeanUtil本地简单测试 在项目中由于需要对某些对象进行深度拷贝然后进行持久化操作,想到了apache和spring都提供了BeanUtils的深度拷贝工具包,自己写了几个Demo做测试,定义了两个类Use...

xiaomin0322
2018/11/30
5.3K
0

没有更多内容

加载失败,请刷新页面

加载更多

PostgreSQL参数search_path影响及作用

search_path稍微熟悉PG就会用到,用法这里就不必讲,本篇主要讲它在程序里怎样处理。 1、GUC参数定义 这是个 config_string 参数 {{"search_path", PGC_USERSET, CLIENT_CONN_STATEMENT,...

有理想的猪
53分钟前
8
0
Qt程序各个平台打包发布及安装程序大全

本文链接:https://blog.csdn.net/zhengtianzuo06/article/details/78468111 通用: 1.准备图标 图标可以直接使用一般格式的图片制作, 比如jpg, png等 推荐使用Photoshop制作原始图 推荐使用I...

shzwork
今天
4
0
springboot2.0 maven打包分离lib,resources

springboot将工程打包成jar包后,会出现获取classpath下的文件出现测试环境正常而生产环境文件找不到的问题,这是因为 1、在调试过程中,文件是真实存在于磁盘的某个目录。此时通过获取文件路...

陈俊凯
今天
22
0
BootStrap

一、BootStrap 简洁、直观、强悍的前端开发框架,让web开发更加迅速、简单 中文镜像网站:http://www.bootcss.com 用于开发响应式布局、移动设备优先的WEB项目 1、使用boot 创建文件夹,在文...

wytao1995
今天
10
0
小知识:讲述Linux命令别名与资源文件的区别

别名 别名是命令的快捷方式。为那些需要经常执行,但需要很长时间输入的长命令创建快捷方式很有用。语法是: alias ppp='ping www.baidu.com' 它们并不总是用来缩短长命令。重要的是,你将它...

老孟的Linux私房菜
今天
18
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部