Java中對象拷貝的兩種方式


JVM實現拷貝的目的:

大家先思考一個問題,為什么需要克隆對象?直接 new 一個對象不行嗎?

  答案是:克隆的對象可能包含一些已經修改過的屬性,而 new 出來的對象的屬性都還是初始化時候的值,所以當需要一個新的對象來保存當前對象的 “狀態” 就靠 clone 方法了。那么我把這個對象的臨時屬性一個一個的賦值給我新 new 的對象不也行嘛?

    可以是可以,但是一來麻煩不說,二來,大家通過上面的源碼都發現了 clone 是一個 native 方法,就是快啊,在底層實現的

引用的拷貝

//引用拷貝
    private static void copyReferenceObject(){
        Person p = new Person(23, "zhang");
        Person p1 = p;
        System.out.println(p);
        System.out.println(p1);
    }

這里打印的結果: 
Person@3654919e 
Person@3654919e 
可以看到,打印的結果是一樣的,也就是說,二者的引用是同一個對象,並沒有創建出一個新的對象。因此要區分引用拷貝和對象拷貝的區別,下面要介紹的就是對象拷貝。 

淺拷貝

  • 如果pojo中存在的是基本數據類型 ,String 除外 ,實現Cloneable 覆寫Clone 方法 這個就是淺拷貝

- code 

 

package core.java.deeporshoawcopy;

/**
 * @author DGW-PC
 * @date 2018年6月7日
 * @see 驗證   淺拷貝  一般要求實體類使用包裝類型 對於深拷貝 類中存在對其他類的引用,也需要實現cloneable接口
 */

class Person implements Cloneable{
    private String name;
    private Integer age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException  {
        /*Person p=null;
        try{
            p=(Person) super.clone();
        }catch (CloneNotSupportedException e) {
        }*/
        return super.clone();
    }
}

public class Base {
    
     public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        person.setName("a");
        person.setAge(12);
        Person per1=(Person) person.clone();
        per1.setName("b");
        per1.setAge(14);;
        System.out.println(person.getName()+" "+person.getAge().hashCode(0));
        System.out.println(per1.getName()+" "+per1.getAge());
    }

}

內存圖:

 

 

 

深拷貝

- 如果你的POJO 存在的不是基本上數據類型,可以是自己定義類型,也可以其他包提供的類型 這里以java 提供的Data 的為例子 可以看下面的代碼 自身實現clone 方法 你在涉及到使用拷貝的時候一定要注意別的包提供的類是否出現了問題

 

/**
     * Return a copy of this object.
     */
    public Object clone() {
        Date d = null;
        try {
            d = (Date)super.clone();
            if (cdate != null) {
                d.cdate = (BaseCalendar.Date) cdate.clone();
            }
        } catch (CloneNotSupportedException e) {} // Won't happen
        return d;
    }

- 下面介紹一下基本深拷貝的代碼 很短 : 你一定主要 主類包裝了多少其他的引用類型的其他類,那么其他必須都要實現Cloneable 接口 以及clone (保護方法) 方法

方法原型:

仔細一看,它還是一個 native 方法,大家都知道 native 方法是非 Java 語言實現的代碼,供 Java 程序調用的,

因為 Java 程序是運行在 JVM 虛擬機上面的,要想訪問到比較底層的與操作系統相關的就沒辦法了,只能由靠近操作系統的語言來實現

1/*
2Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object.
3The general intent is that, for any object x, the expression:
41) x.clone() != x will be true
52) x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements.
63) x.clone().equals(x) will be true, this is not an absolute requirement.
7*/
8protected native Object clone() throws CloneNotSupportedException;

需要滿足的條件:

  對於任何對象x,表達式:

  • 1)x.clone()!= x將為真
  • 2)x.clone()。getClass()== x.getClass()為true,但這不是絕對要求。
  • 3)x.clone()。equals(x)將為true,這不是絕對要求。

 

package core.java.deeporshoawcopy;


/**
 * @author DGW-PC
 * @date 2018年6月7日
 * @see 實現序列化  深拷貝
 */

class Dog implements Cloneable{
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class User implements Cloneable{
    private String name;
    private Dog dog;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Dog getDog() {
        return dog;
    }
    public void setDog(Dog dog) {
        this.dog = dog;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        User u=(User) super.clone();
        u.dog=(Dog) dog.clone(); //多個需要在全部把關系搞清楚
        return u;
    }
}

public class ObjCloner {
        public static void main(String[] args) throws CloneNotSupportedException {
            Dog dog = new Dog();
            dog.setName("田園犬");
            User user = new User();
            user.setDog(dog);
            user.setName("王二");
            User user1=(User) user.clone();
            user1.setName("張三");
            Dog dog2 = new Dog();
            dog2.setName("德國牧羊犬");
            user1.setDog(dog2);
            System.out.println(user.getName()+"養了"+ user.getDog().getName());
            System.out.println(user1.getName()+"養了"+ user1.getDog().getName());
        }
}

結果:

 

類組合形式下深拷貝

class Car implements Cloneable{
    String name;

    public Car(String name) {
        this.name = name;
    }
    @Override
    protected Object clone()  {
        Car car=null;
        try {
            car=(Car) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return car;
    }
    @Override
    public String toString() {
        return "Car [name=" + name + "]";
    }
}
class Home implements Cloneable{
    String name;
    public Home(String name) {
        this.name = name;
    }
    @Override
    protected Object clone()  {
        Home home=null;
        try {
            home=(Home) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return home;
    }
    @Override
    public String toString() {
        return "Home [name=" + name + "]";
    }
}
class Wife implements Cloneable{
    String name;
    public Wife(String name) {
        this.name = name;
    }
    @Override
    protected Object clone()  {
        Wife wife=null;
        try {
            wife=(Wife) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return wife;
    }
    @Override
    public String toString() {
        return "Wife [name=" + name + "]";
    }
}
class Person implements Cloneable{
    String name;
    Car car;
    Home home;
    Wife wife;
    public Person(String name, Car car, Home home, Wife wife) {
        super();
        this.name = name;
        this.car = car;
        this.home = home;
        this.wife = wife;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", car=" + car + ", home=" + home + ", wife=" + wife + "]";
    }
    @Override
    protected Object clone()  {
        Person person=null;
        try {
            person=(Person) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        person.car=(Car) this.car.clone();
        person.home=(Home) this.home.clone();
        person.wife=(Wife) this.wife.clone();
        return person;
    }
    
}

public class Test2 {
    public static void main(String[] args) {
        Person person = new Person("Tom", new Car("bmw"), new Home("一環以內"), new Wife("intkk"));
        //Person person1=person;
        Person person1 = (Person) person.clone();
        System.out.println(person);
        System.out.println(person1);
        person1.name="Jerry";
        person1.home= new Home("帝國");
        person1.car=new Car("五菱骨灰盒");
        System.out.println(person);
        System.out.println(person1);
    }
}

問題: 當存在多個類的時候,每個類都要實現Clonebale接口,實現過於復雜: 特別是下面這段代碼: (當多個類的時候建議使用串行化方式進行clone)

protected Object clone()  {
        Person person=null;
        try {
            person=(Person) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        person.car=(Car) this.car.clone();
        person.home=(Home) this.home.clone();
        person.wife=(Wife) this.wife.clone();
        return person;
    }

串行化方式實現深拷貝

  實現方法:

  •   實現Serializable接口,如果類中存在組合形式的使用,那么每個類都要實現Serializable接口

 

 - 以下代碼着重注意一下 CloneObj方法 ,就行。

package core.java.deeporshoawcopy;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @author DGW-PC
 * @date 2018年6月7日
 * @since 串行化 實現 深拷貝
 */

class Body implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String name;
    private Fonter fonter;
    private Head head;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Fonter getFonter() {
        return fonter;
    }
    public void setFonter(Fonter fonter) {
        this.fonter = fonter;
    }
    public Head getHead() {
        return head;
    }
    public void setHead(Head head) {
        this.head = head;
    }
    @Override
    public String toString() {
        return "Body [name=" + name + ", fonter=" + fonter + ", head=" + head + "]";
    }
    public Body(String name, Fonter fonter, Head head) {
        super();
        this.name = name;
        this.fonter = fonter;
        this.head = head;
    }
    
    
}
class Head implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private Integer size;
}
class Fonter implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private Integer size;
}
class Face implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private Integer size;
}
public class ObjClonerSeiz {
        
        private static <T>T  CloneObj(T obj){
            T retobj=null;
            try {
            //寫入流中
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            //從流中讀取
            ObjectInputStream ios = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
            retobj=(T) ios.readObject();
            }catch (Exception e) {
                e.printStackTrace();
            }
            return retobj;
        }
    
    
    
        public static void main(String[] args) {
                 Body body = new Body("張三", new Fonter(), new Head());
                 Body body2=CloneObj(body);
                 System.out.println("body==body2  ====>"+(body==body2));
                 System.out.println("body.font==body2.font  ====>"+(body.getFonter()==body2.getFonter()));
                 System.out.println("body.head==body2.head  ====>"+(body.getHead()==body2.getHead()));
        }
}

總結:

  • 在Java語言中,如果需要實現深克隆,可以通過覆蓋Object類的clone()方法實現,也可以通過序列化(Serialization)等方式來實現。
  • 如果引用類型里面還包含很多引用類型,或者內層引用類型的類里面又包含引用類型,使用clone方法就會很麻煩。這時我們可以用序列化的方式來實現對象的深克隆。)
  • 實現對象克隆有兩種方式:

      1). 實現Cloneable接口並重寫Object類中的clone()方法;

      2). 實現Serializable接口,通過對象的序列化和反序列化實現克隆,可以實現真正的深度克隆

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM