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


猜你喜欢
- 本文实例为大家分享了C#实现数字华容道游戏的具体代码,供大家参考,具体内容如下代码如下:using System;using System.
- Android UI中TextView的使用方法一、TextView不同区域设置颜色,大小、点击事件String msg = getReso
- 前言:由于公司的业务,硬生生的把ios开发的我,掰成了android!关于上传文件的需求处理,做了一个Java的简单封装 DocumentM
- 引导语本小节和大家一起来看看 CountDownLatch 和 Atomic 打头的原子操作类,CountDownLatch 的源码非常少,
- 简介TreeMap使用红黑树存储元素,可以保证元素按key值的大小进行遍历。继承体系TreeMap实现了Map、SortedMap、Navi
- java.lang.StackOverflowError出现的原因严重: Exception initializing page conte
- 1、官网概括引用官网说法:The Java Virtual Machine defines various run-time data ar
- 我们在使用C# 语言的Assembly.Load 来加载托管程序集并使用反射功能时,一般需要先通过Assembly.Load()
- 本文实例演示了DevExpress实现GridControl单元格编辑验证的方法,比较实用的功能,具体方法如下:主要功能代码如下:/// &
- 一、前期准备表CREATE TABLE `school_student` ( `id` int(11) NOT NULL AUT
- 1.性能考虑,优先选择数组数组在项目开发当中使用的频率是越来越少,特别是在业务为主的开发当中,首先数组没有List,Set等集合提供的诸多方
- 我们在开发需求的时候,难免会接入一下第三方的H5页面,有些H5页面是具有上传照片的功能,Android 中的 WebView是不能直接打开文
- 本文为大家分享了AForge实现C#摄像头视频录制功能的具体方法,供大家参考,具体内容如下1. 概述最近由于兴趣学习了下在C#上使用AFor
- 本文实例为大家分享了Android自定义view贝塞尔曲线,供大家参考,具体内容如下贝塞尔曲线以一个简单的贝塞尔曲线为例,二阶曲线原理贝塞尔
- 前言最近VS2019正式版发布了,装下来顺便试用了一下C#8.0,最大的看点应该就是可空引用类型了。不过C#8.0仍然处于Beta的状态,而
- 使用Aspose.Cells创建和读取Excel文件,供大家参考,具体内容如下1. 创建ExcelAspose.Cells.License
- 前面几篇文章分别讨论了Activity和Service,本文就来讨论BroastcastReceiver,Broastcast是应用程序间通
- 首先了解java构造函数怎么写java构造方法必须满足以下语法规则:(1) 方法名必须与类名相同。(2)不要声明返回类型。(3)不能被sta
- 在Java 字符终端上获取输入有三种方式:1、java.lang.System.in (目前JDK版本均支持)2、java.util.Sca
- 程序中的错误分为编译时的错误和运行时的错误。编译时的错误主要是语法错误,比如:句尾没有加分号,括号不匹配,关键字错误等,这类错误比较容易修改