一、BeanUtil本地簡單測試
在項目中因爲須要對某些對象進行深度拷貝而後進行持久化操做,想到了apache和spring都提供了BeanUtils的深度拷貝工具包,本身寫了幾個Demo作測試,定義了兩個類User和Person,其中User的屬性引用了Person類。spring
public class User { private int id; private String username;// 用戶姓名 private String sex;// 性別 private Date birthday;// 生日 private String address;// 地址 private Person person; //包裝類 //get set方法此處省略 }
//Person類 public class Person { private int id; private String userName ; private int age ; private String mobilePhone ; public Person(){} public Person(int id,String userName, int age, String mobilePhone) { this.id = id; this.userName = userName; this.age = age; this.mobilePhone = mobilePhone; } //get set方法此處省略 }
編寫測試方法進行調研,主要是查看對象中包裝的對象是否引用了同一個地址,從而判斷是不是深度拷貝仍是淺拷貝apache
@Test public void CopyTest(){ User user=new User(); user.setId(1); user.setSex("man"); user.setUsername("Tison"); user.setAddress("address"); user.setBirthday(new Date()); Person p=new Person(); p.setUserName("p1"); user.setPerson(p); User target=new User(); BeanUtils.copyProperties(user,target); System.out.println(target.getAddress()==user.getAddress()); System.out.println(target.getPerson()==user.getPerson()); System.out.println(user.toString()); System.out.println(target.toString()); }
打印結果:安全
1mybatis 2工具 3性能 4測試 |
|
兩個對象的哈希碼不相等,引用對象的地址也不相同,而且對包裝對象的操做都是互不影響,簡單測試下能夠看到BeanUtils實現了深度拷貝的效果。
二、項目測試
可是到了本人所作的項目中,BeanUtils的效果就不是深度拷貝了,用僞代碼進行簡單說明:
//source爲A對象,target爲B對象 BeanUtils.copyProperties(source,target); //調用setSecret方法將B對象的某型包裝屬性set爲null setSecret(target); //分別打印對比的結果 System.out.println(target.getUserInfo()==source.getUserInfo()); System.out.println(source.getUserInfo().hashCode()); System.out.println(target.getUserInfo().hashCode());
1 2 3 4 |
|
兩份對象裏的包裝對象內存地址比較結果爲true,並且對象的哈希嗎指向了同一位置。
顯而易見,BeanUtils並未進行深度拷貝。本人在項目中正由於採用了BeanUtils的拷貝方法,在對兩份對象的不一樣操做時都會互相影響致使持久化的異常,可見基於BeanUtils的拷貝方法並非萬能的,並且因爲源碼中採用反射機制,其性能也被許多博主詬病,在網上進行了綜合調研,發現BeanUtils的copyProperties()方法的確存在着淺拷貝的狀況,這對於持久化操做實體類的時候是很大的一個坑,那麼最靠譜的深拷貝方法仍是要序列化後寫流的方法,只是該方法須要實現Serializable接口。
三、深拷貝
深複製(深克隆)被複制對象的全部變量都含有與原來的對象相同的值,除去那些引用其餘對象的變量,那些引用其餘對象的變量將指向被複制過的新對象,而再也不試原有的那些被引用的對象,換言之,深複製把要複製的對象所引用的對象都複製了一遍。
把對象寫到流裏的過程是串行化(Serilization)過程,可是在Java程序師圈子裏又很是形象地稱爲「冷凍」或者「醃鹹菜(picking)」過程;而把對象從流中讀出來的並行化(Deserialization)過程則叫作「解凍」或者「回鮮(depicking)」過程。應當指出的是,寫在流裏的是對象的一個拷貝,而原對象仍然存在於JVM裏面,所以「醃成鹹菜」的只是對象的一個拷貝,Java鹹菜還能夠回鮮。在Java語言裏深複製一個對象,經常能夠先使對象實現Serializable接口,而後把對象(實際上只是對象的一個拷貝)寫到一個流裏(醃成鹹菜),再從流裏讀出來(把鹹菜回鮮),即可以重建對象。
在項目中咱們須要克隆的對象可能包含多層引用類型,這就要涉及到多層克隆問題,多層克隆不只要將克隆對象實現序列化接口,引用對象也一樣的要實現序列化接口:
public class User implements Serializable{ private int id; private String username;// 用戶姓名 private String sex;// 性別 private Date birthday;// 生日 private String address;// 地址 private Person person; //引用類型 public User myColon(){ User copy=null; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(this); //將流序列化成對象 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); copy = (User) ois.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return copy; } //此處省略get-set方法代碼 }
引用類型也須要實現Serializable接口,不然會序列化失敗。
public class Person implements Serializable { private int id; private String userName ; private int age ; private String mobilePhone ; public Person(){} public Person(int id,String userName, int age, String mobilePhone) { this.id = id; this.userName = userName; this.age = age; this.mobilePhone = mobilePhone; } //此處省略get-set方法 }
結論:
1 2 3 |
|