在有些业务开发场景中需要对已有的数据进行copy(或者clone)操作,那么现有的copy方式有哪些?
开始讲之前需要进行知识点扫盲:
什么是引用?
Java中的引用类似C/C++中的指针,引用的值,指向内存中的一块空间。
对象引用赋值示例:
@Test
public void referenceTest() {
User source = new User();
source.setName("young");
source.setSex("male");
User target = source;
target.setSex("female");
System.out.println(String.format("source:%s,target:%s", source.toString(), target.toString()));
Assert.assertTrue(source.getSex().equals(target.getSex()));
}
例子中,target的修改,同样引起了source的变化。
对象克隆赋值示例:
@Test
public void cloneTest() throws CloneNotSupportedException {
User source = new User();
source.setName("young");
source.setSex("male");
User target = User.class.cast(source.clone());
target.setSex("female");
System.out.println(String.format("source:%s,target:%s", source.toString(), target.toString()));
Assert.assertTrue(source.getSex().equals(target.getSex()));
}
例子中,target的修改,不会引起了source的变化。
什么是深克隆、浅克隆?
对象克隆赋值示例是简单情况的克隆。在了解深克隆与浅克隆之前,需要理解String 是final类。
如果一个类中除了String、Integer、Long等这些final类型的field,也存在普通类的实例,例如:
对象存在引用类型field克隆赋值示例:
public class Account implements Cloneable {
String accountId;
String password;
User user;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
为了限制文章篇幅,getter setter 不写了
@Test
public void cloneReferenceTest() throws CloneNotSupportedException {
Account source = new Account ();
source.accountId="source.accountId";
source.password="source.password";
source.user = new User();
Account target = Account .class.cast(source.clone());
target.accountId="target.accountId";
target.password="target.password";
System.out.println(String.format("source:%s,target:%s", source.accountId, target.accountId));
Assert.assertTrue(source.user==target.user);
}
以上实例会发现,属性field并没有copy一份新的数据出来,这样是达不到我的期望。
这时候可以理解:
深克隆:会克隆出一个新的引用(user)实例
浅克隆:引用(user)不会生成新的实例
JDK中目前带有一些copy的API,进行验证一下:
@Test
public void apiCopyOf() {
User source = new User();
source.setName("young");
source.setSex("male");
List<User> sourceList = new ArrayList<>(1);
sourceList.add(source);
List<Object> targetList = Arrays.asList(Arrays.copyOf(sourceList.toArray(), sourceList.size()));
User target = User.class.cast(targetList.get(0));
target.setSex("female");
System.out.println(String.format("apiCopyOf#source:%s,target:%s", source.toString(), target.toString()));
}
@Test
public void streamCopyOf() {
User source = new User();
source.setName("young");
source.setSex("male");
List<User> sourceList = new ArrayList<>(1);
sourceList.add(source);
List<User> targetList = sourceList.stream().collect(Collectors.toList());
User target = targetList.get(0);
target.setSex("female");
System.out.println(String.format("streamCopyOf#source:%s,target:%s", source.toString(), target.toString()));
}
那么如何完全Copy一个对象的值呢?
答案是利用序列化与反序列化手段,将已存在对象实例序列化为字符串(或者二进制文件),再将字符串反序列化为一个对象实例在内存中。
基于以上的了解,进行对象数组的深克隆:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
public class CollectionsUtils {
private static Logger logger = LogManager.getLogger(CollectionsUtils.class);
private static ObjectMapper objectMapper = new ObjectMapper();
/**
* ArrayList 深克隆
* 本质:序列化与反序列化
* {@link ObjectMapper}版本可能会影响到这个方法的结果
*
* @param source
* @param type
* @param <T>
* @return
*/
public static <T> List<T> copyOf(List<T> source, Class<T> type) {
List<T> target = new ArrayList<>(source.size());
source.stream().forEach(d -> {
try {
String jsonString = objectMapper.writeValueAsString(d);
target.add(objectMapper.readValue(jsonString, type));
} catch (JsonProcessingException e) {
logger.error("object convert to json string failed", e.getMessage());
throw new RuntimeException(e);
} catch (IOException e) {
logger.error("json string convert to object failed", e.getMessage());
throw new RuntimeException(e);
}
});
return target;
}
}
@Test
public void copyOf() {
User source = new User();
source.setName("young");
source.setSex("male");
List<User> sourceList = new ArrayList<>(1);
sourceList.add(source);
List<User> targetList = CollectionsUtils.copyOf(sourceList, User.class);
User target = targetList.get(0);
target.setSex("female");
System.out.println(String.format("copyOf#source:%s,target:%s", source.toString(), target.toString()));
}
版权声明:本文为u010730731原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。