我們在開發中有時候會經常需要進行對象拷貝的情況,舉個例子
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
接口