Fork me on GitHub

Programming Design Notes

使用 Xstream 程式庫去深層複製 Java 物件

| Comments

在某些時候,為免改寫原本 Object 的數據,而需要複製一個一模一樣的 Object 出來,例如記下當時的 Object 狀態,然後備份等等。如果使用 GetterSetter 一個一個數據去複製,一定會大大增加程式碼行數,亦只能對已知的 Object 去複製。當這個 Object 新增一項數據時,便要再重寫複製方法,所以這方法是很沒有效率的。

Xstream 原本是 Java 操作 XML 的一個 Library,除此之外亦可以使用 Xstream 去複製 Object (將 Java Object 轉換成 XML 再轉換為 Java Object)。

Xstream 官方網址: http://xstream.codehaus.org/

下載後將 .jar 加到 class path

假設現在有 ParentInfo 2 個 Object,這 2 個 Object 都是作為傳遞數據的 Object

Parent:
package xstream.sample;

public class Parent {

private String firstName;
private String lastName;

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

@Override
public String toString() {
return "Parent [firstName=" + firstName + ", lastName=" + lastName
+ "]";
}

}

Info:
package xstream.sample;

import java.util.LinkedList;
import java.util.List;

public class Info {

private String firstName;
private String lastName;
private int age;
private List<String> hobbies;
private Parent parent;

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public int getAge() {
return age;
}

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

public List<String> getHobbies() {
return hobbies;
}

public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}

public void addHobby(String hobby) {
if (hobbies == null)
hobbies = new LinkedList<String>();
hobbies.add(hobby);
}

public Parent getParent() {
return parent;
}

public void setParent(Parent parent) {
this.parent = parent;
}

@Override
public String toString() {
return "Info [firstName=" + firstName + ", lastName=" + lastName
+ ", age=" + age + ", hobbies=" + hobbies + ", parent="
+ parent + "]";
}

}

付責複製的 Function:

package xstream.sample;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

public class Main {

private static final XStream XSTREAM = new XStream(new DomDriver());

@SuppressWarnings("unchecked")
public static <T> T cloneObject(T src){
return (T) XSTREAM.fromXML(XSTREAM.toXML(src));
}

}

測試的程式:
package xstream.sample;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

public class Main {

private static final XStream XSTREAM = new XStream(new DomDriver());

public static void main(String[] args) {
//Create info object
Info info = new Info();
Parent parent = new Parent();

//Set data
info.setFirstName("Lawrence");
info.setFirstName("Cheung");
info.setAge(24);
info.addHobby("Sports");
info.addHobby("Reading");

parent.setFirstName("Tom");
parent.setLastName("Cheung");
info.setParent(parent);

//Print my info
System.out.println("Original object: " + info);

System.out.println("Now, we are going to clone info object");
System.out.println();
Info clonedInfo = cloneObject(info);

//Print cloned info
System.out.println("Cloned object: " + clonedInfo);
System.out.println();

//Modify original info
System.out.println("Change original info age to 25 and parent first name set to John.");
info.setAge(25);
info.getParent().setFirstName("John");

System.out.println();
System.out.println("Original object: " + info);
System.out.println("Cloned object: " + clonedInfo);
}

@SuppressWarnings("unchecked")
public static <T> T cloneObject(T src){
return (T) XSTREAM.fromXML(XSTREAM.toXML(src));
}

}


結果顯示如下:
Original object: Info [firstName=Cheung, lastName=null, age=24, hobbies=[Sports, Reading], parent=Parent [firstName=Tom, lastName=Cheung]]
Now, we are going to clone info object

Cloned object: Info [firstName=Cheung, lastName=null, age=24, hobbies=[Sports, Reading], parent=Parent [firstName=Tom, lastName=Cheung]]

Change original info age to 25 and parent first name set to John.

Original object: Info [firstName=Cheung, lastName=null, age=25, hobbies=[Sports, Reading], parent=Parent [firstName=John, lastName=Cheung]]
Cloned object: Info [firstName=Cheung, lastName=null, age=24, hobbies=[Sports, Reading], parent=Parent [firstName=Tom, lastName=Cheung]]

可以看出連 Parent 在內也一起複製了,就算更改了原本的 Info Object 也不會改動到複製出來的 Object,即是 2 個變數的 Pointer 不是指向同一個位置。

相關書籍: Head First Java, 2nd EditionEffective Java (2nd Edition)Sams Teach Yourself Java in 24 Hours (5th Edition)