java 淺拷貝 深拷貝

一,問題背景java

最近遇到一塊代碼,看了半天沒有看明白如何實現樹形結構的。debugger後發現原理,實際上是利用了java對象是引用類型,利用淺拷貝來實現樹型結構。ios

    /**
     * 
     * @param table "樹型結構中的全部節點"
     * @param childrenField "固定key,名稱爲children"
     * @param idField "每一個節點id"
     * @param parentIdField "子節點與父節點的關係屬性parentId"
     * @return
     */
    public static ArrayList list2Tree(List table, String childrenField, String idField, String parentIdField)
    {
        ArrayList tree = new ArrayList();

        Map hash = new HashMap();//裝載全部對象 id,object格式
        for (int i = 0, l = table.size(); i < l; i++)
        {
            Map t = (Map)table.get(i);
            hash.put(t.get(idField), t);
        }
  
        for (int i = 0, l = table.size(); i < l; i++)
        {
            Map t = (Map)table.get(i);
            Object parentID = t.get(parentIdField);
            if (parentID == null || parentID.toString().equals("-1"))//父元素   
            {
                tree.add(t);//向樹型結構裏放入父節點
                continue;
            }
            Map parent = (Map)hash.get(parentID);//子元素
      //這邊修改引用類型的map,修改其屬性值會改變,tree裏面的父節點會相應發生變化
            if (parent == null)    
            {
                tree.add(t);
                continue;
            }
            List children = (List)parent.get(childrenField);
            if (children == null)
            {
                children = new ArrayList();
                parent.put(childrenField, children);
            }
            children.add(t);
        }
        
        
        return tree;
    }  

針對發現的問題,這邊本身查閱資料複習一下淺拷貝深拷貝。數組

二,淺拷貝網絡

  淺拷貝是按位拷貝對象,它會建立一個新對象,這個對象有着原始對象屬性值的一份精確拷貝。若是屬性是基本類型,拷貝的就是基本類型的值;若是屬性是內存地址(引用類型),拷貝的就是內存地址 ,所以若是其中一個對象改變了這個地址,就會影響到另外一個對象。即默認拷貝構造函數只是對對象進行淺拷貝複製(逐個成員依次拷貝),即只複製對象空間而不復制資源。ide

示例代碼:函數

public class test1Main {
    
    public static void main (String[] args){
        Son son = new Son(66);
        Person p1 = new Person("tony",son);
        Person p2 = new Person(p1);
        p1.setSonName("bella");
        p1.getSon().setAge(88);//s是對象是引用,改變s全部引用s的值都會改變
        System.out.println("p1==="+p1);  //        p1===Person [sonName=bella, son=Son [age=88]]
        System.out.println("p2==="+p2);  //        p2===Person [sonName=tony, son=Son [age=88]]

    }
}

 

三,深拷貝工具

深拷貝會拷貝全部的屬性,並拷貝屬性指向的動態分配的內存(會另外開闢一個內存空間)。當對象和它所引用的對象一塊兒拷貝時即發生深拷貝。深拷貝相比於淺拷貝速度較慢而且花銷較大。當咱們須要new兩個徹底不同的對象時,須要序列化來實現深拷貝。this

示例代碼:spa

 

public class Test2Main{
    
    public static void main(String[] args) {
        Son son = new Son(66);
        Person p1 = new Person("小湯", son);
        //系列化實現深拷貝
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(p1);
            oos.flush();
            oos.close();
            bos.close();//這裏是假關閉
            //ByteArrayOutputStream/ByteArrayInputStream流是以一個字節數組來操做。
            //建立輸入流的時候,要先提供一個輸入源byte[],以後讀取實際上就是讀取這個byte[]的內容;
            //而輸出流則是將數據寫入一個內置的數組,通常磁盤和網絡流寫完以後就拿不到寫完的內容,
            //但因爲這個是寫到了一個數組中,因此仍是能夠獲取數據的
            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
            Person p2 = (Person) ois.readObject();
            p1.setSonName("小湯原始");
            p1.getSon().setAge(44);
            System.out.println("p1===="+p1); //p1====Person [sonName=小湯原始, son=Son [age=44]]
            
            System.out.println("p2===="+p2); //p2====Person [sonName=小湯, son=Son [age=66]],徹底一個新的對象
            p2.getSon().setAge(33);
            System.out.println("p2-1===="+p2); // 修改一個新的對象的值    p2-1====Person [sonName=小湯, son=Son [age=33]]
            System.out.println("p1==="+p1); //修改新的對象的值並無改變 p1, p1===Person [sonName=小湯原始, son=Son [age=44]]
            System.out.println("p1.hashCode()===="+ p1.hashCode()); //p1.hashCode()====1118140819
            System.out.println("p2.hashCode()===="+ p2.hashCode()); //p2.hashCode()====1956725890
            //hashCode 不一樣 證實兩個對象的內存地址徹底不同 
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
}

 可複用的深拷貝工具類:debug

public class SerializedClone {
    @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 (Exception e) {
            e.printStackTrace();
        }
        return cloneObj;
    }

 

淺拷貝深拷貝示例代碼person和son類的定義:

 

public class Person implements Serializable  {
    
    private String sonName;
    private Son son;
    
    public Person(String sonName,Son son) {
        this.sonName = sonName;
        this.son = son;
    }
    
    public Person(Person person) {
        this.sonName = person.sonName;
        this.son = person.son;
    }

    public String getSonName() {
        return sonName;
    }

    public void setSonName(String sonName) {
        this.sonName = sonName;
    }

    public Son getSon() {
        return son;
    }

    public void setSon(Son son) {
        this.son = son;
    }

    @Override
    public String toString() {
        return "Person [sonName=" + sonName + ", son=" + son + "]";
    }
    
}

 

public class Son implements Serializable{

    private int age;

    public int getAge() {
        return age;
    }

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

    public Son(int age) {
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Son [age=" + age + "]";
    }
    
    
}

若是person,son實現Cloneable接口,使用clone方法也能夠實現淺拷貝。

 

當寫代碼時,對象屬性有引用類型的時候,操做該對象須要注意淺拷貝深拷貝的區別!

相關文章
相關標籤/搜索