Object中的clone方法
Java中对象的创建
填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。
复制对象 or 复制引用
- Person p = new Person(23, “zhang”);
- Person p1 = p;
- System.out.println(p);
- System.out.println(p1);
Person p = new Person(23, "zhang");
Person p1 = p;
System.out.println(p);
System.out.println(p1);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
当Person p1 = p;执行之后, 是创建了一个新的对象吗? 首先看打印结果:
com.pansoft.zhangjg.testclone.Person@2f9ee1ac
– String源码分析 , 其中有一节讲到了引用和对象的区分)。上面代码执行完成之后, 内存中的情景如下图所示:
Person p = new Person(23, "zhang");
Person p1 = (Person) p.clone();
System.out.println(p);
System.out.println(p1);
从打印结果可以看出,两个对象的地址是不同的,也就是说创建了新的对象, 而不是把原对象的地址赋给了一个新的引用变量:
com.pansoft.zhangjg.testclone.Person@67f1fba0
深拷贝 or 浅拷贝
public class Person implements Cloneable{
private int age ;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public Person() {}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return (Person)super.clone();
}
}
Person p = new Person(23, "zhang");
Person p1 = (Person) p.clone(); String result = p.getName() == p1.getName()
? "clone是浅拷贝的" : "clone是深拷贝的"; System.out.println(result);
覆盖Object中的clone方法, 实现深拷贝
static class Body implements Cloneable{
public Head head;
public Body() {}
public Body(Head head) {this.head = head;}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
static class Head /*implements Cloneable*/{
public Face face;
public Head() {}
public Head(Face face){this.face = face;}
}
public static void main(String[] args) throws CloneNotSupportedException {
Body body = new Body(new Head());
Body body1 = (Body) body.clone();
System.out.println("body == body1 : " + (body == body1) );
System.out.println("body.head == body1.head : " + (body.head == body1.head));
}
在以上代码中, 有两个主要的类, 分别为Body和Face, 在Body类中, 组合了一个Face对象。当对Body对象进行clone时, 它组合的Face对象只进行浅拷贝。打印结果可以验证该结论:
body.head == body1.head : true
static class Body implements Cloneable{
public Head head;
public Body() {}
public Body(Head head) {this.head = head;}
@Override
protected Object clone() throws CloneNotSupportedException {
Body newBody = (Body) super.clone();
newBody.head = (Head) head.clone();
return newBody;
}
}
static class Head implements Cloneable{
public Face face;
public Head() {}
public Head(Face face){this.face = face;}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Body body = new Body(new Head());
Body body1 = (Body) body.clone();
System.out.println("body == body1 : " + (body == body1) );
System.out.println("body.head == body1.head : " + (body.head == body1.head));
}
打印结果为:
body.head == body1.head : false
真的是深拷贝吗
static class Body implements Cloneable{
public Head head;
public Body() {}
public Body(Head head) {this.head = head;}
@Override
protected Object clone() throws CloneNotSupportedException {
Body newBody = (Body) super.clone();
newBody.head = (Head) head.clone();
return newBody;
}
}
static class Head implements Cloneable{
public Face face;
public Head() {}
public Head(Face face){this.face = face;}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
static class Face{}
public static void main(String[] args) throws CloneNotSupportedException {
Body body = new Body(new Head(new Face()));
Body body1 = (Body) body.clone();
System.out.println("body == body1 : " + (body == body1) );
System.out.println("body.head == body1.head : " + (body.head == body1.head));
System.out.println("body.head.face == body1.head.face : " + (body.head.face == body1.head.face));
}
打印结果为:
body.head == body1.head : false
body.head.face == body1.head.face : true
如何进行彻底的深拷贝
static class Head implements Cloneable{
public Face face;
public Head() {}
public Head(Face face){this.face = face;}
@Override
protected Object clone() throws CloneNotSupportedException {
//return super.clone();
Head newHead = (Head) super.clone();
newHead.face = (Face) this.face.clone();
return newHead;
}
}
static class Face implements Cloneable{
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
再次运行上面的示例,得到的运行结果如下:
body.head == body1.head : false
body.head.face == body1.head.face : false
内存结构如下图:
写在最后
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class MyUtil {
private MyUtil() {
throw new AssertionError();
}
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T obj) throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bout);
oos.writeObject(obj);
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bin);
return (T) ois.readObject();
// 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义
// 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这一点不同于对外部资源(如文件流)的释放
}
}
下面是测试代码:
import java.io.Serializable;
/**
* 人类
* @author 骆昊
*
*/
class Person implements Serializable {
private static final long serialVersionUID = -9102017020286042305L;
private String name; // 姓名
private int age; // 年龄
private Car car; // 座驾
public Person(String name, int age, Car car) {
this.name = name;
this.age = age;
this.car = car;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
}
}
/**
* 小汽车类
* @author 骆昊
*
*/
class Car implements Serializable {
private static final long serialVersionUID = -5713945027627603702L;
private String brand; // 品牌
private int maxSpeed; // 最高时速
public Car(String brand, int maxSpeed) {
this.brand = brand;
this.maxSpeed = maxSpeed;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
@Override
public String toString() {
return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]";
}
}
class CloneTest {
public static void main(String[] args) {
try {
Person p1 = new Person("Hao LUO", 33, new Car("Benz", 300));
Person p2 = MyUtil.clone(p1); // 深度克隆
p2.getCar().setBrand("BYD");
// 修改克隆的Person对象p2关联的汽车对象的品牌属性
// 原来的Person对象p1关联的汽车不会受到任何影响
// 因为在克隆Person对象时其关联的汽车对象也被克隆了
System.out.println(p1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,
这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。
让问题在编译的时候暴露出来总是好过把问题留到运行时。
Object中的clone方法的更多相关文章
- 详解Java中的clone方法:原型模式
转:http://developer.51cto.com/art/201506/478985.htm clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的 ...
- 转:Java中的Clone()方法详解
Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那 ...
- 详解Java中的clone方法
详解Java中的clone方法 参考:http://blog.csdn.net/zhangjg_blog/article/details/18369201/ 所谓的复制对象,首先要分配一个和源对象同样 ...
- Java中的clone方法-理解浅拷贝和深拷贝
最近学到Java虚拟机的相关知识,更加能理解clone方法的机制了 java中的我们常常需要复制的类型有三种: 1:8种基本类型,如int,long,float等: 2:复合数据类型(数组): 3:对 ...
- 详解Java中的clone方法 -- 原型模式
转自: http://blog.csdn.net/zhangjg_blog/article/details/18369201 Java中对象的创建 clone顾名思义就是复制, 在Java语言中, ...
- java中的clone方法
Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那 ...
- java Object对象的clone方法
参考copy链接:http://blog.csdn.net/bigconvience/article/details/25025561 在看原型模式,发现要用到clone这个方法,以前和朋友聊过,没怎 ...
- JQ中的clone()方法与DOM中的cloneNode()方法
JQ中的clone()方法与DOM中的cloneNode()方法 cloneNode()定义和用法 cloneNode()方法创建节点的拷贝,并返回该副本. 语法: node.cloneNode(de ...
- jquery中的clone()方法使用
clone([Even[,deepEven]]) 描述: 克隆匹配的DOM元素并且选中这些克隆的副本. 在想把DOM文档中元素的副本添加到其他位置时这个函数非常有用. 1:一个布尔值(true 或者 ...
随机推荐
- python之读取文件的测试数据
假设我们有一个叫testdata.txt的文件,现在在这个文件里面有测试数据,我们怎么利用前2小章学习的知识,读取测试数据呢? 测试数据如下: url:https://www.cnblogs.com/ ...
- VMware下安装CentOS7 无法通过桥接模式进行联网
#修改配置# vi /etc/sysconfig/network-scripts/ifcfg-ens33 (ifcfg-ens33,不同的机器名称不一样,但大都为ifcfg-ensxxx) 把文件中 ...
- 【算法笔记】B1046 划拳
1046 划拳 (15 分) 划拳是古老中国酒文化的一个有趣的组成部分.酒桌上两人划拳的方法为:每人口中喊出一个数字,同时用手比划出一个数字.如果谁比划出的数字正好等于两人喊出的数字之和,谁就赢了,输 ...
- 江西理工大学南昌校区排名赛 B: 单身狗的数字游戏
题目描述 萌樱花是一只单身狗. 萌樱花今天初始化了N个数字,使它们都为0 0 0 0... 萌樱花决定选取某一个位置,分别增加1到N(如果到达数组末尾就从头开始). 比如: 0 0 0(选择第二个位置 ...
- [转] Jenkins Pipeline插件十大最佳实践
[From] http://blog.didispace.com/jenkins-pipeline-top-10-action/ Jenkins Pipeline 插件对于 Jenkins 用户来说可 ...
- 【研究】Discuz<3.4任意文件删除漏洞
这里以Discuz3.2为例 关键字:Powered by Discuz! X3.2 时间有限,就不一一截图了,Discuz所有页面全在Discuz_X3.2_SC_UTF8/upload/目录下 利 ...
- PIE SDK栅格图层渲染变化事件监听
1. 功能简介 通过PIE SDK加载图层后,会默认的赋值给数据一个渲染.当用户重新给数据赋值Render或改变数据显示效果时,会触发渲染变化事件. 所谓的事件监听是在事件触发时,将执行用户指定的函数 ...
- android主线程ActivityThread
ActivityThread在Android中它就代表了Android的主线程,但是并不是一个Thread类. 源码如下: http://androidxref.com/6.0.0_r1/xref/f ...
- js动画实现&&回调地狱&&promise
1. js实现动画 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...
- 这真的该用try-catch吗?
前言 我有个技能,就是把“我”说的听起来特别像“老子”. 以前是小喽啰的时候,会跟领导说“我!不加班.”,听起来就像“老子不加班!”一样.到最后发现,我确实没有把计划内的工作拖到需要加班才能完成,这个 ...