我们在开发中有时候会经常需要进行对象拷贝的情况,举个例子
public static void main(String[] args) {
m1(o);
m2(o);
}
public static void m1(Object o){
//会对o进行修改
}
public static void m2(Object o){
//会对o进行修改
}
此时我们在m1和m2中就需要对入参参数进行拷贝并使用,否则会污染原参数到底其他方法产生问题。
浅拷贝
Java提供了clone()
来实现对象的拷贝,但其默认拷贝是浅拷贝。
什么是浅拷贝?
public static void main(String[] args) throws Exception{
TestClone testClone = new TestClone(1,"jwb", Collections.emptyList());
TestClone clone = testClone.clone();
}
class TestClone implements Cloneable{
private int number;
private String name;
private List<String> list;
@Override
protected TestClone clone() throws CloneNotSupportedException {
return (TestClone) super.clone();
}
}
从断点中我们可以看到,源对象和克隆对象中的引用类型属性(String
类型的name
属性,List
类型的list
属性)的对象地址是相同的,即共享了同一个对象,这时候我们在源对象中或克隆对象中更改这些属性,都会造成另一个对象中的该属性发生改变,因为它们本身就是共有的同一个属性对象。
不止是默认的clone
方法,我们平时经常使用的Spring提供的BeanUtils.copyProperties
其实也是浅拷贝的
public static void main(String[] args) throws Exception{
TestClone testClone = new TestClone(1,"jwb", Collections.emptyList());
// TestClone clone = testClone.clone();
TestClone clone = new TestClone();
BeanUtils.copyProperties(testClone,clone);
}
如何实现深拷贝?
- 重写对象的
clone
方法,手动实现 - 对象序列化,再反序列化为对象
- 各种工具类,其实现原理同样是基于java序列化的,如
commons.lang3
第一种方法这里就不赘述了,我们这里演示一下第二种和第三种
fastjson
public static void main(String[] args) throws Exception{
TestClone testClone = new TestClone(1,"jwb", Collections.emptyList());
TestClone clone = JSON.parseObject(JSON.toJSONString(testClone),TestClone.class);
System.out.println(testClone);
}
commons.lang3
public static void main(String[] args) throws Exception{
TestClone testClone = new TestClone(1,"jwb", new ArrayList<>());
TestClone clone = SerializationUtils.clone(testClone);
System.out.println(testClone);
}
注意其底层是基于Java序列化的,因此克隆类必须实现Serializable
接口