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日

相关文章

  • spring hibernate实现动态替换表名(分表)的方法

    关于“spring hibernate实现动态替换表名(分表)的方法”,我们可以通过动态读取配置文件、使用AOP等方式实现,以下是一份完整攻略: 1. 动态读取配置文件 我们可以通过读取配置文件,获取分表策略的配置信息。这些配置信息包含了关于分表规则的全部信息,我们依据这些信息即可实现动态替换表名。 下面是一个示例: 1.1 配置文件 以XML格式作为示例,…

    Java 2023年5月20日
    00
  • Java 网络爬虫基础知识入门解析

    Java 网络爬虫基础知识入门解析 概述 网络爬虫是一种通过编程方式自动化提取互联网上数据的技术。对于Java开发者而言,使用Java的网络爬虫应该会是最自然的想法。本文将介绍Java网络爬虫的基础知识,以及如何使用Java实现一个网络爬虫。 爬虫原理 一个基本的网络爬虫需要完成以下几个步骤: 发送HTTP请求获取页面内容 解析获取到的页面内容 保存所需的数…

    Java 2023年5月23日
    00
  • Mybatis Plus使用XML编写动态sql的超简易方法

    下面详细讲解”Mybatis Plus使用XML编写动态SQL的超简易方法”。 简介 Mybatis Plus是Mybatis的增强工具,可以用来简化Mybatis的开发。Mybatis Plus默认使用了entity的字段映射表中的字段,但是在实际开发过程中,我们经常会遇到重用entity映射表中同一个字段做不同的条件查询的情况,这时候我们就需要用XML来…

    Java 2023年5月20日
    00
  • spring batch使用reader读数据的内存容量问题详解

    下面是详细讲解“spring batch使用reader读数据的内存容量问题详解”的完整攻略。 1. 什么是Spring Batch Spring Batch是一个轻量级的综合批处理框架,用于开发企业级批处理应用程序。它允许开发人员能够处理大规模的数据,并且将这些数据转换成期望的格式,以便于后续处理。 2. Spring Batch读取器的内存容量问题 在S…

    Java 2023年6月3日
    00
  • Struts2修改上传文件大小限制方法解析

    当我们使用Struts2框架进行文件上传时,有时候会遇到上传的文件大小超过了限制的问题。默认情况下,Struts2上传文件大小限制为2M,如果需要修改文件上传大小限制,则需要进行如下操作: 步骤1:添加struts.xml配置 在struts.xml配置文件中添加以下配置,其中10485760代表文件大小限制为10M。 <interceptors&gt…

    Java 2023年5月19日
    00
  • Struts2 的国际化实现方式示例

    下面将结合代码示例详细讲解 Struts2 的国际化实现方式。 一、国际化实现的基本原理 Struts2 的国际化实现是通过多资源包机制来实现的。在一个 web 应用程序中,我们可以定义多个资源包,每个资源包对应不同的语言/国家 locale,当系统的 locale 和资源包的 locale 匹配时,Struts2 会自动使用该 locale 对应的资源文件…

    Java 2023年5月20日
    00
  • Mybatis迁移到Mybatis-Plus的实现方法

    下面是针对”Mybatis迁移到Mybatis-Plus的实现方法”的攻略: 1. Mybatis和Mybatis-Plus的简介 Mybatis是一种数据访问层框架,它是一个基于JDBC的大型框架,在实际开发生产中,Mybatis灵活可控、语法简练的特点备受开发人员的喜爱,但是Mybatis虽然功能强大,但是安全性和效率上有一些缺陷。 Mybatis-Pl…

    Java 2023年5月20日
    00
  • Java pdu短信解码全面解析

    Java pdu短信解码全面解析 短信协议数据单元(PDU)简介 短信协议数据单元(Protocol Data Unit,PDU)是一种短消息传送协议,它将SMS消息内容进行编码和封装,以方便在移动电话网络上进行传输和接收。在Java中,我们可以使用PDU来解码和编码短信。 短信编码 短信可由两部分组成:短信消息中心号码(SMSC Address)和短信内容…

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