java对象克隆实现方法详解

 更新时间:2023年06月26日 11:17:54   作者:暴走的小帅  
这篇文章主要给大家介绍了关于java对象克隆实现的相关资料,克隆就是复制一个对象的副本,Java支持我们对一个对象进行克隆,通常用在装饰模式和原型模式中,需要的朋友可以参考下

概述:

当我们new一个对象时,其中的属性就会被初始化, 那么想要保存刚开始初始化的值就靠clone方法来实现, 平时我们最常见的是一个对象的引用指向另一个对象,并不是创建了两个对象.

 Person p1 = new Person(100,"jim");
        Person p2 = p1;
        System.out.println(p1==p2);//true

克隆肯定是创建了两个对象

 Person p1 = new Person(100,"jim");
        Person p2 =p1.clone();//克隆的新对象
        System.out.println(p1==p2);//false

克隆分为浅克隆(ShallowClone)和深克隆(DeepClone)。

在 Java 语言中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括 int、double、byte、boolean、char 等简单数据类型,引用类型包括类、接口、数组等复杂类型。基本类型的值可以直接复制,引用类型只能复制引用地址。所以浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复

制.

浅克隆

在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。

实现方式:

1.在 Java 语言中,通过覆盖 Object 类的 clone()方法可以实现浅克隆。

2.在 spring 框架中提供 BeanUtils.copyProperties(source,target);

这里我们主要演示通过重写object中clone方法来实现

1.首先定义一个类(需要被克隆的类)

public class Person implements  Cloneable{
     int num;
     String name;
     Address address;
    public Person() {
    }
    public Person(int num, String name) {
        this.num = num;
        this.name = name;
    }
    public int getNum() {
        return num;
    }
    public void setNum(int num) {
        this.num = num;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
    @Override
    protected Person clone() throws CloneNotSupportedException {
        Person person = (Person)super.clone();
        return person;
    }
    @Override
    public String toString() {
        return "Person{" +
                "num=" + num +
                ", name='" + name + '\'' +
                ", address=" + address +
                '}';
    }
}

2.可以看到Person类关联着Address类,也写出来

public class Address  {
     String  address;
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}

3.写一个Test类进行测试

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address();
                address.setAddress("汉中");
        Person p1 = new  Person(100,"jim");
               p1.setAddress(address);
        Person p2 =p1.clone();
               p2.setName("tom");
               address.setAddress("西安");
        System.out.println(p1); // jim   西安
        System.out.println(p2);// tom    西安
    }
}

首先看name属性,p1的name为jim,克隆出另一个对象p2,将name改成了tom,因为是两个对象,所以输出的结果分别都不同.

再看关联对象, 首先将有汉中信息的address加入到了p1中,所以目前p1中的address是汉中,经过克隆出p2后,其实对于address来说只克隆了地址,所以说p1和p2指向的都是同一个address,所以都是汉中,再经过一次修改成了西安,所以都是西安.

所以说浅克隆只是克隆了引用类型变量的地址.

深克隆

在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制

在 Java 语言中,如果需要实现深克隆,可以通过覆盖 Object 类的 clone()方法实现,也可以通过序列化(Serialization)等方式来实现。序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable 接口,否则无法实现序列化操作

1.重写Object类中的clone方法

1.首先定义一个类(需要被克隆的类),相比于上面的浅克隆增加了一行克隆address对象的代码

public class Person implements  Cloneable{
     int num;
     String name;
     Address address;
    public Person() {
    }
    public Person(int num, String name) {
        this.num = num;
        this.name = name;
    }
    public int getNum() {
        return num;
    }
    public void setNum(int num) {
        this.num = num;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
    @Override
    protected Person clone() throws CloneNotSupportedException {
        Person person = (Person)super.clone();
        person.address = (Address)address.clone();   //深度复制  联同person中关联的对象也一同克隆.
        return person;
    }
    @Override
    public String toString() {
        return "Person{" +
                "num=" + num +
                ", name='" + name + '\'' +
                ", address=" + address +
                '}';
    }
}

2.Address写出来,相比于上面浅克隆多了一个重写Object类的clone方法

public class Address  {
     String  address;
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
   @Override
    protected Address clone() throws CloneNotSupportedException {
        return (Address)super.clone();
    }
}

3.还是Test测试类

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address();
                address.setAddress("汉中");
        Person p1 = new  Person(100,"jim");
               p1.setAddress(address);
        Person p2 =p1.clone();
               p2.setName("tom");
               address.setAddress("西安");
        System.out.println(p1); // jim   西安
        System.out.println(p2);// tom    汉中
    }
}

这次的结果会有所不同, 因为深克隆不仅克隆了自己, 还克隆了关联着的类的对象, 所以说原来的p1存储的汉中被克隆在了p2中,而最后一行更改为西安的是原来的address对象, p2中已经克隆了原来的address并且保存了下来

2.序列化(Serialization)的方式

如果需要被克隆的类中关联的其他类的对象太多, 那么继续用深克隆的话需要耗费大量的时间去一个一个克隆关联着的对象, 而序列化的方式可以将该类中所有关联的对象化成流从而高校的进行克隆.

1.还是创建一个关联着被克隆的类的对象

public class Address  implements Serializable {
     String  address;
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}

2.Person类,里面写一个自己的序列化方式的克隆方法

public class Person implements Serializable {
     int num;
     String name;
     Address address;
    public Person() {
    }
    public Person(int num, String name) {
        this.num = num;
        this.name = name;
    }
    public int getNum() {
        return num;
    }
    public void setNum(int num) {
        this.num = num;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
    /**
     * 自定义克隆方法
     * @return
     */
    public Person myclone() {
            Person person = null;
              try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
                      ObjectOutputStream oos = new ObjectOutputStream(baos);
                      oos.writeObject(this);
            // 将流序列化成对象
                    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                     ObjectInputStream ois = new ObjectInputStream(bais);
                     person = (Person) ois.readObject();
                  } catch (IOException e) {
                     e.printStackTrace();
                  } catch (ClassNotFoundException e) {
                     e.printStackTrace();
                 }
             return person;
          }
    @Override
    public String toString() {
        return "Person{" +
                "num=" + num +
                ", name='" + name + '\'' +
                ", address=" + address +
                '}';
    }
}

3.Test类测试

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address();
                address.setAddress("汉中");
        Person p1 = new  Person(100,"jim");
        p1.setAddress(address);
        Person p2 =p1.myclone();
               p2.setName("tom");
               address.setAddress("西安");
        System.out.println(p1);jim   西安
        System.out.println(p2);tom   汉中
    }
}

所以说, 得看具体情况进行选择

总结

到此这篇关于java对象克隆实现的文章就介绍到这了,更多相关java对象克隆内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java设计模式之命令模式_动力节点Java学院整理

    Java设计模式之命令模式_动力节点Java学院整理

    命令模式就是对命令的封装,下文中给大家介绍了命令模式类图中的基本结构,对java设计模式之命令模式相关知识感兴趣的朋友一起看看吧
    2017-08-08
  • SpringBoot使用GraphQL开发Web API实现方案示例讲解

    SpringBoot使用GraphQL开发Web API实现方案示例讲解

    这篇文章主要介绍了SpringBoot使用GraphQL开发Web API实现方案,GraphQL是一个从服务端检数据的查询语言。某种程度上,是REST、SOAP、或者gRPC的替代品
    2023-04-04
  • idea中一键自动生成序列化serialVersionUID方式

    idea中一键自动生成序列化serialVersionUID方式

    这篇文章主要介绍了idea中一键自动生成序列化serialVersionUID方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • JAVA如何调用Shell脚本

    JAVA如何调用Shell脚本

    本篇文章主要介绍了JAVA如何调用Shell脚本,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • 详解Java Callable接口实现多线程的方式

    详解Java Callable接口实现多线程的方式

    这篇文章主要介绍了详解Java Callable接口实现多线程的方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-04-04
  • Spring Task 动态修改任务执行计划cron方式

    Spring Task 动态修改任务执行计划cron方式

    这篇文章主要介绍了Spring Task 动态修改任务执行计划cron方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • springboot themaleaf 第一次进页面不加载css的问题

    springboot themaleaf 第一次进页面不加载css的问题

    这篇文章主要介绍了springboot themaleaf 第一次进页面不加载css的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • 深入理解java虚拟机的故障处理工具

    深入理解java虚拟机的故障处理工具

    大家都知道在给系统定位问题的时候,知识、经验是关键基础,数据是依据,工具是运用知识处理数据的手段。Java开发人员可以在jdk安装的bin目录下找到除了java,javac以外的其他命令。这些命令主要是一些用于监视虚拟机和故障处理的工具,下面来看看详细的介绍。
    2016-11-11
  • idea打开运行配置java web项目的全过程

    idea打开运行配置java web项目的全过程

    这篇文章主要给大家介绍了关于idea打开运行配置java web项目的相关资料,有些时候我们用IDEA跑之前用eclipse中运行的项目的时候,总是不止所措,要不就是只展示html,要不就是不能部署成功,需要的朋友可以参考下
    2023-08-08
  • 基于restTemplate遇到的编码问题及解决

    基于restTemplate遇到的编码问题及解决

    这篇文章主要介绍了restTemplate遇到的编码问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10

最新评论