1. 程式人生 > >Java物件深度複製和序列化總結

Java物件深度複製和序列化總結

經常會碰到物件複製的問題,這個問題比較基礎,同時與JVM記憶體模型掛鉤。

1. 實現Cloneable介面預設的clone方法是淺拷貝

Java Cloneable介面實際上是個空介面,沒有任何方法,實際的clone()是object的方法,但是是一個protected的方法,因此需要重寫這個方法,並且宣告為public,不然的話protected方法在其它包中是無法訪問的。

問題:既然Cloneable介面是個空介面,實際的clone()是object的方法,那在類中不實現Cloneable介面是否可以?
答案:不可以,你會得到以下異常,JVM會作檢查,哦,你沒實現Cloneable介面,給你的不支援clone異常

java.lang.CloneNotSupportedException: ClassA
	at java.lang.Object.clone(Native Method)
	at ClassA.clone(ClassA.java:62)
	at TestMain.main(TestMain.java:60)

測試類ClassA,淺拷貝:

public class ClassA implements Cloneable {
    
    	public int a;
   		public Map<String, String > map = null;
    
        public ClassA(int a, HashMap<String, String> map){
            
            this.a = a;
            this.map = map;
        }
        
        public Object clone() {
            
            try {
                return super.clone();
            }
            catch (CloneNotSupportedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }
    
    }

以上是淺拷貝,對於原始資料型別或者String這種final型別會分配新記憶體,但是對於其它物件型別,是淺拷貝,只建立了一個新的引用,指的是同一堆中物件。

2. 通過實現Cloneable介面的方式實現深度拷貝的方法是重寫clone方法,並對當前物件內所有引用進行拷貝

所謂通過實現Cloneable實現深度拷貝,就是個苦力活,說白了是迭代的對原物件中的所有物件屬性進行重寫分配記憶體並且賦值的方式。如下面類ClassB,對於其中的Map物件的深度賦值就是迭代賦值。

public class ClassB implements Cloneable {
    
    public int a;
    public Map<String, String > map = null;
    
    public ClassB(int a, HashMap<String, String> map){
        
        this.a = a;
        this.map = map;
    }
    
    public Object clone() throws CloneNotSupportedException {
        
        ClassB result = (ClassB) super.clone();
        Map<String, String> m = new HashMap<String, String>();
        Set<String> set = this.map.keySet();
        Iterator<String> iterator = set.iterator();
        while(iterator.hasNext()) {
            String key = iterator.next();
            String value = this.map.get(key);
            m.put(key, value);
        }
        result.map = m;
        return result;
    }

}

3. 使用序列化方式可以簡便實現深度拷貝

個人認為深度拷貝最有效的方式,通過將當前物件序列化,然後再反序列化,如下面類ClassC中deepClone方法:

public class ClassC implements Serializable {
    
    private static final long serialVersionUID = -6965723414666095332L;
    
    public int a;
    public Map<String, String > map = null;
    
    public ClassC(int a, HashMap<String, String> map){
        
        this.a = a;
        this.map = map;
    }
    
    public Object deepClone() throws IOException, ClassNotFoundException {
        
        // 序列化
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);

        oos.writeObject(this);

        // 反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);

        return ois.readObject();
    }

}

測試類:

public class TestMain {

    /**
     * @param args
     * @throws CloneNotSupportedException 
     * @throws IOException 
     * @throws ClassNotFoundException 
     */
    public static void main(String[] args) throws CloneNotSupportedException, ClassNotFoundException, IOException {

        HashMap<String, String> map = new HashMap<String, String>();
        ClassA obj = new ClassA(10, map);
        ClassA obj2 = (ClassA) obj.clone();
        
        obj.map.put("test", "test");
        System.out.println(obj);
        System.out.println(obj2);
        System.out.println(obj.a);
        System.out.println(obj2.a);
        System.out.println(obj.map.get("test"));
        System.out.println(obj2.map.get("test"));
        System.out.println(obj2.map == obj.map);
        
        System.out.println("------------------------------------");
        System.out.println("------------------------------------");
        
        map = new HashMap<String, String>();
        ClassB objb = new ClassB(10, map);
        objb.map.put("test", "test");
        ClassB objb2 = (ClassB) objb.clone();
        
        objb.map.put("testB", "testB");
        System.out.println(objb);
        System.out.println(objb2);
        System.out.println(objb.a);
        System.out.println(objb2.a);
        System.out.println(objb.map.get("test"));
        System.out.println(objb2.map.get("test"));
        System.out.println(objb.map.get("testB"));
        System.out.println(objb2.map.get("testB"));
        System.out.println(objb2.map == objb.map);
        
        System.out.println("------------------------------------");
        System.out.println("------------------------------------");
        
        map = new HashMap<String, String>();
        ClassC objc = new ClassC(10, map);
        objc.map.put("test", "test");
        ClassC objc2 = (ClassC) objc.deepClone();
        
        objc.map.put("testC", "testC");
        System.out.println(objc);
        System.out.println(objc2);
        System.out.println(objc.a);
        System.out.println(objc2.a);
        System.out.println(objc.map.get("test"));
        System.out.println(objc2.map.get("test"));
        System.out.println(objc.map.get("testC"));
        System.out.println(objc2.map.get("testC"));
        System.out.println(objc2.map == objc.map);
    }

}

參考文章:
https://www.cnblogs.com/NaLanZiYi-LinEr/p/9192734.html
https://blog.csdn.net/baiye_xing/article/details/71788741