java中关于深拷贝的几种方式总结

作者:凌兮~ 时间:2023-12-13 17:39:41 

前言

在java里,当我们需要拷贝一个对象时,有两种类型的拷贝:浅拷贝与深拷贝。

  • 浅拷贝只是拷贝了源对象的地址,所以源对象的值发生变化时,拷贝对象的值也会发生变化。

  • 深拷贝则是拷贝了源对象的所有值,所以即使源对象的值发生变化时,拷贝对象的值也不会改变。

java中关于深拷贝的几种方式总结

方式1:构造函数深拷贝

我们可以调用构造函数进行深拷贝,形参如果是基本类型和字符串则是直接赋值,如果是对象,则是重新new一个。

测试案例

package com.lyj.demo.pojo.cloneTest;
import lombok.Getter;
/**
* @author 凌兮
* @date 2021/4/15 14:28
* 通过构造器进行深拷贝测试
*/
@Getter
public class UserConstruct {
   private String userName;
   private AddressConstruct address;
   public UserConstruct() {
   }
   public UserConstruct(String userName, AddressConstruct address) {
       this.userName = userName;
       this.address = address;
   }
   public static void main(String[] args) {
       AddressConstruct address = new AddressConstruct("小区1", "小区2");
       UserConstruct user = new UserConstruct("小李", address);
       // 调用构造函数进行深拷贝
       UserConstruct copyUser = new UserConstruct(user.getUserName(), new AddressConstruct(address.getAddress1(), address.getAddress2()));
       // 修改源对象的值
       user.getAddress().setAddress1("小区3");
       // false
       System.out.println(user == copyUser);
       // false
       System.out.println(user.getAddress().getAddress1() == copyUser.getAddress().getAddress1());
       // false
       System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1()));
       // true
       System.out.println(user.getAddress().getAddress2().equals(copyUser.getAddress().getAddress2()));
   }
}
package com.lyj.demo.pojo.cloneTest;
import lombok.Getter;
import lombok.Setter;
/**
* @author 凌兮
* @date 2021/4/15 14:28
*/
@Getter
@Setter
public class AddressConstruct {
   private String address1;
   private String address2;
   public AddressConstruct() {
   }
   public AddressConstruct(String address1, String address2) {
       this.address1 = address1;
       this.address2 = address2;
   }
}

方式2:重载Clone()方法深拷贝

Object父类有个clone()的拷贝方法,不过它是protected类型的 ,我们需要重写它并修改为public类型,除此之外,子类还需要实现Cloneable接口来告诉JVM这个类上市可以拷贝的。

测试案例

package com.lyj.demo.pojo.cloneTest;
import lombok.Getter;
import lombok.Setter;
/**
* @author 凌兮
* @date 2021/4/15 14:49
*
*/
@Setter
@Getter
public class AddressClone implements Cloneable{
   private String address1;
   private String address2;
   public AddressClone() {
   }
   public AddressClone(String address1, String address2) {
       this.address1 = address1;
       this.address2 = address2;
   }
   @Override
   protected AddressClone clone() throws CloneNotSupportedException {
       return (AddressClone) super.clone();
   }
}
package com.lyj.demo.pojo.cloneTest;
import lombok.Getter;
import lombok.Setter;
/**
* @author 凌兮
* @date 2021/4/15 14:48
* 通过实现Clone接口实现深拷贝
*/
@Setter
@Getter
public class UserClone implements Cloneable{
   private String userName;
   private AddressClone address;
   public UserClone() {
   }
   public UserClone(String userName, AddressClone address) {
       this.userName = userName;
       this.address = address;
   }
   /**
    * Object父类有个clone()的拷贝方法,不过它是protected类型的,
    * 我们需要重写它并修改为public类型。除此之外,
    * 子类还需要实现Cloneable接口来告诉JVM这个类是可以拷贝的。
    * @return
    * @throws CloneNotSupportedException
    */
   @Override
   protected UserClone clone() throws CloneNotSupportedException {
       // 需要注意的是,super.clone()其实是浅拷贝,
       // 所以在重写UserClone类的clone()方法时,address对象需要调用address.clone()重新赋值
       UserClone userClone = (UserClone) super.clone();
       userClone.setAddress(this.address.clone());
       return userClone;
   }
   public static void main(String[] args) throws CloneNotSupportedException {
       AddressClone address = new AddressClone("小区1", "小区2");
       UserClone user = new UserClone("小李", address);
       UserClone copyUser = user.clone();
       user.getAddress().setAddress1("小区3");
       // false
       System.out.println(user == copyUser);
       // false
       System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1()));
   }
}

需要注意的是,super.clone()其实是浅拷贝,所以在重写User类的clone()方法时,address对象需要调用address.clone()重新赋值。

方式3:Apache Commons Lang序列化方式深拷贝

Java提供了序列化的能力,我们可以先将源对象进行序列化,再反序列化生成拷贝对象。但是,使用序列化的前提是拷贝的类(包括其成员变量)需要实现Serializable接口。

Apache Commons Lang包对Java序列化进行了封装,我们可以直接使用它。

测试案例

package com.lyj.demo.pojo.cloneTest;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
/**
* @author 凌兮
* @date 2021/4/15 15:11
*/
@Getter
@Setter
public class AddressSerializable implements Serializable {
   private String address1;
   private String address2;
   public AddressSerializable() {
   }
   public AddressSerializable(String address1, String address2) {
       this.address1 = address1;
       this.address2 = address2;
   }
}
package com.lyj.demo.pojo.cloneTest;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.SerializationUtils;
import java.io.Serializable;
/**
* @author 凌兮
* @date 2021/4/15 15:10
* 通过Apache Commons Lang 序列化方式深拷贝
* Java提供了序列化的能力,我们可以先将源对象进行序列化,再反序列化生成拷贝对象。
* 但是,使用序列化的前提是拷贝的类(包括其成员变量)需要实现Serializable接口。
* Apache Commons Lang包对Java序列化进行了封装,我们可以直接使用它。
*/
@Getter
@Setter
public class UserSerializable implements Serializable {
   private String userName;
   private AddressSerializable address;
   public UserSerializable() {
   }
   public UserSerializable(String userName, AddressSerializable address) {
       this.userName = userName;
       this.address = address;
   }
   public static void main(String[] args) {
       AddressSerializable address = new AddressSerializable("小区1", "小区2");
       UserSerializable user = new UserSerializable("小李", address);
       UserSerializable copyUser = SerializationUtils.clone(user);
       user.getAddress().setAddress1("小区3");
       // false
       System.out.println(user == copyUser);
       // false
       System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1()));
   }
}

方式4:Gson序列化方式深拷贝

Gson可以将对象序列化成JSON,也可以将JSON反序列化成对象,所以我们可以用它进行深拷贝。

测试案例

package com.lyj.demo.pojo.cloneTest;
import lombok.Data;
/**
* @author 凌兮
* @date 2021/4/15 15:31
*/
@Data
public class AddressGson {
   private String address1;
   private String address2;
   public AddressGson() {
   }
   public AddressGson(String address1, String address2) {
       this.address1 = address1;
       this.address2 = address2;
   }
}
package com.lyj.demo.pojo.cloneTest;
import com.google.gson.Gson;
import lombok.Data;
/**
* @author 凌兮
* @date 2021/4/15 15:30
* 使用Gson序列化方式进行深拷贝
* Gson可以将对象序列化成JSON,也可以将JSON反序列化成对象,所以我们可以用它进行深拷贝
*/
@Data
public class UserGson {
   private String userName;
   private AddressGson address;
   public UserGson() {
   }
   public UserGson(String userName, AddressGson address) {
       this.userName = userName;
       this.address = address;
   }
   public static void main(String[] args) {
       AddressGson address = new AddressGson("小区1", "小区2");
       UserGson user = new UserGson("小李", address);
       // 使用Gson序列化进行深拷贝
       Gson gson = new Gson();
       UserGson copyUser = gson.fromJson(gson.toJson(user), UserGson.class);
       user.getAddress().setAddress1("小区3");
       // false
       System.out.println(user == copyUser);
       // false
       System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1()));
   }
}

方式5:Jackson序列化方式

Jackson与Gson相似,可以将对象序列化成JSON,明显不同的地方是拷贝的类(包括其成员变量)需要有默认的无参构造函数。

测试案例

package com.lyj.demo.pojo.cloneTest;
import lombok.Data;
/**
* @author 凌兮
* @date 2021/4/15 15:41
*/
@Data
public class AddressJackson {
   private String address1;
   private String address2;
   public AddressJackson() {
   }
   public AddressJackson(String address1, String address2) {
       this.address1 = address1;
       this.address2 = address2;
   }
}
package com.lyj.demo.pojo.cloneTest;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
/**
* @author 凌兮
* @date 2021/4/15 15:40
* 通过Jackson方式实现深拷贝
* Jackson与Gson相似,可以将对象序列化成JSON,明显不同的地方是拷贝的类(包括其成员变量)需要有默认的无参构造函数。
*/
@Data
public class UserJackson {
   private String userName;
   private AddressJackson address;
   public UserJackson() {
   }
   public UserJackson(String userName, AddressJackson address) {
       this.userName = userName;
       this.address = address;
   }
   public static void main(String[] args) throws JsonProcessingException {
       AddressJackson address = new AddressJackson("小区1", "小区2");
       UserJackson user = new UserJackson("小李", address);
       // 使用Jackson序列化进行深拷贝
       ObjectMapper objectMapper = new ObjectMapper();
       UserJackson copyUser = objectMapper.readValue(objectMapper.writeValueAsString(user), UserJackson.class);
       user.getAddress().setAddress1("小区3");
       // false
       System.out.println(user == copyUser);
       // false
       System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1()));
   }
}

来源:https://blog.csdn.net/qq_40093255/article/details/115691423

标签:java,深拷贝
0
投稿

猜你喜欢

  • 浅谈在springboot中使用定时任务的方式

    2023-03-02 19:14:09
  • Java爬虫实现Jsoup利用dom方法遍历Document对象

    2023-06-15 07:52:36
  • 详解Java8中CompletableFuture类的使用

    2022-06-28 17:39:43
  • C# 操作Excel代码总结

    2022-10-11 01:54:29
  • C#导入导出Excel数据的两种方法

    2021-07-01 02:29:54
  • Android编程实现TextView部分颜色变动的方法

    2021-05-29 15:12:23
  • C#使用反射机制实现延迟绑定

    2021-06-13 22:22:42
  • 详解Java的Spring框架下bean的自动装载方式

    2022-10-23 08:13:59
  • C#中字符串的加密的源码

    2023-09-14 22:35:34
  • Android自定义View实现BMI指数条

    2021-08-28 18:12:12
  • java ThreadPoolExecutor线程池拒绝策略避坑

    2021-09-05 08:39:52
  • utf8编码检测方法分享

    2023-05-18 12:34:27
  • Java编程枚举类实战代码分享

    2023-10-16 09:36:51
  • Dubbo服务校验参数的解决方案

    2023-06-09 14:30:10
  • Android Studio打包.so库到apk中实例详解

    2022-06-25 19:19:25
  • Android webView字体突然变小的原因及解决

    2022-01-02 23:39:08
  • IntelliJ IDEA使用教程从入门到上瘾(2019图文版)

    2023-03-30 17:00:49
  • Java抛出异常与自定义异常类应用示例

    2022-10-23 01:58:52
  • java 获取字节码文件的几种方法总结

    2023-11-29 15:17:57
  • 浅谈单例模式和线程安全问题

    2023-11-25 06:27:34
  • asp之家 软件编程 m.aspxhome.com