java对象拷贝详解及实例

首先我们需要明确一下,Java中的对象拷贝指的是拷贝一个对象的副本,而不是两个对象共享同一块内存地址。在Java中,我们可以使用浅拷贝和深拷贝两种方式来实现对象的拷贝。

浅拷贝

浅拷贝最简单的方式就是使用Object类的clone()方法,该方法可以复制一个Java对象。但是,它并不是完全的复制。当我们使用clone()方法来复制一个Java对象时,它会返回一个新的对象,但是该对象的非基本类型字段将会和原对象共享同一块内存地址。也就是说,如果我们修改了新对象中的某个非基本类型字段的值,原对象中的对应字段的值也会发生变化。

示例1:

public class Person implements Cloneable {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person("Tom", 18);
        Person person2 = (Person) person1.clone();
        person2.setName("Jerry");
        System.out.println("person1 name: " + person1.getName()); //输出Tom
        System.out.println("person2 name: " + person2.getName()); //输出Jerry
    }

    //省略getter和setter方法
}

在上述示例中,我们创建了一个名为Person的类,并实现了Cloneable接口,然后使用Object类的clone()方法实现了复制。我们首先创建了一个名为person1的Person对象,然后调用clone()方法复制出了一个名为person2的新对象,接着我们修改了person2对象中的name属性值,最后打印出了person1和person2对象的name属性值。从结果可以看出,person1和person2对象的name属性值不同,说明复制成功。

但是,如果我们将Person类中的name属性改为一个非基本类型的Student类,就会发现修改person2对象中的Student类的属性值时,person1中对应的属性值也会发生变化。

示例2:

public class Person implements Cloneable {
    private String name;
    private Student student;

    public Person(String name, Student student) {
        this.name = name;
        this.student = student;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Student student1 = new Student("Jack", 19);
        Person person1 = new Person("Tom", student1);
        Person person2 = (Person) person1.clone();
        student1.setName("Jerry");
        System.out.println("person1 student name: " + person1.getStudent().getName()); //输出Jerry
        System.out.println("person2 student name: " + person2.getStudent().getName()); //输出Jerry
    }

    //省略getter和setter方法
}

class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //省略getter和setter方法
}

在上述示例中,我们在Person类中新增了一个Student类型的属性,然后在main方法中创建了一个Person对象,并将一个Student对象赋值给了它的student属性。然后,我们使用clone方法将该对象复制到person2中,并修改了student1对象中的name属性值。最后,我们打印出了person1和person2中student属性的name属性值,发现它们是一样的。

深拷贝

为了避免浅拷贝带来的问题,我们可以使用深拷贝。深拷贝会复制所有的属性,包括对象的非基本类型属性。在Java中,常用的方式是使用序列化和反序列化实现深拷贝。

示例3:

public class Person implements Serializable {
    private String name;
    private Student student;

    public Person(String name, Student student) {
        this.name = name;
        this.student = student;
    }

    public Person deepClone() throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (Person) ois.readObject();
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Student student1 = new Student("Jack", 19);
        Person person1 = new Person("Tom", student1);
        Person person2 = person1.deepClone();
        student1.setName("Jerry");
        System.out.println("person1 student name: " + person1.getStudent().getName()); //输出Jerry
        System.out.println("person2 student name: " + person2.getStudent().getName()); //输出Jack
    }

    //省略getter和setter方法
}

class Student implements Serializable {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //省略getter和setter方法
}

在上述示例中,我们实现了Serializable接口,并定义了deepClone方法实现深拷贝。在该方法中,我们使用ByteArrayOutputStream和ObjectOutputStream将对象序列化成二进制流,然后使用ByteArrayInputStream和ObjectInputStream将二进制流反序列化成对象。最后,我们调用deepClone方法将person1对象深拷贝到person2中,并修改student1对象中的name属性值,最终发现person1和person2对象的student属性的name属性值不同。

再给出一个深拷贝的例子:

示例4:

public class Person implements Cloneable {
    private String name;
    private Student student;

    public Person(String name, Student student) {
        this.name = name;
        this.student = student;
    }

    protected Object clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        person.student = (Student) this.student.clone();
        return person;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Student student1 = new Student("Jack", 19);
        Person person1 = new Person("Tom", student1);
        Person person2 = (Person) person1.clone();
        student1.setName("Jerry");
        System.out.println("person1 student name: " + person1.getStudent().getName()); //输出Jerry
        System.out.println("person2 student name: " + person2.getStudent().getName()); //输出Jack
    }

    //省略getter和setter方法
}

class Student implements Cloneable {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    //省略getter和setter方法
}

在上述示例中,我们使用了深拷贝的第二种方式——手动进行深拷贝。在Person类中,我们在实现clone()方法时,首先调用了super.clone()方法复制出一个新的Person对象。接着,我们将原对象中的student对象再次进行clone(),然后将其设置到新的person对象中。最后,我们调用clone()方法将person1对象深拷贝到person2中,并修改student1对象中的name属性值,最终发现person1和person2对象student属性的name属性值不同。

总之,通过以上的例子,我们可以看到,Java对象的拷贝分为浅拷贝和深拷贝。使用浅拷贝可以简单地复制一个对象的基本类型成员变量,但不会复制它的非基本类型成员变量。使用深拷贝可以更完整地复制一个对象,包括它的基本类型成员变量和其非基本类型成员变量。在Java中,我们可以使用序列化和反序列化,以及手动进行clone()方法实现深拷贝。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java对象拷贝详解及实例 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • Java中的异常处理机制是什么?

    Java中的异常处理机制是通过try-catch语句块和throw抛出异常语句实现的。以下是Java中异常处理机制的详细步骤: 1. 什么是异常 在编写程序时,不可避免遇到一些非预期的错误,这些错误被成为异常。Java中的异常是一种对象,它用来信号某个方法出现了错误,有关这种错误的信息被封装在异常对象中并传递给调用该方法的程序。 2. 异常分类 Java中的…

    Java 2023年4月27日
    00
  • java多线程JUC常用辅助类详解

    让我们来详细讲解“java多线程JUC常用辅助类详解”的攻略。 一、JUC简介 JUC(Java Util Concurrent)是Java SE 5中推出的一个并发编程框架,提供了许多特殊的并发编程工具类,以及对Java线程池的支持等。 二、JUC常用函数 以下是JUC中常用的辅助类: 1. CountDownLatch(倒计数器) CountDownLa…

    Java 2023年5月18日
    00
  • Java由浅入深细数数组的操作上

    Java数组操作完整攻略 一、数组概述 Java数组是一种包含固定数量元素的数据结构,这些元素属于同一种数据类型。 Java数组由以下几个基本点组成: 数组声明 数组的创建 数组的初始化 数组的访问 数组的遍历 二、数组的声明 Java中数组的声明包含两个重要的部分,第一部分是数组的类型,第二部分是数组的名字。如下所示: int[] arr; //第一种声明…

    Java 2023年5月26日
    00
  • Java Web中ServletContext对象详解与应用

    下面我将为你详细讲解Java Web中ServletContext对象的完整攻略。 什么是ServletContext对象 ServletContext是Java Web容器中的一个重要对象,它代表整个Web应用程序,一个Web应用程序只有一个ServletContext对象。ServletContext对象在Web应用程序启动时被创建,在Web应用程序停止…

    Java 2023年6月15日
    00
  • java实现文件上传、下载、图片预览

    Java实现文件上传、下载、图片预览的完整攻略 上传文件 首先在前端页面设计一个上传文件的form表单,并设置enctype为multipart/form-data。form表单提交时,浏览器会解析其中的文件,并将其封装到一个HTTP请求中,在请求的正文中发送到服务器。 <form action="/upload" method=&…

    Java 2023年5月19日
    00
  • Springboot 全局时间格式化操作

    下面是关于Spring Boot全局时间格式化操作的完整攻略。 背景 Spring Boot是一个使用很方便的轻量级框架,它内置了很多常用的扩展功能。在实际应用中,我们经常需要对时间类型数据进行格式化处理,以满足业务需求。此时,全局时间格式化就成了必不可少的一个功能。 解决方案 方案一:在全局配置文件中配置时间格式 可以在application.proper…

    Java 2023年5月20日
    00
  • cmd中javac和java使用及注意事项详解

    当我们需要开发Java程序时,需要使用到JDK提供的工具 javac 和 java。其中 javac 是用于编译Java源代码生成二进制字节码文件,而 java 则是用于运行已经编译好的二进制字节码文件,下面详细介绍一下在cmd中使用javac和java的方法及注意事项。 1. 安装JDK并配置环境变量 在使用javac和java之前,首先需要安装JDK并配…

    Java 2023年5月23日
    00
  • ajax无刷新动态调用股票信息(改良版)

    Ajax无刷新动态调用股票信息(改良版)攻略 简介 本文介绍如何使用Ajax无刷新动态调用股票信息的改良版。通过使用jQuery的Ajax方法,可以实现股票信息的实时刷新,提高用户体验和数据准确性。 准备工作 在开始之前,需要以下几个工作: 获取股票API接口,本文以https://api.asilu.com/stock/为例; 引入jQuery库,本文以C…

    Java 2023年6月16日
    00
合作推广
合作推广
分享本页
返回顶部