`

Java的深拷贝和浅拷贝 [基础知识]

阅读更多

熟悉C++的朋友对这个话题应该很熟悉,浅拷贝就是指两个对象共同拥有同一个值,一个对象改变了该值,也会影响到另一个对象。深拷贝就是两个对象的值相等,但是互相独立。本来想把以前写的一篇文章扩充一下,没想到居然牵扯出很多复杂的问题。本文测试环境是windows xp sp3中文版、NetBeans6.7.1,JDK1.6-update16。这里抛砖引玉,希望大家能提宝贵意见。

    首先,Java中常用的拷贝操作有三个,operator = 、拷贝构造函数 和 clone()方法。由于Java不支持运算符重载,我们无法在自己的自定义类型中定义operator=。拷贝构造函数大家应该很熟悉,现在看一下如何支持clone方法:

  1. 实现 Cloneable接口,因为 Object clone方法将检查类是否实现了 Cloneable接口,如果没有将抛出异常 CloneNotSupportedException对象。 Cloneable接口没有任何方法,只是个标志,所以只需要简单的写上 implements Cloneable即可。

  2. 改写从 Object继承而来的 clone方法,使它的访问权限为 public,因为为了防止意外的支持 clone操作,Object clone方法是 protected权限。


    通过上面的分析,可以看出,如果我们要给自己的类添加拷贝功能,我们可以添加拷贝构造函数和实现Cloneable接口。
    现在,来看一下不同的类型在拷贝过程中的表现:

 

  Operator = 拷贝构造函数 clone方法
预定义非集合类型 深拷贝

如果支持拷贝构造函数的类型,则是深拷贝

 

不支持
自定义类型 浅拷贝

 

取决于实现

 

取决于实现
预定义集合类型 浅拷贝 会逐个调用每个元素的operator=方法 (所以不一定是深or浅)
会逐个调用每个元素的operator=方法


    下面是测试代码,首先测试的是预定义非集合类型的operator =操作:
        int x=1;
        int y=x;
        y=2;
        if(x!=y){
            System.out.println("deep copy");
        }


        Integer a=1;
        Integer b=a;
        b=2;
        if(!a.equals(b)){
            System.out.println("deep copy");
        }


        String m="ok";
        String n=m;
        n="no";
        if(!m.equals(n)){
            System.out.println("deep copy");
        }

        程序运行后,输出三行deep copy,测试结果表明,这三种类型的operator =操作都是深拷贝。由于我没有测试完所有的预定义非集合类型,我这里推测它们的operator =都是深拷贝。

    下面测试预定义非集合类型拷贝构造函数
        Integer a=1;
        Integer b=new Integer(a);
        b=2;
        if(!a.equals(b)){
            System.out.println("deep copy");
        }


        String m="ok";
        String n=new String(m);
        n="no";
        if(!m.equals(n)){
            System.out.println("deep copy");
        }

        程序运行后,输出两行deep copy,测试结果表明,这两种类型的拷贝构造函数都是深拷贝。int没有拷贝构造函数。
    
        现在我们来测试自定义类型的operator=操作,假设我有一个类

Person,代码如下:


public class Person implements Cloneable{
    private int age;
    private String name;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    void setName(String name) {
        this.name = name;
    }
}
    测试代码:
        Person p=new Person();
        p.setAge(32);
        p.setName("陈抒");

        Person p2=p;
        p.setAge(33);
        p.setName("老陈");

        if( (p.getAge()!=p2.getAge())&&(!p.getName().equals(p2.getName())) ){
            System.out.println("deep copy");
        }

        运行后,没有输出deep copy说明这是浅拷贝。这里就是我们常说的两个引用之间的赋值,仅仅是让两个引用指向同一个对象。

    现在,我们来测试预定义集合类型的operator=操作:
        ArrayList list1=new ArrayList();
        list1.add("yangzhou");
        ArrayList list2=list1;
        list1.clear();

        if(list2.isEmpty()){
            System.out.println("shallow copy");
        }

    结果输出为shallow copy

    现在我来测试拷贝构造函数
        ArrayList list1=new ArrayList();
        list1.add("yangzhou");
        ArrayList list2=new ArrayList(list1);
        list1.clear();

        if(list2.isEmpty()){
            System.out.println("shallow copy");
        }else{
            System.out.println("deep copy");
        }

        输出结果是deep copy;


    clone方法的测试代码只是将第三行换成list1.clone(),加上类型转换,这里不再贴代码了。结果也证明是深拷贝 


    预定义集合类的深拷贝 实际上就是调用每个元素的operator =。如果元素都是自定义类型的化,实际上还是浅拷贝。现在来看测试代码:


    ArrayList list1=new ArrayList();
        Person p1=new Person();
        p1.setAge(32);
        p1.setName("陈抒");
        list1.add(p1);

        ArrayList list2=(ArrayList) list1.clone();
        list2.get(0).setName("chenshu");

        if(list2.get(0).getName().equals(list1.get(0).getName())){
            System.out.println("shallow copy");
        }else{
            System.out.println("deep copy");

        }
    
    输出为shallow copy,Person是自定义类型,它的operator =运算符只是引用之间赋值,是浅拷贝。因此当修改了list2的第一个元素指向的Person对象的name属性,也就是修改了list1第一个元素所指向的Person对象的name属性。对于这种拷贝,我自己起了一个名字,叫做第一层深拷贝。
    

 


    现在我们有了表格中的结论,自己实现拷贝构造函数或者clone方法的时候就心里有数多了。
    假如我的自定义类型内部成员变量都是预定义非集合类型,那么在clone方法中只需要调用Object.clone即可完成深拷贝操作。在拷贝构造函数中需要使用operator=来一个个的深拷贝;
    假如我们的自定义类型内部成员变量有一些预定义类型,另一些是自定义类型,如果要深拷贝的话,最好调用自定义类型成员变量的拷贝构造函数或者clone方法。下面是例子代码:

public class Company {
    public Company(){

    }

    public Company(Company c){
        name=c.name;
        person=new Person(c.person);
    }
    private String name;
    private Person person;

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public Object clone() throws CloneNotSupportedException{
        Company c=new Company();
        c.setName(name);
        c.setPerson((Person) person.clone());
        return c;
    }
}

public class Person implements Cloneable{
    public Person(){

    }

    public Person(Person p){
        age=p.age;
        name=p.name;
    }
    private int age;
    private String name;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    
    @Override
    public Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
}
    Person类的两个成员变量都是预定义非集合类型,所以只需要在clone方法中简单的调用super.clone()即可实现深拷贝。Company类有一个Person成员变量,因此要调用Person的clone方法。

分享到:
评论

相关推荐

    阿里面试(java)

    在准备阿里面试的过程中,Java开发者需要掌握一系列关键知识点,这些知识不仅涵盖了语言基础,还包括了高级特性、设计模式、框架应用以及实际开发中的最佳实践。以下是对标题和描述中涉及知识点的详细解释: 1. **...

    Java基础知识点 - 内容比较全面

    下面将详细讲解标题和描述中提及的一些核心Java基础知识。 1. **Java中的引用概念**:在Java中,引用是对象的一个指针,它指向内存中的对象实例。有强引用、软引用、弱引用和虚引用四种类型,分别对应不同的内存...

    java面试100题,包含java基础等

    本文将对 Java 中的访问修饰符、运算符、垃圾回收机制、自动装箱和自动拆箱、类加载机制、异常层次结构、接口和抽象类、内部类、深拷贝和浅拷贝、String、StringBuilder 和 StringBuffer、集合框架、泛型、反射、...

    Java中高级核心知识全面解析(精华必看)

    深拷贝和浅拷贝涉及对象复制的不同层次。 **2.3.1 静态方法与实例方法** 静态方法属于类,不依赖于类的实例,而实例方法属于对象,需通过对象调用。静态方法不能访问非静态成员,因为非静态成员在类加载时未创建。 ...

    Java基础知识.doc

    21、`clone()`方法通常会首先调用`super.clone()`以创建一个浅拷贝,然后进行必要的字段复制以实现深拷贝。如果类中包含不可克隆的对象,需要实现`Cloneable`接口并覆盖`clone()`方法。 以上是对Java基础的一些详细...

    java_clone用法

    ### Java中的`clone`方法详解:浅拷贝与深拷贝 #### 一、引言 在Java中,`clone`方法提供了一种快速复制对象的方式。它属于`Object`类的一部分,但需要显式地在子类中声明并实现`Cloneable`接口才能正常使用。本文...

    最全java面试题及答案(208道).pdf

    Java中有多种对象拷贝方式,例如浅拷贝、深拷贝等。 JavaWeb JavaWeb是Java中的一种Web开发技术,用于开发Web应用程序。JavaWeb中有多种框架和技术,例如Servlet、JSP、Spring等。 异常 异常是Java中的一种机制...

    java高端技术课程--Java数组如何实现复制

    在Java中,数组的复制分为浅拷贝和深拷贝两种。浅拷贝仅复制数组本身,不复制其元素,而深拷贝则会复制整个对象包括嵌套的对象。对于基本类型数组,复制过程就是简单的内存复制;对于引用类型数组,浅拷贝仅复制...

    java_汇总知识点1

    - 深拷贝和浅拷贝:深拷贝复制对象及其引用的对象,浅拷贝只复制对象本身。 - 泛型擦除:Java泛型在编译后会擦除,只保留类型信息,运行时无法感知泛型。 - Start和Run区别:start()方法启动线程,run()方法是...

    java实验,里面有个人做的实验报告,总共三份

    在Java中,对象拷贝分为浅拷贝和深拷贝两种。浅拷贝仅仅复制对象引用,这意味着两个对象共享同一块内存空间,改变其中一个对象的状态会影响到另一个。而深拷贝则会创建一个新的对象实例,并复制原对象的所有属性,...

    java面试题合集1,检测知识点

    * HashMap的Put方法深拷贝和浅拷贝 * CopyOnWriteArrayList的底层原理 三、Java多线程 * 什么是线程安全? * 为什么需要线程安全? * Java中的线程池是如何工作的? * CountDownLatch和Semaphore的区别和底层原理 ...

    java面试大全-黑马

    此外,还会讲解`clone()`方法的使用和含义,以及如何创建深拷贝和浅拷贝。 **JavaSE语法** 这部分关注Java语言的语法细节,如`goto`关键字的不存在性,`&`与`&&`在逻辑运算中的差异,以及在多重循环中如何跳出当前...

    Java 最常见的 200+ 面试题汇总以及答案总结汇总.pdf

    容器(如ArrayList、LinkedList、HashMap等)用于存储和管理对象,多线程则讨论如何在Java中实现并发执行,反射允许在运行时检查和操作类、接口、字段和方法,而对象拷贝涉及深拷贝和浅拷贝的区别。 Java Web、异常...

    Java高级资深核心知识全面解析.pdf

    方法的重载和重写是多态性的体现,深拷贝和浅拷贝则关乎对象复制的深度。 面向对象编程是Java的核心,包括类、对象、封装、继承和多态。类是对象的模板,对象是类的实例。构造器用于初始化对象,可以被重载。成员...

    Java-Java面向对象中引用传递教程

    - Java提供了`Cloneable`接口来实现对象的深拷贝,但默认的`clone()`方法只进行浅拷贝。若需要深拷贝,需要自行实现拷贝逻辑。 6. **序列化与反序列化实现深拷贝** - 通过实现`Serializable`接口,可以利用`...

    Java知识框架梳理,常见的面试题整理和一些工作过程中的实践

    以上知识点构成了Java知识框架的主要部分,而"常见的面试题整理"可能涉及这些领域的经典问题,例如“解释什么是Java中的深拷贝和浅拷贝”、“描述一下单例模式的实现方式”等。"一些工作过程中的最佳实践"可能涵盖...

    10万字208道Java经典面试题总结(附答案).pdf

    深拷贝和浅拷贝分别拷贝对象本身和引用。 16. **throw和throws**:throw用于抛出异常,而throws用于声明方法可能抛出的异常。 17. **final、finally、finalize的区别**:final修饰变量表示不可变,修饰类和方法...

    Java程序设计实验报告

    此外,还将介绍静态成员、内部类和枚举,以及对象的深拷贝和浅拷贝的区别,这些都是面向对象编程中常见的概念。 实验六:嵌套类与面向对象 实验六关注的是嵌套类,包括静态嵌套类(静态内部类)和非静态嵌套类...

Global site tag (gtag.js) - Google Analytics