前言
一般Java對象的複製包含 淺拷貝、深度複製、BeanUtils.copyProperties() 等三種方式。
對象的克隆是指創建一個新的對象,且新的對象的狀態與原始對象的狀態相同且我們對新對象修改時,不影響原對象的狀態。
原理如下
clone()是object類的protected 方法,只有類的對象自己可以克隆自己
因此,必須實現cloneable接口纔可以使用obj.clone()方法,如下方式
淺拷貝
class CloneClass implements Cloneable{
public int a;
public Object clone(){
CloneClass o = null;
try{
o = (CloneClass)super.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return o;
}
}
深度複製
class CloneClass implements Cloneable{
public int a;
public Class1 t;
public CloneClass (int a,Class1 t) {
this.a = a;
this.t = t;
}
public Object clone(){
CloneClass o = null;
try{
o = (CloneClass)super.clone();
o.test = (Class1)t.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return o;
}
}
//Class1 也必須實現Cloneable接口
class Class1 implements Cloneable{
public Object clone(){
Class1 o = null;
try{
o = (Class1 )super.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return o;
}
}
淺拷貝clone()
如果對象中的所有數據域都是數值或者基本類型,使用clone()即可滿足需求,如:
Person p = new Person();
Person p1 = p.clone();
這樣p和p1分別指向不同的對象。
深度複製
如果在對象中包含子對象的引用,拷貝的結果是使得兩個域引用同一個對象,默認的拷貝是淺拷貝,沒有拷貝包含在對象中的內部對象。
如果子對象是不可變的,如String,這沒有什麼問題;如果對象是可變的,必須重新定義clone方法;
序列化可克隆(深拷貝)
- 爲克隆使用序列化, * 直接將對象序列化到輸出流中,然後將其讀回,這樣產生的新對象是對現有對象的一個深拷貝 *
在此過程中,不必將對象寫出到文件,可以用ByteArrayOutPutStream將數據保存到字節數組中 * *
這個方法很靈巧,它通常會比顯示地構建新對象並複製或克隆數據域的克隆方法慢得多
public class SerialCloneTest
{
public static void main(String[] args)
{
Employee harry = new Employee("Harry Hacker", 35000);
// clone harry
Employee harry2 = (Employee) harry.clone();
System.out.println(harry==harry2);
System.out.println(harry);
System.out.println(harry2);
}
}
/**
A class whose clone method uses serialization.
*/
class SerialCloneable implements Cloneable, Serializable
{
private static final long serialVersionUID = 1L;
//深拷貝
public Object clone()
{
try
{
// save the object to a byte array
//將該對象序列化成流,因爲寫在流裏的是對象的一個拷貝,而原對象仍然存在於JVM裏面
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bout);
out.writeObject(this);
out.close();
// read a clone of the object from the byte array
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream in = new ObjectInputStream(bin);
Object ret = in.readObject();
in.close();
return ret;
}
catch (Exception e)
{
return null;
}
}
}
/**
The familiar Employee class, redefined to extend the
SerialCloneable class.
*/
class Employee extends SerialCloneable
{
private static final long serialVersionUID = 1L;
private String name;
private double salary;
public Employee(String n, double s)
{
name = n;
salary = s;
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public String toString()
{
return getClass().getName()
+ "[name=" + name
+ ",salary=" + salary
+ "]";
}
}
BeanUtils.copyProperties()方式
下面我們通過三個測試類來說明用法
Person
public class Person {
private String name;
private String sex;
private int age;
private Date birthday;
private Dog dog;
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
private Double high;
public String getName() {
return name;
}
public Double getHigh() {
return high;
}
public void setHigh(Double high) {
this.high = high;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", birthday=" + birthday +
", dog=" + dog +
", high=" + high +
'}';
}
}
Dog
public class Dog {
public String dogName;
public String getDogName() {
return dogName;
}
public void setDogName(String dogName) {
this.dogName = dogName;
}
@Override
public String toString() {
return "Dog{" +
"dogName='" + dogName + '\'' +
'}';
}
}
BeanUtilTest
import org.apache.commons.beanutils.BeanUtils;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;
public class BeanUtilTest {
public static void main(String[] args) {
Person per = new Person();
Person per1 = new Person();
per.setName("zhangsan");
per.setSex("男");
per.setAge(20);
per.setBirthday(new Date());
Dog dog = new Dog();
dog.setDogName("1111111111111111");
per.setDog(dog);
try {
BeanUtils.copyProperties(per1, per);
Dog dog1 = per.getDog();
dog1.setDogName("2222222222222222");
per.setName("666666666666");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
System.out.println(per.toString());
System.out.println(per1.toString());
}
}
總結
1、針對對象中的一般字段可以實現複製對象和源對象各自修改互不影響(如person的name屬性)
2、針對裏面的引用對象,沒有實現嵌套的拷貝(如Dog對象)