如何通过JVM角度谈谈Java的clone操作
前言
最近在给熔断器组件增加一个降级策略(Hystrix好像没有这个配置),我们提供了如下几种策略:
1、默认策略
2、返回常量值
3、抛出指定异常
4、执行一段groovy脚本
当然了,这些配置都是可以在平台上配置,并立即生效的。
目前返回常量值的实现如下:
今天发现,如果每次都返回同一个对象,而且这个逻辑对业务来说是黑盒(业务不知道每次拿到的对象是同一个),如果对这个对象进行了操作,那肯定会影响到其它的请求,为了避免背锅,我们需要每次返回一个新的对象,第一反应是通过clone是否可行(因为每次json的序列化也会损耗性能),但是jdk自带的clone方法只是浅克隆,如果对象中包含了另一个复杂对象,clone出来的对象还是存在被修改的风险。
详细介绍:
大家可以看下面一个例子:
class Master { String name; public Master(String name) { this.name = name; } }
初始化一个Master类
class Dog implements Cloneable { String name; int age; Master master; public Dog(String name, int age, Master master) { this.name = name; this.age = age; this.master = master; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "{name:" + name + ", age: " + age + ", master: "+ master.name + "}"; } }
再初始化一条狗
public static void main(String[] args) throws Exception { Master master = new Master("zj0"); Dog dog1 = new Dog("旺财", 1, master); Dog dog2 = (Dog)dog1.clone(); dog1.name = "比利"; dog1.master.name = "zj1"; System.out.println(dog2); }
最后运行一下,结果如下:
{name:旺财, age: 1, master: zj1}
dog1是原始狗,dog2是克隆出来的,但是我修改dog1的master的name时,克隆的master也跟着变化了,这显然不行。
虽然一直都知道Object的clone方法是浅克隆,也一直没继续探索一下,今天碰到了就顺手看下JVM的实现,好像很简单,在jvm.cpp文件中,搜索"JVM_Clone"
以前一直没用过clone这个方法,通过源码发现在运行的时候会检查类是否实现Cloneable接口,编译的时候不检查,想啥呢?
根据对象或者数据的大小,从堆中开辟一块同等大小的内存,然后把原始对象的数据都复制到新的内存地址,对于基本类型,可以把原始值复制过来,但是对于内部对象来说,其保存的只是一个地址,复制时也是对地址的复制,最终还是指向同一个对象,所以就造成了上述的问题。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。
- Ubuntu12.04安装QQ for Linux
- 树莓派:最好的安排
- idea 高级调试技巧
- JS魔法堂:彻底理解0.1 + 0.2 === 0.30000000000000004的背后
- mybatis: 利用多数据源实现分库存储
- 树莓派:文本编辑器与文件
- Java常用类(三)之StringBuffer与StringBuidler
- 使用监听器:定时清除map缓存
- 树莓派:光阴的故事
- lombok在IntelliJ IDEA下的使用
- 基础野:细说浮点数
- 微信开发如何保证access_token/jsapi_ticket长期有效
- struts2(五)之struts2拦截器与自定义拦截器
- 自定义的html radio button的样式
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Ubuntu18 给terminal改个漂亮的命令行提示符的方法
- leetcode树之将有序数组转换为二叉搜索树
- 在 Ubuntu 上安装 Protobuf 3 的教程详解
- protobuf简单介绍和ubuntu 16.04环境下安装教程
- Ubuntu解决火狐浏览器无法同步书签的问题【推荐】
- Ubuntu添加swap分区的方法
- linux文件目录管理命令整理总结
- C++核心准则SF.8:为所有的.h文件使用包含监护
- 利用logsave如何将命令输出保存起来
- CentOs下安装gcc/g++/gdb的方法
- 详解firewall的规则设置与命令(白名单设置)
- 二叉树:构造一棵最大的二叉树
- Ubuntu16.04 ext4格式硬盘挂载普通用户权限控制的操作方法
- Apache Spark 2.0 在作业完成时却花费很长时间结束
- centos 6.9 升级glibc动态库的详细过程