JAVA中对象的克隆及深拷贝和浅拷贝

使用场景:

在日常的编程过程 中,经常会遇到,有一个对象OA,在某一时间点OA中已经包含了一些有效值 ,此时可能会需一个和OA完全相对的新对象OB,并且要在后面的操作中对OB的任何改动都不会影响到OA的值,也就是OA与Ob是需要完全两个独立的对象。

但OB的初始值是由对象OA确定的。在JAVA语言中,用普通的赋值语句是满足不了需求的。使用对象的clone()方法是实现克隆的最简单、也是最高效的手段。

Java的所有类都默认继承java.lang.Object类,在java.lang.Object类中有一个方法clone()。JDK API的说明文档解释这个方法将返回Object对象的一个拷贝。要说明的有两点:一是拷贝对象返回的是一个新对象,而不是一个引用。二是拷贝对象与用 new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息。 实现克隆可以用深拷贝和浅拷贝来实现。

深拷贝和浅拷贝的基本概念的理解:

浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象,被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象

深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象

通过实例看深拷贝和浅拷贝的实现和不同:

浅拷贝

class AddressNew implements Cloneable {
    private String add;

    public String getAdd() {
        return add;
    }

    public void setAdd(String add) {
        this.add = add;
    }

    public Object clone() throws CloneNotSupportedException{
       return super.clone();
    }

}

public class StudentNew implements Cloneable{
    private int number;
    private String name;

    public String getName() {
        return name;
    }

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

    private AddressNew addr;

    public AddressNew getAddr() {
        return addr;
    }

    public void setAddr(AddressNew addr) {
        this.addr = addr;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public Object clone() throws CloneNotSupportedException {
       return  super.clone();
    }
}

public class JavaShallowCopy {
   public static void main(String [] args) throws Exception{

       AddressNew addr = new AddressNew();
       addr.setAdd("杭州市");
       StudentNew stu1 = new StudentNew();
       stu1.setNumber(123);
       stu1.setName("s1");
       stu1.setAddr(addr);

       StudentNew stu2 = (StudentNew)stu1.clone();

       System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
       System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
       System.out.println(stu1);
       System.out.println(stu2);
       addr.setAdd("西湖区");
       //stu1.setNumber(20);
       //stu2.setName("s2");

       System.out.println("学生1:" + stu1.getName() + ",地址:" + stu1.getAddr().getAdd());
       System.out.println("学生2:" + stu2.getName() + ",地址:" + stu2.getAddr().getAdd());
   }
}

运行结果:

学生1:123,地址:杭州市
学生2:123,地址:杭州市
[email protected]
[email protected]
学生1:s1,地址:西湖区
学生2:s1,地址:西湖区

从运行结果来看,stu1,stu2的解是2个不同的对象了,但是在改变了,addr的对象的地址之后,stu1,stu2的2个对象的引用对象addr的值都改变了,也就是说stu1和stu2的addr对象引用的是同一个地址,这个不是我们想要的结果,在实际的开发工作中这一块一定要特别注意,使用不当可能会使业务功能或数据造成错误或混乱,所以这个拷贝只是实现在浅拷贝,那么从我们需要的场景看需要实现深拷贝才能达到我们想要的结果,下面会通过实例来看深拷贝的2种不同的实现。

深拷贝:

class Address implements Cloneable {
    private String add;

    public String getAdd() {
        return add;
    }

    public void setAdd(String add) {
        this.add = add;
    }
    @Override
    public Object clone() {
        Address addr = null;
        try{
            addr = (Address)super.clone();
        }catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return addr;
    }

}

public class Student implements Cloneable{
    private int number;
    private String name;

    public String getName() {
        return name;
    }

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

    private Address addr;

    public Address getAddr() {
        return addr;
    }

    public void setAddr(Address addr) {
        this.addr = addr;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    @Override
    public Object clone() {
        Student stu = null;
        try{
            stu = (Student)super.clone();   //浅复制
        }catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
        stu.addr = (Address)addr.clone();   //深度复制
        return stu;
    }
}

public class javaDeepCopy {
    public static void main(String args[]) {
Address addr = new Address();addr.setAdd("杭州市");Student stu1 = new Student();stu1.setNumber(123);stu1.setName("s1");stu1.setAddr(addr);

Student stu2 = (Student) stu1.clone();

System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());System.out.println(stu1);System.out.println(stu2);addr.setAdd("西湖区");stu1.setNumber(20);System.out.println("学生1:" + stu1.getName() + ",地址:" + stu1.getAddr().getAdd());System.out.println("学生2:" + stu2.getName() + ",地址:" + stu2.getAddr().getAdd());
 } }

运行结果:

学生1:123,地址:杭州市
学生2:123,地址:杭州市
[email protected]
[email protected]
学生1:s1,地址:西湖区
学生2:s1,地址:杭州市

从运行结果来看,stu1,stu2的解是2个不同的对象了,在改变了addr的对象的地址之后,stu1,stu2的2个对象的引用对象addr的值只有stu1r 改变了,也就是说stu1和stu2的addr对象引用的不是同一个地址,这个是我们想要的结果所以这个拷贝只是实现在浅拷贝,那么从我们需要的场景看,这种方式实现了深拷,达到了我们想要的结果,下面会通过实例来看深拷贝的另一种实现:通过序例化来实现深拷贝。

深拷贝序列化的实现:

class AddressSerial implements Serializable {
    private String add;

    public String getAdd() {
        return add;
    }

    public void setAdd(String add) {
        this.add = add;
    }

}

public class StudentSerial implements Serializable{
    private int number;
    private String name;

    public String getName() {
        return name;
    }

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

    private AddressSerial addr;

    public AddressSerial getAddr() {
        return addr;
    }

    public void setAddr(AddressSerial addr) {
        this.addr = addr;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public Object clone() throws CloneNotSupportedException {
        return  super.clone();
    }

    public  Object deepClone() throws IOException,OptionalDataException,ClassNotFoundException{
        ByteArrayOutputStream bo=new ByteArrayOutputStream();
        ObjectOutputStream oo=new ObjectOutputStream(bo);
        oo.writeObject(this);
        ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi=new ObjectInputStream(bi);
        return oi.readObject();
    }
}

public class javaDeepCopySreial {
    public static void  main(String [] args) throws Exception{

        AddressSerial addr = new AddressSerial();
        addr.setAdd("杭州市");
        StudentSerial stu1 = new StudentSerial();
        stu1.setNumber(123);
        stu1.setName("s1");
        stu1.setAddr(addr);

        StudentSerial stu2 = (StudentSerial)stu1.deepClone();

        System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
        System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
        System.out.println(stu1);
        System.out.println(stu2);
        addr.setAdd("西湖区");
        //stu1.setNumber(20);
        //stu2.setName("s2");

        System.out.println("学生1:" + stu1.getName() + ",地址:" + stu1.getAddr().getAdd());
        System.out.println("学生2:" + stu2.getName() + ",地址:" + stu2.getAddr().getAdd());
    }
}

运行结果:

学生1:123,地址:杭州市
学生2:123,地址:杭州市
[email protected]
[email protected]
学生1:s1,地址:西湖区
学生2:s1,地址:杭州市

从运行的结果看序例化也达到了深拷贝的场景。

参考和阅读的几偏深拷贝浅拷贝的文章:

https://www.cnblogs.com/null00/archive/2010/12/14/2065088.html

https://www.cnblogs.com/xuanxufeng/p/6558330.html

https://blog.csdn.net/baiye_xing/article/details/71788741

原文地址:https://www.cnblogs.com/songpingyi/p/10283742.html

时间: 2024-10-04 01:23:13

JAVA中对象的克隆及深拷贝和浅拷贝的相关文章

java学习---对象的克隆和序列化

1.假clone,虚拟机只是将对象的内存地址clone了一份,修改克隆后的对象内容,将影响原对象 public class Employee{ public Employee(){ } public Employee(String name, int age){ this.age = age; this.name = name; } @Override public String toString(){ return "姓名: " + name + "年龄: " +

IOS中复制对象的用法及深拷贝和浅拷贝详解

亲爱的网友,我这里有套课程想和大家分享,如果对这个课程有兴趣的,可以加我的QQ2059055336和我联系. 课程内容简介 我们软件是基于移动设备的.所以我们必然的选择了安卓作为我们的开发工具.课程中,我们将简要的介绍Android的基本概念,然后进行我们的实战开发.在开发中,大家讲学习到基本的组件,适配UI,数据的存储,多线程下载,开机广播,闹钟提醒,短信发送等实际项目开发中碰到的有用的知识点.通过课程学习,让大家能够掌握Android软件开发的流程,注意点,及优化.帮助大家迅速的掌握Andr

Java中对象、对象引用、堆、栈、值传递以及引用传递的详细解释

Java中对象.对象引用.堆.栈.值传递以及引用传递的详细解释 1.对象和对象引用的区别: (1).对象: 万物皆对象.对象是类的实例.在Java中new是用来在堆上创建对象用的.一个对象可以被多个引用所指向. (2).对象引用: 类似于C++中的地址.通过对象引用可以找到对象.一个引用可以指向多个对象.操纵的标识符实际上是指向对象的引用. 就像:对象存放在屋子里,对象的引用就相当于屋子的钥匙. 2.值传递和引用传递的区别: (1).值传递:传递的是值的拷贝.也就是说传递后就不互相关了. (2)

java中对象多态时成员变量,普通成员函数及静态成员函数的调用情况

/* 样例1: class Parent{ int num = 3; } class Child extends Parent{ int num = 4; } */ /* 样例2: class Parent{ } class Child extends Parent{ int num = 4; } */ /* 样例3: class Parent{ void show(){ System.out.println("Parent Show!"); } } class Child exten

java中对象的初始化过程

class Parent{ int num = 8;// ->3 Parent(){ //super(); // ->2 //显示初始化 // ->3 //构造代码段 // ->4 show(); // ->5 } {// ->4 System.out.println("Parent constructor code run->"); } public void show(){//被覆盖 System.out.println("num

java中对象的深拷贝和浅拷贝

根据对象成员变量的拷贝程度(基本数据类型.引用类型),可将拷贝分为浅拷贝和深拷贝. 一.浅拷贝 package javaKeyTechnology; class PerSon{ private String name; private int age; PerSon(String name,int age){ this.name = name; this.age = age; } public void setName(String name){ //私有数据的更改器 this.name = n

面试题:Java中对象序列化接口(Serializable)的意义

Serializable接口是一个里面什么都没有的接口 它的源代码是public interface Serializable{},即什么都没有. 如果一个接口里面什么内容都没有,那么这个接口是一个标识接口,比如,一个学生遇到一个问题,排错排了几天也没解决,此时,她举手了(示意我去帮他解决),然后我过去,帮他解决了,那么这个举手其实就是一个标识,自己不能解决的问题标示我去帮他解决,在Java中的这个Serializable接口是给JVM看的,告诉JVM,我不做这个类的序列化了,你(JVM)给我序

java中对象和对象的引用

在Java中,有一组名词经常一起出现,它们就是“对象和对象引用”,很多朋友在初学Java的时候可能经常会混淆这2个概念,觉得它们是一回事,事实上则不然. 方法/步骤 1.何谓对象? 在Java中有一句比较流行的话,叫做“万物皆对象”,这是Java语言设计之初的理念之一.要理解什么是对象,需要跟类一起结合起来理解.下面这段话引自<Java编程思想>中的一段原话: “按照通俗的说法,每个对象都是某个类(class)的一个实例(instance),这里,‘类’就是‘类型’的同义词.” 从这一句话就可

JAVA中对象创建和初始化过程

1.Java中的数据类型 Java中有3个数据类型:基本数据类型(在Java中,boolean.byte.short.int.long.char.float.double这八种是基本数据类型).引用类型和null类型.其中,引用类型包括类类型(含数组).接口类型. 下列语句声明了一些变量: 以下是引用片段: int k ;  A a; //a是A数据类型的对象变量名.  B b1,b2,-,b10000;// 假定B是抽象类或接口.  String s; 注意:从数据类型与变量的角度看,基本数据