java對象的clone


 

克隆的目的:快速創建一個已有對象的副本。

克隆的步驟: 

  1. 創建一個對象
  2. 將原有對象的數據導入到新創建的數據中

clone方法首先會判對象是否實現了Cloneable接口,若無則拋出CloneNotSupportedException, 最后會調用internalClone. intervalClone是一個native方法,一般來說native方法的執行效率高於非native方法。

 源碼:

 

當某個類要復寫clone方法時,要繼承Cloneable接口。通常的克隆對象都是通過super.clone()方法來克隆對象。

 

2.淺克隆(shadow clone)

   克隆就是復制一個對象的復本.若只需要復制對象的字段值(對於基本數據類型,如:int,long,float等,則復制值;對於復合數據類型僅復制該字段值,如數組變量則復制地址,對於對象變量則復制對象的reference。

 

  1. 實現Cloneable接口的類都具備被拷貝的能力,拷貝在內存中進行,比new生成對象性能明顯提升;
  2. clone方法是Object類的protected方法,也就是在用戶編寫的代碼中不能直接調用。為此必須重寫定義clone方法,並聲明為public,
  3. Cloneable接口的出現與接口正常使用沒有關系,是一個空接口,它只是一個標記表示一個對象需要克隆,如果一個對象需要克隆,而沒有實現Cloneable接口,就會產生一個CloneNotSupportedException異常
  4. Clone()方法存在與Object對象中,但是其不會將對象的全部屬性都拷貝,而是有選擇的拷貝
    1. 基本類型:
    1. 對象:如果變量是一個實例對象,只拷貝地址引用;
    2. String字符串:拷貝地址引用,但是修改時,會從字符串池中重新生成新的字符串,原來的保持不變;
  1. 對於Clone實現對象的拷貝:需要新建大量的對象,工程量大可以使用序列化來實現對象的拷貝

 

需要clone的對象:

package com.gzu.pyu.thinking.in.java.practice0211;

import java.io.Serializable;

public class Student implements Cloneable ,Serializable {
    public String name;
    public int age;
    public Address address;

    public Student() {
    }

    public Student(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public Object clone()  {
        Student student=null;
        try {
            //淺拷貝
            student= (Student)super.clone();
        } catch (CloneNotSupportedException e) {
            return new Student();
        }
        //支持深度克隆
        student.address=address.clone();
        return student;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("Student{");
        sb.append("name='").append(name).append('\'');
        sb.append(", age=").append(age);
        sb.append(", address=").append(address);
        sb.append('}');
        return sb.toString();
    }
}

class Address implements Cloneable,Serializable{
    String city;
    String town;

    public Address() {
    }

    public Address(String city, String town) {
        this.city = city;
        this.town = town;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getTown() {
        return town;
    }

    public void setTown(String town) {
        this.town = town;
    }

    @Override
    public Address clone()  {

        Address address=null;
        try {
            address= (Address) super.clone();
        } catch (CloneNotSupportedException e) {
            return new Address();
        }
        return address;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("Address{");
        sb.append("city='").append(city).append('\'');
        sb.append(", town='").append(town).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

 

測試方法如下:
package com.gzu.pyu.thinking.in.java.practice0211;

import com.gzu.pyu.thinking.in.java.utils.CloneUtils;

public class TestMain {
    public static void main(String ... args){
        Address address=new Address("ShengZheng","LongGang");
        //將address指向的區域賦值給addressNew,使得addressNew和address同時指向了同一個區域
        Address addressNew=address;

        //address指向的內容改變了,addressNew指向的內容也跟着改變
        System.out.println(address==addressNew);// true
        address.setCity("GuiYang");
        System.out.println(address);
//        Address{city='GuiYang', town='LongGang'}
        System.out.println(addressNew);
//        Address{city='GuiYang', town='LongGang'}

        //address進行克隆,將address進行了復制了,產生了一個新的對象
        Address addressClone = address.clone();
        System.out.println(address==addressClone);// false
        address.setTown("HuaXi");
        System.out.println(address);
//        Address{city='GuiYang', town='HuaXi'}
        System.out.println(addressClone);
//        Address{city='GuiYang', town='LongGang'}

        //在內存中通過字節流的拷貝是比較容易的,把木對象寫入到一個字節流中,再從字節流中讀出來
        Address addr = CloneUtils.clone(address);
        System.out.println(address==addr);// false
        addr.setCity("PanZhou");
        System.out.println(address);
//        Address{city='GuiYang', town='HuaXi'}
        System.out.println(addr);
//        Address{city='PanZhou', town='HuaXi'}
    }
}

 

二、使用序列化來實現對象的拷貝

  1. 在內存中通過字節流的拷貝是比較容易的,把木對象寫入到一個字節流中,再從字節流中毒出來,這樣可以創建一個新的對象,且新對象與木對象不存在引用共享的問題,實現的對象的深拷貝   
package com.gzu.pyu.thinking.in.java.utils;

import java.io.*;

/**
 * 在內存中通過字節流的拷貝是比較容易的,把木對象寫入到一個字節流中,再從字節流中讀出來,
 * 這樣可以創建一個新的對象,且新對象與木對象不存在引用共享的問題,實現的對象的深拷貝
 */
public class CloneUtils {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj){
        T cloneObj = null;
            //寫入字節流
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream obs = new ObjectOutputStream(out);
            obs.writeObject(obj);
            obs.close();

            //分配內存,寫入原始對象,生成新對象
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(ios);
            //返回生成的新對象
            cloneObj = (T) ois.readObject();
            ois.close();
        }catch(IOException e){
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return cloneObj;
    }
}

 


免責聲明!

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



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