再谈java clone 以及 浅/深拷贝
简单对象的拷贝,直接使用其clone方法 即可, 不会有什么问题:
class Dog implements Cloneable
public Dog clone() {
int age;
String name;
// getter setter
Dog myDog = null;
try {
myDog = (Dog) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return myDog;
}
// any test ...
如此简单!
不过,如果对象有嵌套,我们还是使用这个做法(浅拷贝), 不注意就会出问题:
package design.creator.prototype; import java.util.ArrayList;
import java.util.List; /**
* 浅拷贝:
*/
class Dog implements Cloneable {
int age;
String name;
List list;
Pojo pojo; public Dog(String tempName) {
name = tempName;
list = new ArrayList<>();
list.add();
list.add();
list.add();
pojo = new Pojo();
pojo.setV1();
pojo.setV2("aa");
} // public void ShowName() {
// System.out.println(name);
// } public Dog clone() {
Dog myDog = null;
try {
myDog = (Dog) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return myDog;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getName() {
return name;
} public List getList() {
return list;
} public void setList(List list) {
this.list = list;
} @Override
public String toString() {
return "Dog [age=" + age + ", name=" + name +
", list=" + list + "]" + ", pojo=" + pojo + "]";
} public Pojo getPojo() {
return pojo;
} public void setPojo(Pojo pojo) {
pojo = pojo;
} } class Pojo {
int v1;
String v2;
public int getV1() {
return v1;
}
public void setV1(int v1) {
this.v1 = v1;
}
public String getV2() {
return v2;
}
public void setV2(String v2) {
this.v2 = v2;
}
@Override
public String toString() {
return "Pojo [v1=" + v1 + ", v2=" + v2 + "]";
} } public class PrototypeTest {
public static void main(String[] args) {
Dog myDog = new Dog("热狗");
myDog.setAge();
Dog newDog = (Dog) myDog.clone(); System.out.println(" myDog " + myDog );
System.out.println(" newDog " + newDog ); // myDog.ShowName();
// newDog.ShowName(); myDog.setName("aaaaa");
myDog.setAge();
myDog.getList().clear();
myDog.getPojo().setV1();
myDog.getPojo().setV2("bbb"); // myDog.ShowName();
// newDog.ShowName(); System.out.println(" myDog " + myDog );
System.out.println(" newDog " + newDog );
}
}
打印
myDog Dog [age=, name=热狗, list=[, , ]], pojo=Pojo [v1=, v2=aa]]
newDog Dog [age=, name=热狗, list=[, , ]], pojo=Pojo [v1=, v2=aa]]
myDog Dog [age=, name=aaaaa, list=[]], pojo=Pojo [v1=, v2=bbb]]
newDog Dog [age=, name=热狗, list=[]], pojo=Pojo [v1=, v2=bbb]]
你可能不懂为什么newDog 的name没变化,而newDog 的pojo、list都发生了变化—— 原来java 的clone 方法把 String当做了普通字段并进行了深复制, 而其他对象类型数据仍然的浅复制。
那么正确的做法是:
package design.creator.prototype; import java.util.ArrayList;
import java.util.List; /**
* 深度拷贝:
*/
class Dog implements Cloneable {
int age;
String name;
List list;
Pojo pojo; public Dog(String tempName) {
name = tempName;
list = new ArrayList<>();
list.add();
list.add();
list.add();
pojo = new Pojo();
pojo.setV1();
pojo.setV2("aa");
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getName() {
return name;
} public List getList() {
return list;
} public void setList(List list) {
this.list = list;
} @Override
public String toString() {
return "Dog [age=" + age + ", name=" + name +
", list=" + list + "]" + ", pojo=" + pojo + "]";
} public Pojo getPojo() {
return pojo;
} public void setPojo(Pojo pojo) {
pojo = pojo;
} public Dog clone() {
Dog myDog = null;
try {
myDog = (Dog) super.clone();
myDog.list = (List) ((ArrayList)myDog.list).clone(); // 想要调用其clone方法,必须1 implements Cloneable 2 对其重写clone
myDog.pojo = (Pojo) myDog.pojo.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return myDog;
}
} class Pojo implements Cloneable{
int v1;
String v2;
public int getV1() {
return v1;
}
public void setV1(int v1) {
this.v1 = v1;
}
public String getV2() {
return v2;
}
public void setV2(String v2) {
this.v2 = v2;
}
@Override
public String toString() {
return "Pojo [v1=" + v1 + ", v2=" + v2 + "]";
} @Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
} } public class PrototypeTest {
public static void main(String[] args) {
Dog myDog = new Dog("热狗");
myDog.setAge();
Dog newDog = (Dog) myDog.clone(); System.out.println(" myDog " + myDog );
System.out.println(" newDog " + newDog ); // myDog.ShowName();
// newDog.ShowName(); myDog.setName("aaaaa");
myDog.setAge();
myDog.getList().clear();
myDog.getPojo().setV1();
myDog.getPojo().setV2("bbb"); // myDog.ShowName();
// newDog.ShowName(); System.out.println(" myDog " + myDog );
System.out.println(" newDog " + newDog );
}
}
总结:
clone 方法只是浅拷贝,也就是说他只对象的第一层属性进行拷贝,其对象中的对象是不会进行重新拷贝的, 而仅仅只是拷贝那个引用。 如果对象有嵌套,那么需要注意重写相关步骤,使用 深拷贝。
参考:
http://blog.csdn.net/jariwsz/article/details/8588570
http://blog.csdn.net/xiaofengcanyuexj/article/details/23212189
再谈java clone 以及 浅/深拷贝的更多相关文章
- 沉淀再出发:再谈java的多线程机制
沉淀再出发:再谈java的多线程机制 一.前言 自从我们学习了操作系统之后,对于其中的线程和进程就有了非常深刻的理解,但是,我们可能在C,C++语言之中尝试过这些机制,并且做过相应的实验,但是对于ja ...
- 再谈Java数据结构—分析底层实现与应用注意事项
在回顾js数据结构,写<再谈js对象数据结构底层实现原理-object array map set>系列的时候,在来整理下java的数据结构. java把内存分两种:一种是栈内存,另一种是 ...
- Java clone克隆方法 --深拷贝--浅拷贝 --原型模型
什么是深拷贝? 什么是浅拷贝? 创建一个对象的方法有几种? 默认的Object方法中的clone是深拷贝还是浅拷贝? 为什么说很多深拷贝都是不彻底的深拷贝? 什么是原型模型,什么是原型模式? 原型模型 ...
- 再谈java两种变量(基本类型和引用类型)(综合各路大神)
基本类型: 基本类型自然不用说了,它的值就是一个数字,一个字符或一个布尔值. int a: a=250: //声明变量a的同时,系统给a分配了数据空间. 引用类型: 是一个对象类型,值是什么呢? ...
- 记一次synchronized锁字符串引发的坑兼再谈Java字符串
问题描述 业务有一个需求,我把问题描述一下: 通过代理IP访问国外某网站N,每个IP对应一个固定的网站N的COOKIE,COOKIE有失效时间.并发下,取IP是有一定策略的,取到IP之后拿IP对应的C ...
- 记一次 synchronized 锁字符串引发的坑兼再谈 Java 字符串
业务有一个需求,我把问题描述一下: 通过代理IP访问国外某网站N,每个IP对应一个固定的网站N的COOKIE,COOKIE有失效时间. 并发下,取IP是有一定策略的,取到IP之后拿IP对应的COOKI ...
- Java知多少(25)再谈Java包
在Java中,为了组织代码的方便,可以将功能相似的类放到一个文件夹内,这个文件夹,就叫做包. 包不但可以包含类,还可以包含接口和其他的包. 目录以"\"来表示层级关系,例如 E:\ ...
- [Java学习] 再谈Java包
在Java中,为了组织代码的方便,可以将功能相似的类放到一个文件夹内,这个文件夹,就叫做包. 包不但可以包含类,还可以包含接口和其他的包. 目录以"\"来表示层级关系,例如 E:\ ...
- 三. Java类与对象8.再谈Java包
在Java中,为了组织代码的方便,可以将功能相似的类放到一个文件夹内,这个文件夹,就叫做包. 包不但可以包含类,还可以包含接口和其他的包. 目录以"\"来表示层级关系,例如 E:\ ...
随机推荐
- android利用provider查询同一个数据库中没有对外暴露出来的表
[原创]转载请加本篇博客地址http://www.cnblogs.com/scarecrow-blog/p/6266042.html 个人感觉这是android provider 的一个漏洞, 废话少 ...
- Java课程设计(2019版)
参考资料 Java课程设计参考资料(2018-12-26更新) Java课程设计常见问题(程序部署.数据库.JSP) 项目开发参考-阿里巴巴Java开发手册(正式版) 更多参考资料请查看QQ群文件中的 ...
- elasticsearch mapping demo
curl -XPUT localhost:9200/local -d '{ "settings" : { "analysis" : { "analyz ...
- 廖雪峰Java3异常处理-1错误处理-1Java的异常
1.计算机运行中的错误 在计算机程序运行的过程中,错误总会出现,不可避免的 用户输入错误 读写文件错误 网络错误.内存耗尽.无法连接打印机不可 String s = "abc"; ...
- [UE4]在AIController中使用行为树
行为树会在Root根下面的每个子节点中从左右到右来回往复循环执行.
- ha环境下重新格式化hdfs报错
datanode启动不成功,如下所示,我的136,137.138都是datanode,都启动不了. 查看datanode日志文件发现报错: 一个报错Incompatible clusterIDs in ...
- 解决sql中上下左右backspace不能用的方法
一. 解决输入 BACKSPACE 键变成 ^h 的问题 #su - oracle $stty erase ^h. 要永久生效,可以加入到用户环境配置文件 .bash_profile 中 , 加入如下 ...
- Redis进阶实践之十三 Redis的Redis-trib.rb脚本文件使用详解
转载来源:http://www.cnblogs.com/PatrickLiu/p/8484784.html 一.简介 事先说明一下,本篇文章不涉及对redis-trib.rb源代码的分析,只是从使用的 ...
- POJ3159 Candies
#include <iostream> #include <queue> #include <cstring> #define maxn 30005 #define ...
- Kaptcha
Kaptcha:google自动生成验证码组件 kaptcha的使用比较方便,只需添加jar包依赖之后简单地配置就可以使用了 kaptcha所有配置都可以通过web.xml来完成,如果你的项目中使用了 ...