java object 之clone方法全面解析

Java对象之clone方法全面解析

简介

在Java中,如果使用赋值号将一个对象赋值给另外一个对象,那么这两个对象会共用同一份数据。而通过clone()方法可以创建一个新的对象,并复制原始对象数据到新对象中。

在本篇文章中,我们将全面解析clone()方法,介绍如何使用clone()方法拷贝一个Java对象。

clone() 方法说明

clone()方法是Java中一个被重载的方法,它的主要作用是提供一种克隆方法,用于通过拷贝生成和原始对象完全相同数据的新对象。clone方法并不需要实现Cloneable接口,但是如果不实现的话,在调用clone方法时会抛出CloneNotSupportedException异常。

下面是clone方法的声明:

protected Object clone() throws CloneNotSupportedException

clone方法是一个受保护的方法,因此,只有该类和该类的子类才能访问该方法。如果想要在该类的外部访问该方法,需要在该类的内部定义一个public方法,并在该方法中调用clone方法。

clone的默认行为

通过clone方法,生成了新的对象,但这新的对象和原始对象的数据增加了一份引用。这意味着,如果新对象和原始对象中的某个数据被修改了,那么这两个对象中都会体现出来。

下面是一个例子:

class Person {
   String firstName;
   String lastName;
}

class MainClass {
   public static void main(String args[]) {
      Person originalPerson = new Person();
      originalPerson.firstName = "Tom";
      originalPerson.lastName = "Swift";

      Person newPerson = originalPerson.clone();
      newPerson.firstName = "John";

      System.out.println("Original Person First Name: " + originalPerson.firstName);
      System.out.println("New Person First Name: " + newPerson.firstName);
   }
}

在这个例子中,我们创建了一个Person对象,并将其赋值给一个引用变量(originalPerson)。然后,我们使用clone()方法创建了一个新的对象,并将其赋值给一个新的引用变量(newPerson)。我们将新对象的firstName属性设为John。我们在输出中查看每个对象的firstName属性值,发现原始对象的firstName属性值没有发生变化,而新的对象的firstName属性被修改了。

通过重写clone方法制定克隆的行为

因为clone方法的默认行为并不总是所期望的,所以你可能需要重写clone()方法来明确克隆的对象拷贝格式。在默认的情况下,clone()方法会生成一个新的对象,但是该对象中的数据项还是原来的数据项的引用(这是浅拷贝,shallow copy的一种)。在大多数情况下,这是无法满足需求的。

还是使用上面的Person类为例,在该类中增加一个address属性:

class Person {
   String firstName;
   String lastName;
   Address address;
}

class Address {
   String street;
   String city;
   String state;
}

现在尝试通过原始对象拷贝一个新的对象来进行数据的clone操作:

Person originalPerson = new Person();
originalPerson.firstName = "Tom";
originalPerson.lastName = "Swift";
originalPerson.address = new Address();
originalPerson.address.street = "31 Frost St.";
originalPerson.address.city = "New York";
originalPerson.address.state = "NY";

Person newPerson = originalPerson.clone();
newPerson.firstName = "John";
newPerson.address.street = "20 Broadway";

这段代码将会生成一个新的Person对象。因为Address对象中的属性都是引用拷贝,所以结果中,originalPerson对象和newPerson对象中的Address对象的street属性都会被修改,这显然背离了我们希望达到的效果。

在这种情况下,不使用clone方法是更好的选择。应该重写clone方法,将原始对象与新对象进行比较,并在必要时通过深拷贝变得不同。通过实现深拷贝,可以确保新对象中的每个属性都不是原始属性的引用。

下面是一个创建Person方法的示例,其中新对象中的Address属性被深拷贝:

class Person implements Cloneable {
   String firstName;
   String lastName;
   Address address;

   protected Object clone() throws CloneNotSupportedException {
      Person newPerson = (Person) super.clone();
      newPerson.address = (Address) newPerson.address.clone();
      return newPerson;
   }
}

class Address implements Cloneable {
    String street;
    String city;
    String state;

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

如上所示,当我们在Person类和Address类中实现Cloneable接口时,就可以在深拷贝新对象时使用clone()方法。在这个示例中,当我们从原始对象进行深拷贝时,将新的Address对象相应地拷贝到新的Person对象中。

下面是一个演示深拷贝的例子:

Person originalPerson = new Person();
originalPerson.firstName = "Tom";
originalPerson.lastName = "Swift";
originalPerson.address = new Address();
originalPerson.address.street = "31 Frost St.";
originalPerson.address.city = "New York";
originalPerson.address.state = "NY";

Person newPerson = originalPerson.clone();
newPerson.firstName = "John";
newPerson.address.street = "20 Broadway";

System.out.println("Original Person Street: " + originalPerson.address.street);
System.out.println("New Person Street: " + newPerson.address.street);

在这个例子中,我们创建了一个Person对象,并将其赋值给一个引用变量originalPerson。我们还创建了一个新的Person对象,通过调用originalPerson对象的clone()方法来实现深拷贝,将其赋值给新创建的对象。我们修改了新对象的firstName属性,并将新对象的address属性地址设为20 Broadway。我们在输出中查看每个对象的address.street属性值,发现原始对象中的address.street属性值没有发生变化,而新对象的address.street属性被新复制了一份,值变为了20 Broadway,满足了我们的要求。

结论

clone()方法可以生成两个相同的Java对象,可以根据需要深度或浅拷贝对象。clone方法分为浅拷贝和深拷贝,浅拷贝复制的是引用地址,而深拷贝则是深层次的拷贝所有对象中的属性。

如果你决定在你的Java代码中使用clone()方法,你应该重写clone()方法,并遵循在上面提到的建议。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java object 之clone方法全面解析 - Python技术站

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

相关文章

  • 可能是全网最详细的springboot整合minio教程

    可能是全网最详细的 Spring Boot 整合 MinIO 教程 介绍 本教程将带领读者了解 Spring Boot 如何与 MinIO 对象存储进行整合。我们将使用 Spring Boot 的官方框架 spring-boot-starter-web、spring-boot-starter-test,以及本文作者写的 minio-spring-boot-s…

    Java 2023年5月19日
    00
  • Java如何使用Agent和ASM在字节码层面实现方法拦截

    下面我将详细讲解“Java如何使用Agent和ASM在字节码层面实现方法拦截”的完整攻略,希望能对你有所帮助。 首先,我们需要了解Agent和ASM的相关知识。 Agent是JavaSE5引入的一个新特性,是一种运行时的插件机制,它通过在被启动的Java虚拟机上安装一个代理程序,可以实现许多高级的功能,比如动态注入代码或修改以及捕获JVM内部的事件信息等。 …

    Java 2023年5月26日
    00
  • JsonFormat与@DateTimeFormat注解实例解析

    JsonFormat与@DateTimeFormat注解实例解析 在Java中,我们经常需要将日期和时间格式化为特定的格式。为了实现这个目的,我们可以使用@JsonFormat和@DateTimeFormat注解。在本文中,我们将详细讲解这两个注解的用法,并提供两个示例来说明这个过程。 JsonFormat注解 @JsonFormat注解用于指定日期和时间的…

    Java 2023年5月18日
    00
  • java数据库连接池的特点及步骤

    Java数据库连接池是Java web开发中常用的工具之一,下面按照以下步骤来详细讲解Java数据库连接池的使用: 步骤一:导入数据库连接池相关依赖 首先需要在项目中导入数据库连接池相关的依赖,比如Apache Tomcat、C3P0、Druid等等保证正在使用的数据库连接工具导入正确的驱动包。 步骤二:配置连接池参数属性 在Java代码中配置连接池的参数属…

    Java 2023年5月20日
    00
  • Java swing读取txt文件实现学生考试系统

    准备工作 首先,我们要在Java环境中搭建好Java swing的开发环境,并确定好要读取的txt文件的路径和文件名。 读取txt文件 我们可以使用Java语言中的文件输入流(FileInputStream)、字符输入流(InputStreamReader)和缓存输入流(BufferedReader)来读取txt文件,并将其存储到字符串中。代码示例如下: i…

    Java 2023年5月30日
    00
  • 线程局部变量的作用是什么?

    以下是关于线程局部变量的作用的完整使用攻略: 线程局部变量的作用 线程局部变量是指在多线程编程中,每个线程都拥有自己的变量本,互不干扰的一种变量。线程局部变量可以用来存储线程的状态信息,从而实现线程之间的数据隔离和共享。 线程局部变量的作用主要有以下几个方面: 1. 实现线程之间的数据隔离 在多线程编程中,线程之间的数据隔离是非常重要的。线程局部变量用来存储…

    Java 2023年5月12日
    00
  • 详解MybatisPlus集成nacos导致druid连接不上数据库

    我很高兴为您提供“详解MybatisPlus集成nacos导致druid连接不上数据库”的完整攻略。 问题描述MybatisPlus集成nacos后,我们发现druid连接池无法连接数据库了,导致应用程序无法启动。这是由于Druid数据源在生成时需要使用一些配置参数,例如驱动类名、连接字符串、用户名/密码等,而这些参数在nacos配置中心中没有被正确指定。 …

    Java 2023年6月15日
    00
  • Spring RestTemplate具体使用详解

    Spring RestTemplate具体使用详解 简介 RestTemplate是Spring提供的专门用于调用Restful风格的Web服务的客户端工具,它封装了底层的HTTP连接以及常用的HTTP请求方法,包括GET、POST、PUT、DELETE等,可以大大简化我们调用Web服务的代码量。 基本用法 使用RestTemplate最基本的用法是通过创建…

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