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

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

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

通过上面的分析,可以看出,如果我们要给自己的类添加拷贝功能,我们可以添加拷贝构造函数和实现Cloneable接口。

    现在,来看一下不同的类型在拷贝过程中的表现:

Operator =

拷贝构造函数

clone方法

预定义非集合类型

深拷贝

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

不支持

自定义类型

浅拷贝

取决于实现

取决于实现

预定义集合类型

浅拷贝

会逐个调用每个元素的operator=方法

会逐个调用每个元素的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深拷贝浅拷贝的更多相关文章

  1. Java 深拷贝浅拷贝 与 序列化

    一.浅拷贝.深拷贝 浅拷贝会对对象中的成员变量进行拷贝:如果是基本类型,拷贝的就是基本类型的值:如果属性是内存地址(引用类型),拷贝的就是内存地址 : 深拷贝,除了基本类型外,引用类型所引用的对象也会 ...

  2. Java基础 深拷贝浅拷贝

    Java基础 深拷贝浅拷贝 非基本数据类型 需要new新空间 class Student implements Cloneable{ private int id; private String na ...

  3. Java之浅拷贝与深拷贝

    ----?浅拷贝 --- 概念 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.简单说,浅拷贝就是只复制所考虑的对象,而不复制它所引用的对象 --- 实现方 ...

  4. Java 深拷贝和浅拷贝 利用序列化实现深拷贝

    Java 深拷贝和浅拷贝 转自:http://www.cnblogs.com/mengdd/archive/2013/02/20/2917971.html 深拷贝(deep clone)与浅拷贝(sh ...

  5. Java的浅拷贝与深拷贝

    Java的浅拷贝与深拷贝 Java中,所有的类都继承Object,Object中有clone方法,它被声明为了 protected ,所以我们但是如果要使用该方法就得重写且声明为public,必须在要 ...

  6. java 深拷贝与浅拷贝机制详解

    概要: 在Java中,拷贝分为深拷贝和浅拷贝两种.java在公共超类Object中实现了一种叫做clone的方法,这种方法clone出来的新对象为浅拷贝,而通过自己定义的clone方法为深拷贝. (一 ...

  7. 一种c#深拷贝方式完胜java深拷贝(实现上的对比)

    楼主是一名asp.net攻城狮,最近经常跑java组客串帮忙开发,所以最近对java的一些基础知识特别上心.却遇到需要将一个对象深拷贝出来做其他事情,而原对象保持原有状态的情况.(实在是不想自己new ...

  8. c# 内存的具体表现- 通用类型系统 深拷贝 浅拷贝 函数传参

    c# 通用类型系统 及变量在 深拷贝 浅拷贝 函数传参 中的深层次的表现 在编程中遇到了一些想不到的异常,跟踪发现,自己对于c#变量在内存上的表现理解有偏差,系统的学习并通过代码实验梳理了各种情况下, ...

  9. python集合增删改查,深拷贝浅拷贝

    集合 集合是无序的,不重复的数据集合,它里面的元素是可哈希的(不可变类型),但是集合本身是不可哈希(所以集合做不了字典的键)的.以下是集合最重要的两点: 去重,把一个列表变成集合,就自动去重了. 关系 ...

随机推荐

  1. gitlab操作指南

    概述 GitLab是利用 Ruby on Rails 一个开源的版本管理系统,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目.它拥有与Github类似的功能,能够浏览源代码 ...

  2. Node.js 撸第一个Web应用

    使用Node.js 创建Web 应用与使用PHP/Java 语言创建Web应用略有不同. 使用PHP/Java 来编写后台代码时,需要Apache 或者 Nginx 的HTTP 服务器,而接受请求和提 ...

  3. 深入浅出如何解析xml文件---上篇

    xml小伙伴们并不陌生,xml是可扩展标记语言,标准通用标记语言语言的子集,是一种用来标记电子文件使其具有结构性的标记语言.我们知道xml可以用dom与sax等方法进行解析,但是xml为什么要解析呢? ...

  4. 使用shell操作HDFS

    前提是都已经配置好了,可以参考hadoop伪分布安装:http://blog.csdn.net/jerome_s/article/details/25788967 linux的文件系统与hdfs的关系 ...

  5. 使用DWR实现自动补全 类似百度搜索框的自动显示效果

    使用DWR实现自动补全 自动补全:是指用户在文本框中输入前几个字母或汉字的时候,自动在存放数据的文件或数据库中将所有以这些字母或汉字开头的数据提示给用户供用户选择 在日常上网过程中,我们经常使用搜索引 ...

  6. 21 RadioGroup ListFragment

    结构 MainActivity.java package com.qf.day21_radiogroupfragment_demo3; import java.util.ArrayList; impo ...

  7. Asp.net 在刷新或提交页面后保持滚动条的位置

    网页内容在较长时,每次回传刷新页面或提交网页时都会定位到最顶端,非常不利于用户交互. 将Page.MaintainScrollPositionOnPostBack属性值设置为true即可实现刷新后保持 ...

  8. Hibernate之配置文件

    可持久化对象有以下三种状态: 临时状态(Transient):对象在保存进数据库之前为临时状态,这时数据库中没有该对象的信息,如果没有持久化,程序退出后临时状态的对象信息将会丢失.随时可能被垃圾回收器 ...

  9. XMPP系列(六)---创建群组

    最近公司项目需要,要做一个自己的IMSDK,顺便先把之前没有记录的群聊功能记录一下. 先上资料,查看XMPP群聊相关的资料,可以去这里看协议:XEP-0045 . 创建群组 XMPP 框架里有一个类X ...

  10. Android简易实战教程--第十九话《手把手教您监听EditText文本变化,实现抖动和震动的效果》

    昨晚写博客太仓促,代码结构有问题,早上测试发现没法监听文本变化!今日更改一下.真心见谅啦,哈哈!主活动的代码已经改好了,看截图这次的确实现了文本监听变化情况. 监听文本输入情况,仅仅限于土司略显low ...