java对象拷贝详解及实例

yizhihongxing

首先我们需要明确一下,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与微信小程序实现websocket长连接

    下面是详细讲解“java与微信小程序实现websocket长连接”的完整攻略。 什么是WebSocket长连接 WebSocket是一种在单个TCP连接上进行全双工通信的协议。它通过在握手阶段将HTTP升级协议为WebSocket协议来实现。在握手成功后,客户端和服务器端可以互相推送消息,而不需要不断的发起HTTP请求和响应。 WebSocket长连接通常用…

    Java 2023年5月23日
    00
  • JAVA多线程之实现用户任务排队并预估排队时长

    JAVA多线程之实现用户任务排队并预估排队时长 问题描述 我们在开发一个应用程序时,可能需要实现任务排队功能,以确保多个用户提交的任务可以依次执行,并预估排队时长,方便用户等待。本文将介绍如何使用Java多线程技术实现用户任务排队并预估排队时长。 方案概述 我们可以使用Java的线程池技术实现任务排队功能。Java线程池是一种机制,它可以维护一组线程,以便在…

    Java 2023年5月18日
    00
  • 在Java与Kotlin之间如何进行互操作详解

    在Java与Kotlin之间进行互操作是常见的需求,因为很多项目使用的是Java语言,而Kotlin作为一门兼容Java的语言,也有大量的应用场景。下面就详细讲一下在Java与Kotlin之间进行互操作的方法。 1. Java中使用Kotlin类 Kotlin的类可以在Java中被使用,与Java的类一样,可以创建对象并调用其中的函数和属性。 示例1 在Ko…

    Java 2023年5月26日
    00
  • Quarkus中的依赖注入DI和面向切面aop编程

    Quarkus是一个Java框架,它旨在提供快速启动和低内存消耗的微服务。而依赖注入(DI)和面向切面编程(AOP)是Quarkus的两个重要特性。 什么是依赖注入? 依赖注入是Quarkus中最基本的概念之一。它的目的是使应用程序具有可扩展性并降低组件之间的耦合度。 根据Quarkus文档的描述,依赖注入是将实例变量传递给类的技术。在Quarkus中,我们…

    Java 2023年6月15日
    00
  • 在JavaScript中使用for循环的方法

    在 JavaScript 中,for 循环用于重复执行某些代码。for 循环通常用于遍历数组或对象,执行相同的代码多次。 基本格式为: for (初始值; 终止条件; 增量) { // 要执行的代码块 } 其中: 初始值:定义用于循环计数的变量,并设置初始值。 终止条件:定义循环运行条件,如果该条件为 true,则循环继续执行;如果为 false,则循环结束…

    Java 2023年5月26日
    00
  • 常见的Java安全漏洞有哪些?

    常见的Java安全漏洞 Java在发展过程中,也出现了很多安全漏洞。下面是一些常见的Java安全漏洞: 1. SQL注入漏洞 SQL注入漏洞指的是攻击者利用应用程序中没有对用户输入的SQL语句参数进行验证或转义,从而在应用程序中执行恶意的SQL语句。 示例: 假设有一个查询用户姓名的SQL语句: String sql = "SELECT * FRO…

    Java 2023年5月11日
    00
  • Linux系统下Tomcat8启动速度很慢的解决方法

    下面是详细的“Linux系统下Tomcat8启动速度很慢的解决方法”攻略: 问题背景 在Linux系统下使用Tomcat8启动web应用时,可能会遇到启动速度较慢的问题,需要对其进行优化。 解决方案 1. 调整JVM参数 在Tomcat8的bin目录下找到catalina.sh文件(如果使用包管理器安装Tomcat,则该文件位于/usr/share/tomc…

    Java 2023年5月19日
    00
  • Maven使用方法详及方式详细介绍

    Maven 使用方法详及方式详细介绍 什么是 Maven Apache Maven 是一个软件项目管理和理解工具。Maven 可以帮助你自动化构建、测试和部署你的项目。Maven 还提供了许多用来管理项目的标准化范式和描述,使得开发人员可以更容易地协作。 简介 Maven 使用一个 Project Object Model (POM) 文件来描述项目的构建,…

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