Java實現對象克隆的方法

本文首發於cartoon的博客     
轉載請註明出處:https://cartoonyu.github.io/cartoon-blog/post/java/java%E5%AE%9E%E7%8E%B0%E5%85%8B%E9%9A%86%E7%9A%84%E6%96%B9%E6%B3%95/java

前言

這也是昨天的面試題。git

當時只說了深拷貝以及淺拷貝,面試官問了兩遍還有嗎,我很確定的說就這兩種了,面試結束以後查了一下,啪啪打臉。github

正文

JAVA實現克隆有兩種形式面試

  • 淺克隆
  • 深克隆
淺克隆與深克隆的區別

JAVA將數據類型分爲基本數據類型以及引用數據類型,我認爲淺克隆與深克隆的區別主要在於對引用類型的成員屬性的操做。深度克隆應該遞歸克隆引用類型的成員屬性。ide

淺克隆實現
  • 實現Cloneable接口
  • 重寫clone方法,調用父類的clone方法

代碼post

public class Text implements Cloneable{
 ​
  private int age;
 ​
  private Name name;
 ​
  public int getAge() {
  return age;
  }
 ​
  public void setAge(int age) {
  this.age = age;
  }
 ​
  public Name getName() {
  return name;
  }
 ​
  public void setName(Name name) {
  this.name = name;
  }
 ​
  @Override
  protected Object clone(){
  try {
  return super.clone();
  } catch (CloneNotSupportedException e) {
  e.printStackTrace();
  }
  return null;
  }
 }
 ​
 class Name{
  private String name;
 ​
  public String getName() {
  return name;
  }
 ​
  public void setName(String name) {
  this.name = name;
  }
 }
 ​
 public class Main {
 ​
  public static void main(String[] args){
  Name name1=new Name();
  name1.setName("name1");
  Text t1=new Text();
  t1.setAge(12);
  t1.setName(name1);
  Text t2=(Text) t1.clone();
  System.out.println(t2.getName().getName());
  name1.setName("name2");
  System.out.println(t2.getName().getName());
 ​
  }
 ​
 }

輸出this

name1
name2

結果分析code

由於只是直接調用父類的clone方法,沒有對成員屬性進行處理,因此在修改t1屬性name的值時,t2屬性name的值也會隨之改變。對象

優勢blog

簡單易實現

缺點

沒法真正克隆對象

深克隆實現
經過遞歸克隆實現

代碼

 public class Text implements Cloneable{
 ​
  private int age;
 ​
  private Name name;
 ​
  public int getAge() {
  return age;
  }
 ​
  public void setAge(int age) {
  this.age = age;
  }
 ​
  public Name getName() {
  return name;
  }
 ​
  public void setName(Name name) {
  this.name = name;
  }
 ​
  @Override
  protected Object clone(){
  Text text=null;
  try {
  text=(Text) super.clone();
  } catch (CloneNotSupportedException e) {
  e.printStackTrace();
  }
  text.setName((Name) text.getName().clone());
  return text;
  }
 }
 ​
 class Name implements Cloneable{
  private String name;
 ​
  public String getName() {
  return name;
  }
 ​
  public void setName(String name) {
  this.name = name;
  }
 ​
  @Override
  protected Object clone() {
  try {
  return super.clone();
  } catch (CloneNotSupportedException e) {
  e.printStackTrace();
  }
  return null;
  }
 }

輸出

name1
name1
經過序列化實現

代碼

 public class Text implements Serializable{
 ​
  private static final long serialVersionUID = 8723901148964L;
 ​
  private int age;
 ​
  private Name name;
 ​
  public int getAge() {
  return age;
  }
 ​
  public void setAge(int age) {
  this.age = age;
  }
 ​
  public Name getName() {
  return name;
  }
 ​
  public void setName(Name name) {
  this.name = name;
  }
 ​
  public Object myClone(){
  Text text=null;
  ByteArrayOutputStream bos=new ByteArrayOutputStream();
  try {
  ObjectOutputStream oos=new ObjectOutputStream(bos);
  oos.writeObject(this);
  ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
  ObjectInputStream ois=new ObjectInputStream(bis);
  text=(Text)ois.readObject();
  } catch (IOException e) {
  e.printStackTrace();
  } catch (ClassNotFoundException e) {
  e.printStackTrace();
  }
  return text;
  }
 }
 ​
 class Name implements Serializable {
 ​
  private static final long serialVersionUID = 872390113109L;
 ​
  private String name;
 ​
  public String getName() {
  return name;
  }
 ​
  public void setName(String name) {
  this.name = name;
  }
 ​
  @Override
  public String toString() {
  return name;
  }
 }

輸出

 name1
 name1

結果分析

採用深克隆能有效隔離源對象與克隆對象的聯繫。

從實現過程來講,遞歸克隆存在克隆過程多且複雜的缺點,因此建議採用序列化的方式進行

深克隆。

總結

JAVA對象克隆共有兩種形式,三種方法

  • 淺克隆

    • 調用clone方法
  • 深克隆

    • 遞歸調用clone方法
    • 序列化對象

三種方法之間互有優缺點,具體採用要根據實際狀況。

相關文章
相關標籤/搜索