Java中深度克隆和浅度克隆

一:使用目的:

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

二: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 : 法国白兰地   

五、总结:

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(),尽管这些不是必须的

原文地址:https://www.cnblogs.com/xhlwjy/p/11473911.html

时间: 2024-08-29 08:39:10

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

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

java中传值及引伸深度克隆的思考 大家都知道java中没有指针.难道java真的没有指针吗?句柄是什么?变量地址在哪里?没有地址的话简直不可想象! java中内存的分配方式有两种,一种是在堆中分配,一种是在堆栈中分配,所有new出来的对象都是在堆中分配的,函数中参数的传递是在栈中分配的.通常情况下堆的内存可以很大,比如32位操作系统中的虚拟内存都可以被堆所使用(当内存紧张的时候甚至硬盘都可以是堆的存储空间),而堆栈的内存分配是有限的. 这和c++中内存分配差不多(c++中还要有另一种方式用于全

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

编程人员经常误用各个集合类提供的拷贝构造函数作为克隆List,Set,ArrayList,HashSet或者其他集合实现的方法.需要记住的是,Java集合的拷贝构造函数只提供浅拷贝而不是深拷贝,这意味着存储在原始List和克隆List中的对象是相同的,指向Java堆内存中相同的位置.增加了这个误解的原因之一是对于不可变对象集合的浅克隆.由于不可变性,即使两个集合指向相同的对象是可以的.字符串池包含的字符串就是这种情况,更改一个不会影响到另一个.使用ArrayList的拷贝构造函数创建雇员List

Java中克隆机制

首先了解一下什么叫做拷贝? Employ e1 = new Employ(); Employ e2 = e1; 这就是拷贝,原始变量与拷贝变量指向相同的引用对象,如果改变其中一个对象的状态,其他的对象变量的对象状态也会随之改变. 什么叫做克隆? Employ e1 = new Employ(); Employ e2 = e1.clone(); 克隆之后的变量,各自指向自己的对象,这个对象状态初始是相同的,只不过在改变其中一个对象的状态的时候,另一个对象状态不会再发生改变. clone()方法在O

Java中的克隆close()和赋值引用的区别

学生类Student: package 克隆clone; /*要克隆必须实现这个借口:Cloneable,以标记这个对象可以克隆 Cloneable:此类实现了 Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制. 这个接口是标记接口,告诉我们实现该接口的类就可以实现对象的复制了. */ public class Student implements Cloneable { private String name; private int ag

Java对象和集合的拷贝/克隆/复制

昨天同事遇到了一个奇怪的问题,他需要将一个JavaBean拷贝一份,然后对新创建的Bean进行操作.但是他对新的Bean操作后,会影响旧的Bean的值.当听到这个问题的时候,我第一反应就是他的拷贝方法有问题,只是将aBean的内容复制给了bBean,但是在内存中指向的是同一个地址.这里就引出了两个关键词,浅拷贝和深拷贝. 浅拷贝(浅克隆) 被复制对象的所有变量值都和原来的对象的值相同,但是复制后的对象的引用仍然指向原来的对象.简单来说,就是对A进行拷贝生成B,只是将A的值复制给了B,内存中指向的

关于 Java 中 finally 语句块的深度辨析

可不能小看这个简单的 finally,看似简单的问题背后,却隐藏了无数的玄机.接下来我就带您一步一步的揭开这个 finally 的神秘面纱. 问题分析 首先来问大家一个问题:finally 语句块一定会执行吗? 很多人都认为 finally 语句块是肯定要执行的,其中也包括一些很有经验的 Java 程序员.可惜并不像大多人所认为的那样,对于这个问题,答案当然是否定的,我们先来看下面这个例子. 清单 1. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1

java中数据在内存中的状态

此文转自csdn,看完瞬间就明白了 首先,我们知道,Java中的数据类型分为两种,基本数据类型和引用数据类型.而基本数据类型,为什么不直接使用他们的包装类呢,例如Integer.Long等等呢?下面是Thinking in Java 中的解释: 有 一系列类需特别对待:可将它们想象成“基本”.“主要”或者“主”(Primitive)类型,进行程序设计时要频繁用到它们.之所以要特别对待,是由于 用new创建对象(特别是小的.简单的变量)并不是非常有效,因为new将对象置于“堆”里.对于这些类型,J

Java中的不可变类

本文与个人博客 zhiheng.me 同步发布,标题: Java中的不可变类. 不可变类(Immutable Objects):当类的实例一经创建,其内容便不可改变,即无法修改其成员变量. 可变类(Mutable Objects):类的实例创建后,可以修改其内容. Java 中八个基本类型的包装类和 String 类都属于不可变类,而其他的大多数类都属于可变类. 与引用不可变的区别 需要特别注意的是,不可变类的不可变是指该类的实例不可变而非指向该实例的引用的不可变. String s = "ab

.NET基础之深度复制和浅度复制

之前一直没有搞清楚深度复制和浅度复制的区别到底在哪里,今天彻底把这个东西弄懂了,写出来与到家共勉. 大家都知道Object是所有类共同的基类,其有个方法是MemberwiseClone(),其用途为 我们可以通过这个方法来达到浅度复制的效果. 下面我们通过一个例子来阐述一下浅度复制,其与深度复制的区别在什么地方: public class Content { public int Val; } public class Cloner { public Content MyContent = ne