深拷貝 vs 淺拷貝
淺拷貝
概念
複製基本類型的屬性;引用類型的屬性複製,複製棧中的變量 和 變量指向堆內存中的對象的指針,不復制堆內存中的對象。
如圖:
特點
1.對於基本數據類型的成員對象,因爲基礎數據類型是值傳遞的,所以是直接將屬性值賦值給新的對象。基礎類型的拷貝,其中一個對象修改該值,不會影響另外一個。
2.對於引用類型,比如數組或者類對象,因爲引用類型是引用傳遞,所以淺拷貝只是把內存地址賦值給了成員變量,它們指向了同一內存空間。改變其中一個,會對另外一個也產生影響。
實現
實現 Cloneable 接口,重寫 clone() 方法
/**
* @ClassName ShallowCopyDemo
* @Description: 實現 Cloneable 接口,重寫 clone() 方法。
* @Author fking
* @Date 2020/4/30 0030
* @Version V1.0
**/
public class ShallowCopyDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person(1, "fking01");//創建對象 Person p1
Person p2 = (Person)p1.clone();//克隆對象 p1
p2.setName("fking02");//修改 p2的name屬性,p1的name未變
System.out.println(p1);
System.out.println(p2);
p2 = p1;
p2.setName("fking02");//修改 p2的name屬性,p1的也跟着變
System.out.println(p1);
System.out.println(p2);
}
}
class Person implements Cloneable {
private int pid;
private String name;
public Person(int pid, String name) {
this.pid = pid;
this.name = name;
System.out.println("Person constructor call");
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Person [pid:" + pid + ", name:" + name + "]";
}
}
深拷貝
概念
複製基本類型的屬性;引用類型的屬性複製,複製棧中的變量 和 變量指向堆內存中的對象的指針和堆內存中的對象。
特點
1.對於基本數據類型的成員對象,因爲基礎數據類型是值傳遞的,所以是直接將屬性值賦值給新的對象。基礎類型的拷貝,其中一個對象修改該值,不會影響另外一個(和淺拷貝一樣)。
2.對於引用類型,比如數組或者類對象,深拷貝會新建一個對象空間,然後拷貝里面的內容,所以它們指向了不同的內存空間。改變其中一個,不會對另外一個也產生影響。
3.對於有多層對象的,每個對象都需要實現 Cloneable
並重寫 clone()
方法,進而實現了對象的串行層層拷貝。
4.深拷貝相比於淺拷貝速度較慢並且花銷較大。
實現方式
1.對象的屬性的Class 也實現 Cloneable 接口,在克隆對象時也手動克隆屬性
/**
* @ClassName DeepCopyDemo01
* @Description: 對象的屬性的Class 也實現 Cloneable 接口,在克隆對象時也手動克隆屬性。
* @Author fking
* @Date 2020/4/30 0030
* @Version V1.0
**/
public class DeepCopyDemo01 {
public static void main(String[] args) throws CloneNotSupportedException {
DPerson01 p1 = new DPerson01(1, "fking01", new DFood01("food01"));//創建Person 對象 p1
DPerson01 p2 = (DPerson01)p1.clone();//克隆p1
p2.setName("fking02");//修改p2的name屬性
p2.getFood().setName("food02");//修改p2的自定義引用類型 food 屬性
System.out.println(p1);//修改p2的自定義引用類型 food 屬性被改變,p1的自定義引用類型 food 屬性也隨之改變,說明p2的food屬性,只拷貝了引用,沒有拷貝food對象
System.out.println(p2);
}
}
class DPerson01 implements Cloneable {
private int pid;
private String name;
private DFood01 food;
public DPerson01(int pid, String name, DFood01 food) {
this.pid = pid;
this.name = name;
this.food = food;
System.out.println("DPerson01 constructor call");
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 對象的屬性的Class 也實現 Cloneable 接口,在克隆對象時也手動克隆屬性。
* @return
* @throws CloneNotSupportedException
*/
@Override
public Object clone() throws CloneNotSupportedException {
DPerson01 p = (DPerson01)super.clone();
p.setFood((DFood01)p.getFood().clone());
return p;
}
@Override
public String toString() {
return "DPerson01 [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
}
public DFood01 getFood() {
return food;
}
public void setFood(DFood01 food) {
this.food = food;
}
}
class DFood01 implements Cloneable {
private String name;
public DFood01(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
2.結合序列化(JDK java.io.Serializable 接口、JSON格式、XML格式等),完成深拷貝
/**
* @ClassName DeepCopyDemo02
* @Description: 結合序列化(JDK java.io.Serializable 接口、JSON格式、XML格式等),完成深拷貝
* @Author fking
* @Date 2020/4/30 0030
* @Version V1.0
**/
public class DeepCopyDemo02 {
public static void main(String[] args) throws CloneNotSupportedException {
DPerson02 p1 = new DPerson02(1, "fking01", new SFood02("food01"));//創建 DPerson02 對象 p1
DPerson02 p2 = (DPerson02)p1.cloneBySerializable();//克隆 p1
p2.setName("fking02");//修改 p2 的 name 屬性
p2.getFood().setName("food02");//修改 p2 的自定義引用類型 food 屬性
System.out.println(p1);//修改 p2 的自定義引用類型 food 屬性被改變,p1的自定義引用類型 food 屬性未隨之改變,說明p2的food屬性,只拷貝了引用和 food 對象
System.out.println(p2);
}
}
class DPerson02 implements Cloneable, Serializable {
private static final long serialVersionUID = -7710144514831611031L;
private int pid;
private String name;
private SFood02 food;
public DPerson02(int pid, String name, SFood02 food) {
this.pid = pid;
this.name = name;
this.food = food;
System.out.println("DPerson02 constructor call");
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 通過序列化完成克隆
* @return
*/
public Object cloneBySerializable() {
Object obj = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
obj = ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return obj;
}
@Override
public String toString() {
return "DPerson02 [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
}
public SFood02 getFood() {
return food;
}
public void setFood(SFood02 food) {
this.food = food;
}
}
class SFood02 implements Serializable {
private static final long serialVersionUID = -3443815804346831432L;
private String name;
public SFood02(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
如果大家對java架構相關感興趣,可以關注下面公衆號,會持續更新java基礎面試題, netty, spring boot,spring cloud等系列文章,一系列乾貨隨時送達, 超神之路從此展開, BTAJ不再是夢想!