一:使用目的:

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

二: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. Python笔记(五)_内置函数BIF

    查看所有的内置函数:dir(__builtins__) abs()   获取绝对值 max()   返回给定元素中的最大值 min()   返回给定元素中的最小值 sum()   求和 reverse ...

  2. python 装饰器 第四步:基本装饰器的实现

    #第四步:基本装饰器的实现 #用于扩展基本函数的函数 def kuozhan(func): #内部函数(扩展之后的eat函数) def neweat(): #以下三步就是扩展之后的功能,于是我们把这三 ...

  3. C# 几进制 转换到几进制

    public string ConvertString(string value, int fromBase, int toBase) { int intValue = Convert.ToInt32 ...

  4. javaIO流(三)--IO深入操作

    一.字符编码 在计算机的世界中,本质上只认识0,1的字节数据,如果要想描述一些文字的编码就需要对这些二进制的数据进行组合,就需要对二进制的数据进行组合,所以才有了现在可看见的中文显示,但是在进行编码的 ...

  5. JavaScript的日期对象

    1.Date对象用来处理日期和时间. 2.创建Date对象的语法: var myDate = new Date(); 3.Date对象的常用方法: 格式:Date.XX(); getDate() 从 ...

  6. wireshark抓取本地回环及其问题 转摘:http://www.cnblogs.com/luminji/p/3503464.html

    一:The NPF driver isn’t running 这个错误是因为没有开启NPF服务造成的. NPF即网络数据包过滤器(Netgroup Packet Filter,NPF)是Winpcap ...

  7. 高逼格企业级MySQL数据库备份方案,原来是这样....

    很多人,这里说的是运维工程师们,一提到写某某方案,很是头疼.不是上某度一统搜索,就是同样一句话在N个群全部群发一遍:“有没有某某方案,可以共享一下的吗??求助,各位大佬们”,估计十有八九,全部石沉大海 ...

  8. 第十三章 存储之volume

    容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题.首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失——容器以干净的状态(镜像最初的状态)重新启动.其 ...

  9. jQuery 菜单 垂直菜单实现

    HTML <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <ti ...

  10. python利用ConfigParser读写配置文件

    ConfigParser 是Python自带的模块, 用来读写配置文件, 用法非常简单. 配置文件的格式是: []包含的叫section,    section 下有option=value这样的键值 ...