再谈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:\ ...
随机推荐
- webservice的model层命名空间不同的问题
[XmlType(Namespace = "http://tempuri.org/MyClass4")] [XmlRoot(Namespace = "ht ...
- Fiddler的学习之路
Fiddler是位于客户端和服务器端的HTTP代理,也是目前最常用的http抓包工具之一 . 它能够记录客户端和服务器之间的所有 HTTP请求,可以针对特定的HTTP请求,分析请求数据.设置断点.调试 ...
- java网络编程Socket通信详解
Java最初是作为网络编程语言出现的,其对网络提供了高度的支持,使得客户端和服务器的沟通变成了现实,而在网络编程中,使用最多的就是Socket.像大家熟悉的QQ.MSN都使用了Socket相关的技术. ...
- 黄聪:windowss7显示桌面图标设置在任务栏的解决办法
1.新建一个本文文档,将以下内容复制进去: [Shell] Command=2 IconFile=explorer.exe,3 [Taskbar] Command=ToggleDesktop 2.将该 ...
- 【ActiveMQ】之安全机制(一)管控台安全设置
ActiveMQ 管控台基于jetty,默认端口8161,默认用户名,密码都是admin,这样的安全配置过于弱化,所以我们需要修改一下 1.修改端口 找到conf/jetty.xml文件里面这一段配置 ...
- C++进阶--类的继承
//############################################################################ /* * 公有,保护,私有继承 */ cl ...
- [Easyui - Grid]为easyui的datagrid、treegrid增加表头菜单,用于显示或隐藏列
为easyui的datagrid.treegrid增加表头菜单,用于显示或隐藏列 /** * @author 孙宇 * * @requires jQuery,EasyUI * * 为datagrid. ...
- hadoop 完全分布式安装
一个完全的hadoop分布式安装至少需要3个zookeeper,3个journalnode,3个datanode,2个namenode组成. 也就是说需要11个节点,但是我云主机有限,只有3个,所以把 ...
- selectedIndex 属性
selectedIndex 属性可设置或返回下拉列表中被选选项的索引号. 注释:若允许多重选择,则仅会返回第一个被选选项的索引号. 语法 selectObject.selectedIndex=numb ...
- 怎么把PNG图的位深度24位变成32位
在PS里把图片的变成层模式,不透明度设置成99%,在保存成PNG