码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • Java对象深拷贝 终极方案 deep clone an object


    Java对象深拷贝 终极方案

      • 定义 深拷贝
      • 深拷贝常见误区
        • `spring / apache commons ` 等工具类的 `BeanUtils.copy` 方法 ❌
      • 正确做法: 上中下3策 ✔
        • json 序列化 (用jackson,别用其他的gson/fastjson/json-lib 等,不解释)
          • objectMapper 工具类初始化
          • 1. 对象 <===> json字符串 互相转换 (下策)
          • 2. 对象 <===> jsonNode 互相转换 (中策)
          • 3. 对象 <===> TokenBuffer 互相转换 (上策)

    定义 深拷贝

    • 必须是全新的对象,object堆内存地址是全新的;
    • 该对象的各个属性值 prop Value,也是全新的,指向全新的堆内存地址。
    • 作用: 原有旧对象和新对象,可以独立修改各自的属性值,互相之间没影响。
    • 场景:
      • java的method 参数传递的是内存地址,也就是对象的引用句柄,只有 primitive 基本类型和String 作为参数时,传递的才是值
      • 某个对象,需要作为 多个方法的参数,进行不同的操作。
        为了防止这个原始对象的属性值被意外修改,就需要 深拷贝为新对象。
        操作新对象,不会对 原始对象有任何影响。
      • 该对象作为参数进行传递的次数越多,因为属性的内存地址都是一样的,属性值被修改的风险就越高

    深拷贝常见误区

    spring / apache commons 等工具类的 BeanUtils.copy 方法 ❌

    • 查看源码可知,使用的是 对象的get/set 方式实现的
    • 对象虽然是新的,但是 属性值的内存地址是相同的
    • 修改新对象的属性值,会同时影响原有对象的属性值
    • 如果属性是 特殊类型比如map 或者 list 或者 嵌套对象属性,可能就不好使了
      在这里插入图片描述

    正确做法: 上中下3策 ✔

    json 序列化 (用jackson,别用其他的gson/fastjson/json-lib 等,不解释)

    类似 java 的对象序列化和反序列化过程( object Serialization & deserialization ),产生的是全新的对象
    不同的是,java 对象序列化需要落地为磁盘文件,jackson 序列化则正常运行在jvm内存中。

    Jackson 是个神奇的东东,共有3种方式可实现深拷贝,来源

    objectMapper 工具类初始化
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.JavaType;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.node.ArrayNode;
    import com.fasterxml.jackson.databind.node.ObjectNode;
    import com.fasterxml.jackson.databind.util.TokenBuffer;
    import lombok.SneakyThrows;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.PostConstruct;
    import java.util.List;
    
    @Slf4j
    @Component
    public class JacksonUtils {
        @Autowired
        ObjectMapper objectMapper;
        private static JacksonUtils MAPPER;
    
        /**
         * 非 Controller 层使用 @Autowired 方式注入 spring 初始化好的全局单例  objectMapper
         * 无需自己手动 new ObjectMapper();
         */
        @PostConstruct
        public void init(){
            MAPPER = this; // JacksonUtils 类的单例 bean
            MAPPER.objectMapper = this.objectMapper; // spring启动过程中,会暴露该 objectMapper 单例
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    1. 对象 <===> json字符串 互相转换 (下策)
    2. 对象 <===> jsonNode 互相转换 (中策)
    3. 对象 <===> TokenBuffer 互相转换 (上策)
    /**
     * 不同class类型拷贝
     * @Param clazz 这个类,必须有1个默认的构造 方法,这是使用 Jackson 正反序列化必须的; 
     * 如果是Gson 进行正反序列化,则没有该 构造方法 的要求 : 
     * Gson 参考 https://www.baeldung.com/java-deep-copy#2-json-serialization-with-gson
     */
    @SneakyThrows
    public static <T> T deepClone(Object javaObj, Class<T> clazz) {
        ObjectMapper mapper = MAPPER.objectMapper;
    
        // method 1 : json string 🚗 . 速度最慢,但是兼容性较好
        if(javaObj instanceof String){
          return mapper.readValue(mapper.writeValueAsString(javaObj), targetClass);
        }
    
        // method 2 : jsonNode tree 🛫
        // return mapper.treeToValue(mapper.valueToTree(javaObj), clazz);
    
        // method 3 : token buffer 🚀
        TokenBuffer tb = new TokenBuffer(mapper, false);
        mapper.writeValue(tb, javaObj);
        return mapper.readValue(tb.asParser(), clazz);
    }
    
    /**
     * 相同class类型拷贝
     */
    @SneakyThrows
    public static <T> T deepClone(T source) {
        return (T) deepClone(source, source.getClass());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    初始化工具类,来源todo
    深拷贝代码参考来源

  • 相关阅读:
    基于粒子群优化的支持向量机房价预测分析
    LeetCode题解:1720. 解码异或后的数组,异或,JavaScript,详细注释
    第二周opencv
    vue实现列表自动无缝滚动列表
    微信小程序 - WXML 模板语法 - 事件绑定
    paddlepaddle使用实践过程中的问题记录
    Windows系统安装MySQL数据库详细教程
    《c++ Primer Plus 第6版》读书笔记(6)
    今天来说说Java开发中常用的框架有哪些?
    Java入门 实用类 (二)(第二十四天)
  • 原文地址:https://blog.csdn.net/w1047667241/article/details/127676699
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号