再谈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:\ ...
随机推荐
- Django 模板语法
模板语法之变量 变量在HTML中的表示:{{var_name}} 变量取值:句点符 "." views: def index(request): import datetime s ...
- centos 磁盘清理 /dev/vda1系统盘满了
df -h 检查一台服务器磁盘使用空间,发现磁盘已经使用了100% 思路是: 1.cd /usr 当然这里不一定是/usr目录,最好是cd到 根目录再执行下一步 2.du -sh * 看哪 ...
- 同台同时多开DELPHI2007的解决办法
Cannot create file "C:\Users\Administrator\AppData\Local\Temp\EditorLineEnds.ttr"这个问题的产生根据 ...
- STL基础--算法(已排序数据的算法,数值算法)
已排序数据的算法 Binary search, merge, set operations 每个已排序数据算法都有一个同名的更一般的形式 vector vec = {8,9,9,9,45,87,90} ...
- python3学习笔记二(注释、缩进)
注释 单行注释,用#开头即可 多行注释,用''' ''' 或""" """ 缩进 python不能像其他语言一样采用{}或者begin... ...
- uoj#209. 【UER #6】票数统计
http://uoj.ac/problem/209 当x!=y时,这个限制条件是确定的,可以枚举总通过数,用组合数计算,当x==y时,这个限制条件表示前x个全部通过或后x个全部通过,只有最大的x有用, ...
- ElasticSearch 5.0.0 安装部署常见错误或问题
1.ERROR: bootstrap checks failed [1]: max file descriptors [65535] for elasticsearch process is too ...
- java多线程——监视锁(monitor)(转)
https://blog.csdn.net/hqq2023623/article/details/51000153 java中每个对象都有唯一的一个monitor,想拥有一个对象的monitor的话有 ...
- .net core批量注入实现类
1.获取实现类程序集方法 public class RuntimeHelper { //通过程序集的名称加载程序集 public static Assembly GetAssemblyByName(s ...
- SCCM 2012 R2实战系列之二:前提工作准备
在上一篇中,我们完成了SQL Server 2012的安装和配置.现在跟大家分享SCCM安装前的准备工作. 2.1 SCCM 2012 R2 准备工作 2.1.1 创建并分配System Manage ...