一:使用目的:

就是为了快速构造一个和已有对象相同的副本。如果需要克隆对象,一般需要先创建一个对象,然后将原对象中的数据导入到新创建的对象中去,而不用根据已有对象进行手动赋值操作。

二:Object中的clone()方法

 

protected native Object clone() throws CloneNotSupportedException;

  说明:1.这是一个navtive方法  2.要使用该方法必须继承Object类,因为修饰符为protected  3.返回值为Object,需要强转

  

  使用该方法时:x.clone()!=x为true,对于基础类型来说,在堆内存中创建了一个独立且内容与之相同的内存区域.对于引用数据类型来说,克隆对象和原始对象在java 堆(heap)中是两个独立的对  象,x.clone().getClass() == x.getClass()  他们所属的类是同一个,x.clone().equals(x)   所比较的对象内容相同

    

三:深度克隆和浅度克隆

浅度克隆:被克隆得到的对象基本类型的值修改了,原对象的值不会改变

深度克隆:被克隆得到的对象基本类型的值修改了,原对象的值改变

public class ShadowClone implements Cloneable {
private int a; // 基本类型
private int[] b; // 非基本类型
// 重写Object.clone()方法,并把protected改为public
@Override
public Object clone(){
ShadowClone sc = null;
try
{
sc = (ShadowClone) super.clone();
} catch (CloneNotSupportedException e){
e.printStackTrace();
}
return sc;
}
public int getA()
{
return a;
}
public void setA(int a)
{
this.a = a;
}
public int[] getB() {
return b;
}
public void setB(int[] b) {
this.b = b;
}
}
ShadowClone c1 = new ShadowClone();
//对c1赋值
c1.setA(100) ;
c1.setB(new int[]{1000}) ; System.out.println("克隆前c1: a="+c1.getA()+" b="+c1.getB()[0]);
//克隆出对象c2,并对c2的属性A,B,C进行修改
ShadowClone c2 = (ShadowClone) c1.clone();
//对c2进行修改
c2.setA(50) ;
int []a = c2.getB() ;
a[0]=500 ;
c2.setB(a);
System.out.println("克隆前c1: a="+c1.getA()+" b="+c1.getB()[0]);
System.out.println("克隆后c2: a="+c2.getA()+ " b[0]="+c2.getB()[0]);

console:

克隆前c1: a=100 b=1000
克隆前c1: a=100 b=500
克隆后c2: a=50 b[0]=500

可见:基本类型可以使用浅克隆,而对于引用类型,由于引用的是内容相同,所以改变c2实例对象中的属性就会影响到c1。所以引用类型需要使用深克隆。另外,在开发一个不可变类的时候,如果这个不可变类中成员有引用类型,则就需要通过深克隆来达到不可变的目的。

四:重写clone方法完成深度克隆

class bottle implements Cloneable {
public wine wn; public bottle(wine wn) {
this.wn = wn;
}
// 覆写clone()方法
protected Object clone() throws CloneNotSupportedException {
bottle newBtl = (bottle) super.clone();
newBtl.wn = (wine) wn.clone();
return newBtl;
}
} class wine implements Cloneable {
int degree;
public int getDegree() {
return degree;
}
public void setDegree(int degree) {
this.degree = degree;
}
// 覆写clone()方法
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) throws CloneNotSupportedException {

    bottle bottle = new bottle(new wine());
bottle bottle1 = (bottle) bottle.clone(); System.out.println("bottle1.wine : " + bottle1.wn.getDegree() );
bottle1.wn.setDegree(100); System.out.println("bottle1.wine : " + bottle1.wn.getDegree() );
System.out.println("bottle.wine : " + bottle.wn.getDegree());
}

console:

bottle1.wine : 0
bottle1.wine : 100
bottle.wine : 0

如果wine类中多了一个String name的属性呢?

class wine implements Cloneable {
int degree;
String name="法国白兰地"; public int getDegree() {
return degree;
}
public void setDegree(int degree) {
this.degree = degree;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
} // 覆写clone()方法
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class bottle implements Cloneable {
public wine wn; public bottle(wine wn) {
this.wn = wn;
}
// 覆写clone()方法
protected Object clone() throws CloneNotSupportedException {
bottle newBtl = (bottle) super.clone();
newBtl.wn = (wine) wn.clone();
return newBtl;
}
}

Test

     bottle bottle = new bottle(new wine());
bottle bottle1 = (bottle) bottle.clone(); System.out.println("bottle1.wine : " + bottle1.wn.getName() );
bottle1.wn.setName("中国二锅头"); System.out.println("bottle1.wine : " + bottle1.wn.getName() );
System.out.println("bottle.wine : " + bottle.wn.getName());

console:

bottle1.wine : 法国白兰地
bottle1.wine : 中国二锅头
bottle.wine : 法国白兰地

五:使用序列化实现深度克隆

public class DeepPerson implements Serializable {
private int a;
private int[] b; public DeepPerson() {
} public DeepPerson(int a, int[] b) {
this.a = a;
this.b = b;
} public int getA() {
return a;
} public void setA(int a) {
this.a = a;
} public int[] getB() {
return b;
} public void setB(int[] b) {
this.b = b;
}
}
public class Test1 {

    public static void main(String[] args) throws CloneNotSupportedException{
DeepPerson dc1 = new DeepPerson();
// 对dc1赋值
dc1.setA(100);
dc1.setB(new int[] { 1000 });
System.out.println("克隆前dc1: a=" + dc1.getA()+"b[0]=" + dc1.getB()[0]);
DeepPerson dc2 = (DeepPerson) deepClone(dc1);
// 对c2进行修改
dc2.setA(50);
int[] a = dc2.getB();
a[0] = 500;
System.out.println("克隆后dc1: a=" + dc1.getA()+"b[0]=" + dc1.getB()[0]);
System.out.println("克隆后dc2: a=" + dc2.getA()+"b[0]=" + dc2.getB()[0]);
} public static Object deepClone(Object object){
Object o=null;
try{
if (object != null){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
o = ois.readObject();
ois.close();
}
} catch (IOException e){
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return o;
}
}

console:

克隆前dc1: a=100b[0]=1000
克隆后dc1: a=100b[0]=1000
克隆后dc2: a=50b[0]=500

六、总结:

1.克隆方法用于创建对象的拷贝,为了使用clone方法,类必须实现java.lang.Cloneable接口重写protected方法clone,如果没有实现Clonebale接口会抛出CloneNotSupportedException.

2.在克隆java对象的时候不会调用构造器
3.java提供一种叫浅拷贝(shallow copy)的默认方式实现clone,创建好对象的副本后然后通过赋值拷贝内容,意味着如果你的类包含引用类型,那么原始对象和克隆都将指向相同的引用内容,这是很危险的,因为发生在可变的字段上任何改变将反应到他们所引用的共同内容上。为了避免这种情况,需要对引用的内容进行深度克隆。
4.按照约定,实例的克隆应该通过调用super.clone()获取,这样有助克隆对象的不变性。如:clone!=original和clone.getClass()==original.getClass(),尽管这些不是必须的

Java中深度克隆和浅度克隆的更多相关文章

  1. Java的深度克隆和浅度克隆

    说到克隆,其实是个比较简单的概念,跟现实生活正的克隆一样,复制一个一模一样的对象出来.clone()这个方法是从Object继承下来的,一个对象要实现克隆,需要实现一个叫做Cloneable的接口,这 ...

  2. Cloneable接口的作用与深度克隆与浅度克隆

    cloneable接口的作用 cloneable其实就是一个标记接口,只有实现这个接口后,然后在类中重写Object中的clone方法,然后通过类调用clone方法才能克隆成功,如果不实现这个接口,则 ...

  3. Java 中如何使用clone()方法克隆对象?

    java为什么要 对象克隆: 在程序开发时,有时可能会遇到以下情况:已经存在一个对象A,现在需要一个与A对象完全相同的B 对象,并对B 对象的属性值进行修改,但是A 对象原有的属性值不能改变.这时,如 ...

  4. java对象 深度克隆(不实现Cloneable接口)和浅度克隆

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt128 为什么需要克隆: 在实际编程过程中,我们常常要遇到这种情况:有一个对象 ...

  5. java中传值及引伸深度克隆的思考(说白了Java只能传递对象指针)

    java中传值及引伸深度克隆的思考 大家都知道java中没有指针.难道java真的没有指针吗?句柄是什么?变量地址在哪里?没有地址的话简直不可想象! java中内存的分配方式有两种,一种是在堆中分配, ...

  6. js浅度克隆/深度克隆

    首先弄明白几个概念: 一. 具体数据类型分为两种:  1.原始数据类型  2.引用数据类型 原始数据类型存储的是对象的实际地址,包括: number.string.boolean.还有两个特殊的nul ...

  7. Java中如何克隆集合——ArrayList和HashSet深拷贝

    编程人员经常误用各个集合类提供的拷贝构造函数作为克隆List,Set,ArrayList,HashSet或者其他集合实现的方法.需要记住的是,Java集合的拷贝构造函数只提供浅拷贝而不是深拷贝,这意味 ...

  8. Java实例 Part6:Java中的克隆

    目录 Part6:Java中的克隆 Example01:Java对象的假克隆 Example02:Java对象的浅克隆 Example03:Java对象的深克隆 Example04:序列化与对象克隆 ...

  9. 深入浅出Java中的clone克隆方法,写得太棒了!

    作者:张纪刚 blog.csdn.net/zhangjg_blog/article/details/18369201/ Java中对象的创建 clone 顾名思义就是 复制 , 在Java语言中, c ...

随机推荐

  1. 显示等待WebDriverWait+lambda

    代码,关键代码标红 参考文章:https://www.cnblogs.com/yoyoketang/p/6517477.html #coding:utf-8 ''' 这里写了一个百度搜索页的pageo ...

  2. Hive SQL语法总结

    Hive是一个数据仓库基础的应用工具,在Hadoop中用来处理结构化数据,它架构在Hadoop之上,通过SQL来对数据进行操作. Hive 查询操作过程严格遵守Hadoop MapReduce 的作业 ...

  3. centOS不显示ipv4地址的解决办法

    版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/lx_Frolf/article/deta ...

  4. Tomcat服务的配置

    首先到Apache官网,下载tomcat,在官网有两种tomcat,一种是安装版,一种是压缩版,对于安装版的一台机器只能安装一个tomcat,而对于压缩版的tomcat一台机器可以安装多个tomcat ...

  5. 未找到源文件:C:\loadrunner-11\urunner MSI\bin\icudt36.dll.o1d解决方法

    安装HP LoadRunner 11.00 未找到源文件:C:\loadrunner-11\urunner MSI\bin\icudt36.dll.o1d 下载loadrunner11 使用迅雷下载, ...

  6. Skimap_ros 利用RGBD创建Octomap(一)

    1. 奥比中光astra RGBD相机安装 1.1 安装依赖 $ sudo apt-get install build-essential freeglut3 freeglut3-dev 1.2 检查 ...

  7. 同事遇到了一个问题(在DllMain函数之前抢控制权)

    同事有个需求,他的进程会加载一个DLL,他需要在那个DLL的DllMain函数执行之前控制DLL,修改DLL的内存. 以上工作要求全部在应用层执行. 这个其实有点悲剧. 因为这个需求其实有点坑,因为需 ...

  8. 转载:jQuery的deferred对象详解

    一.什么是deferred对象? 开发网站的过程中,我们经常遇到某些耗时很长的javascript操作.其中,既有异步的操作(比如ajax读取服务器数据),也有同步的操作(比如遍历一个大型数组),它们 ...

  9. MySQL日志文件与分析

    1.查询日志.慢查询日志.二进制日志对比 查询日志 general_log 会记录用户的所有操作,其中包含增删查改等 可以指定输出为表 慢查询日志 slow_log 只要超过定义时间的所有操作语句都记 ...

  10. 转帖 移动端h5页面不同尺寸屏幕适配兼容方法

    1. viewport属性及html页面结构   <meta name="viewport" content="width=device-width,initial ...