一個較實用的Pojo(實體)基類


轉自:http://blog.csdn.net/CodingMouse/article/details/4064007


今天實現了一個較實用的Pojo(實體)基類

 

 

呵呵!也許你會覺得就單單重寫了Object根類的equals、hashCode、toString這三個方法有什麼意義?

實質上,如果你封裝過泛型集合基類,並在泛型集合基類中玩過根據自定義屬性排序的話,那麼你會發現實現這樣的一個Pojo基類很有必要!

 

先看看代碼的實現:

 

 

  1. package com.china.codingmouse.cmsdk4j.pojo;  
  2.   
  3. import java.io.Serializable;  
  4. import java.lang.reflect.InvocationTargetException;  
  5. import java.lang.reflect.Method;  
  6. import java.util.List;  
  7. import java.util.Vector;  
  8.   
  9. /** 
  10.  * BasePojo Pojo(實體)基類 
  11.  * @author CodingMouse 
  12.  * @version 1.0.0.1 2009-4-10 
  13.  */  
  14. public class BasePojo implements Serializable {  
  15.   
  16.     private static final long serialVersionUID = -5520682886492533483L;  // 版本序列號  
  17.       
  18.     /** 
  19.      * 指示其他某個對象是否與此對象“相等” 
  20.      */  
  21.     @Override  
  22.     public boolean equals(Object obj) {  
  23.         // 自身比較  
  24.         if (obj == this) {  
  25.             return true;  
  26.         }  
  27.         // 類型相同  
  28.         if (obj.getClass() == this.getClass()) {  
  29.             // 當前類反射方法組  
  30.             Method[] thisMethodGroup = this.getClass().getMethods();  
  31.               
  32.             try {  
  33.                 // 遍歷反射方法組並提取當前類屬性的getter方法  
  34.                 for (Method method : thisMethodGroup) {  
  35.                     // 過濾與當前類屬性無關的get方法  
  36.                     if (method.getName().startsWith("get")  
  37.                         && !method.getName().equals("getClass")) {  
  38.                         // 將當前類屬性的getter方法與比較類屬性的getter方法值作比較  
  39.                         Method currentMethod = obj.getClass().getMethod(method.getName());  
  40.                         // 執行方法以獲取返回值比較(關鍵點:注意參數不相同)  
  41.                         Object objReturnValue = currentMethod.invoke(obj);  
  42.                         Object thisReturnValue = method.invoke(this);  
  43.                         // 空值報異  
  44.                         if (objReturnValue == null) {  
  45.                             System.err.println("異常信息:類" + obj.getClass().getName()   
  46.                                 + "中的" + currentMethod.getName() + "方法爲null值!無法進行對象比較!");  
  47.                         }  
  48.                         if (thisReturnValue == null) {  
  49.                             System.err.println("異常信息:類" + this.getClass().getName()   
  50.                                 + "中的" + method.getName() + "方法爲null值!無法進行對象比較!");  
  51.                         }  
  52.                         // 返回值不相等則返回邏輯假  
  53.                         if (!objReturnValue.equals(thisReturnValue)) {  
  54.                             return false;  
  55.                         }  
  56.                     }  
  57.                 }                     
  58.             } catch (SecurityException ex) {  
  59.                 System.err.println("異常信息:參數錯誤,安全管理器檢測到安全侵犯!/r/n" + ex.getMessage());  
  60.             } catch (NoSuchMethodException ex) {  
  61.                 System.err.println("異常信息:參數錯誤,無法找到某一特定的方法!/r/n" + ex.getMessage());  
  62.             } catch (IllegalArgumentException ex) {  
  63.                 System.err.println("異常信息:參數錯誤,向方法傳遞了一個不合法或不正確的參數!/r/n" + ex.getMessage());  
  64.             } catch (IllegalAccessException ex) {  
  65.                 System.err.println("異常信息:參數錯誤,對象定義無法訪問,無法反射性地創建一個實例!/r/n" + ex.getMessage());  
  66.             } catch (InvocationTargetException ex) {  
  67.                 System.err.println("異常信息:參數錯誤,由調用方法或構造方法所拋出異常的經過檢查的異常!/r/n" + ex.getMessage());  
  68.             }  
  69.         }  
  70.           
  71.         // 通過不相等比較則返回邏輯真  
  72.         return true;  
  73.           
  74.     }  
  75.   
  76.     /** 
  77.      * 返回該對象的哈希碼值 
  78.      */  
  79.     @Override  
  80.     public int hashCode() {  
  81.   
  82.         // 生成簡單的位運算hash散列碼  
  83.         String key = this.toString();  
  84.         int prime = key.hashCode();  
  85.         int hash = prime;  
  86.         for (int i = 0; i < key.length(); i++) {  
  87.              hash ^= (hash << 23 >> 17) ^ key.charAt(i) * 13131;  
  88.         }  
  89.         // 返回結果  
  90.         return (hash % prime) * 33;  
  91.           
  92.     }  
  93.   
  94.     /** 
  95.      * 返回該對象的字符串表示(類似數組的toString方法輸出結果) 
  96.      */  
  97.     @Override  
  98.     public String toString() {  
  99.           
  100.         // 當前類反射方法組  
  101.         Method[] methodGroup = this.getClass().getMethods();  
  102.         // 保存內容  
  103.         StringBuffer content = new StringBuffer("[");  
  104.         // 保存屬性的getter方法組  
  105.         List<Method> getMethodGroup = new Vector<Method>();  
  106.           
  107.         try {  
  108.             // 遍歷反射方法組並提取屬性的getter方法  
  109.             for (Method method : methodGroup) {  
  110.                 // 過濾與屬性無關的get方法  
  111.                 if (method.getName().startsWith("get")  
  112.                     && !method.getName().equals("getClass")) {  
  113.                     // 保存屬性的getter方法  
  114.                     getMethodGroup.add(method);  
  115.                 }  
  116.             }  
  117.             // 處理僅包含屬性的getter方法  
  118.             for (int i = 0; i < getMethodGroup.size(); i++) {  
  119.                 // 執行get方法並拼接獲取到的返回值(如果底層方法返回類型爲 void,則該調用返回 null)  
  120.                 content.append(getMethodGroup.get(i).invoke(this)   
  121.                     + ((i < getMethodGroup.size() - 1) ? ",/u0020" : "]"));  
  122.             }  
  123.         } catch (IllegalAccessException ex) {  
  124.             System.err.println("異常信息:參數錯誤,對象定義無法訪問,無法反射性地創建一個實例!/r/n" + ex.getMessage());  
  125.         } catch (IllegalArgumentException ex) {  
  126.             System.err.println("異常信息:參數錯誤,向方法傳遞了一個不合法或不正確的參數!/r/n" + ex.getMessage());  
  127.         } catch (InvocationTargetException ex) {  
  128.             System.err.println("異常信息:參數錯誤,由調用方法或構造方法所拋出異常的經過檢查的異常!/r/n" + ex.getMessage());  
  129.         }  
  130.           
  131.         // 返回結果  
  132.         return content.toString();  
  133.           
  134.     }  
  135.       
  136. }  

 

 

衆所周知,String 、Math、還有包裝類(如:Integer、Float、Double、Boolean等)都重寫了Object的equals方法,這樣才使得它們不再比較引用(某些地方也稱爲“句柄”,“句柄”一詞貌似在Windows操作系統中經常使用,如“窗口句柄”),而是比較內容(值)相等。因爲,Object的equals()方法比較的是地址值。

 

特別需要注意的是:

Java語言對equals()的要求如下,

• 對稱性:如果x.equals(y)返回是“true”,那麼y.equals(x)也應該返回是“true”。
• 反射性:x.equals(x)必須返回是“true”。
• 類推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那麼z.equals(x)也應該返回是“true”。

• 還有一致性:如果x.equals(y)返回是“true”,只要x和y內容一直不變,不管你重複x.equals(y)多少次,返回都是“true”。
• 任何情況下,x.equals(null),永遠返回是“false”;x.equals(和x不同類型的對象)永遠返回是“false”。

那麼,又爲什麼要重寫hashCode方法呢?網上的一些文章中是這樣描述的:

 

我們應該先了解java判斷兩個對象是否相等的規則。

 

在Java的集合中,判斷兩個對象是否相等的規則是:


首先,判斷兩個對象的hashCode是否相等;

如果不相等,認爲兩個對象也不相等;
如果相等,則判斷兩個對象用equals運算是否相等;
如果不相等,認爲兩個對象也不相等;
如果相等,認爲兩個對象相等;

 

我們在equals方法中需要向下轉型,效率很低,所以先判斷hashCode方法可以提高效率。

 

一般來說,如果你要把一個類的對象放入集合中,那麼通常要爲其重寫equals()方法,讓它們比較地址值而不是內容值。特別地,如果要把你的類的對象放入散列中,那麼還要重寫hashCode()方法;要放到有序容器中,還要重寫compareTo()方法。

 

另外,網上也有這樣的一段描述:

 

關於在hibernate的pojo類中,重新equals()和hashcode()的問題:
1),重點是equals,重寫hashCode只是技術要求(爲了提高效率)
2),爲什麼要重寫equals呢,因爲在java的集合框架中,是通過equals來判斷兩個對象是否相等的
3),在hibernate中,經常使用set集合來保存相關對象,而set集合是不允許重複的。我們再來談談前面提到在向hashset集合中添加元素時,怎樣判斷對象是否相同的準則,前面說了兩條,其實只要重寫equals()這一條也可以。
但當hashset中元素比較多時,或者是重寫的equals()方法比較複雜時,我們只用equals()方法進行比較判斷,效率也會非常低,所以引入了hashcode()這個方法,只是爲了提高效率,但是我覺得這是非常有必要的(所以我們在前面以兩條準則來進行hashset的元素是否重複的判斷)。
比如可以這樣寫:
public int hashCode(){
        return 1;

} // 等價於hashcode無效
這樣做的效果就是在比較哈希碼的時候不能進行判斷,因爲每個對象返回的哈希碼都是1,每次都必須要經過比較equals()方法後才能進行判斷是否重複,這當然會引起效率的大大降低。

效率影響的體現可以在hashset、hashmap、hashtable、LinkedHashMap等帶hash字樣的集合中去測試,這裏就不演示了。

 

至於爲什麼我要重寫toString方法,有兩點理由:

1、作爲hashCode生成算法的一部分,與其內容直接相關,有更好的散列效果。

2、便於獲取其子類更詳細的內容描述,便於調試,而不是得到諸如“java.lang.Object@757aef”這樣讓人難以理解的文字內容。

 

 

我自己也編寫了一個測例:

 

  1. package com.china.codingmouse.cmsdk4j.example.pojo;  
  2.   
  3. import java.io.Serializable;  
  4. import java.sql.Timestamp;  
  5.   
  6. import com.china.codingmouse.cmsdk4j.pojo.BasePojo;  
  7.   
  8. /** 
  9.  * UserPojo 用戶信息實體類 
  10.  * @author CodingMouse 
  11.  * @version 1.0.0.1 2009-4-10 
  12.  */  
  13. public class UserPojo extends BasePojo implements Serializable {  
  14.   
  15.     private static final long serialVersionUID = -2214074259397104603L;  // 版本序列號  
  16.     private int id;             // 用戶ID  
  17.     private String name;        // 用戶姓名  
  18.     private boolean sex;        // 用戶性別  
  19.     private int age;            // 用戶年齡  
  20.     private String address;     // 用戶住址  
  21.     private Timestamp regTime;  // 用戶註冊時間  
  22.       
  23.     /** 
  24.      * 默認構造器 
  25.      */  
  26.     public UserPojo() {  
  27.         super();  
  28.     }  
  29.     /** 
  30.      * 參數化構造器 
  31.      * @param id       用戶ID 
  32.      * @param name     用戶姓名 
  33.      * @param sex      用戶性別 
  34.      * @param age      用戶年齡 
  35.      * @param address  用戶住址 
  36.      * @param regTime  用戶註冊時間 
  37.      */  
  38.     public UserPojo(int id, String name, boolean sex, int age, String address, Timestamp regTime) {  
  39.         super();  
  40.         this.setId(id);  
  41.         this.setName(name);  
  42.         this.setSex(sex);  
  43.         this.setAge(age);  
  44.         this.setAddress(address);  
  45.         this.setRegTime(regTime);  
  46.     }  
  47.     /** 
  48.      * 用戶ID取值方法 
  49.      * @return 用戶ID 
  50.      */  
  51.     public int getId() {  
  52.         return id;  
  53.     }  
  54.     /** 
  55.      * 用戶ID賦值方法 
  56.      * @param id 用戶ID 
  57.      */  
  58.     public void setId(int id) {  
  59.         this.id = id;  
  60.     }  
  61.     /** 
  62.      * 用戶姓名取值方法 
  63.      * @return 用戶姓名 
  64.      */  
  65.     public String getName() {  
  66.         return name;  
  67.     }  
  68.     /** 
  69.      * 用戶姓名賦值方法 
  70.      * @param name 用戶姓名 
  71.      */  
  72.     public void setName(String name) {  
  73.         this.name = name;  
  74.     }  
  75.     /** 
  76.      * 用戶性別取值方法 
  77.      * @return 用戶性別 
  78.      */  
  79.     public boolean getSex() {  
  80.         return sex;  
  81.     }  
  82.     /** 
  83.      * 用戶性別賦值方法 
  84.      * @param sex 用戶性別 
  85.      */  
  86.     public void setSex(boolean sex) {  
  87.         this.sex = sex;  
  88.     }  
  89.     /** 
  90.      * 用戶年齡取值方法 
  91.      * @return 用戶年齡 
  92.      */  
  93.     public int getAge() {  
  94.         return age;  
  95.     }  
  96.     /** 
  97.      * 用戶年齡賦值方法 
  98.      * @param age 用戶年齡 
  99.      */  
  100.     public void setAge(int age) {  
  101.         this.age = age;  
  102.     }  
  103.     /** 
  104.      * 用戶住址取值方法 
  105.      * @return 用戶住址 
  106.      */  
  107.     public String getAddress() {  
  108.         return address;  
  109.     }  
  110.     /** 
  111.      * 用戶住址賦值方法 
  112.      * @param address 用戶住址 
  113.      */  
  114.     public void setAddress(String address) {  
  115.         this.address = address;  
  116.     }  
  117.     /** 
  118.      * 用戶註冊時間取值方法 
  119.      * @return 用戶註冊時間 
  120.      */  
  121.     public Timestamp getRegTime() {  
  122.         return regTime;  
  123.     }  
  124.     /** 
  125.      * 用戶註冊時間賦值方法 
  126.      * @param regTime 用戶註冊時間 
  127.      */  
  128.     public void setRegTime(Timestamp regTime) {  
  129.         this.regTime = regTime;  
  130.     }  
  131.       
  132. }  

 

  1. package com.china.codingmouse.cmsdk4j.example.pojo.test;  
  2.   
  3. import java.sql.Timestamp;  
  4.   
  5. import com.china.codingmouse.cmsdk4j.example.pojo.UserPojo;  
  6.   
  7. /** 
  8.  * 用戶信息實體類Equals與HashCode方法測試類 
  9.  * @author CodingMouse 
  10.  * @version 1.0.0.1 2009-4-10 
  11.  */  
  12. public class UserPojoEqualsAndHashCodeTest {  
  13.   
  14.     /** 
  15.      * 測試類主方法 
  16.      * @param args 
  17.      */  
  18.     public static void main(String[] args) {  
  19.   
  20.         UserPojo up1 = new UserPojo(3"鄧超"true25"四川隆昌"new Timestamp(System.currentTimeMillis()));  
  21.         UserPojo up2 = new UserPojo(3"鄧超"true25"四川隆昌"new Timestamp(System.currentTimeMillis()));  
  22.         System.out.println("User1的內容:" + up1);  
  23.         System.out.println("User2的內容:" + up2);  
  24.         System.err.println("User1的散列碼:" + up1.hashCode());  
  25.         System.err.println("User2的散列碼:" +up2.hashCode());  
  26.         System.out.println("測試User1與User2地址(==)相等:" + (up1 == up2));  
  27.         System.out.println("測試User1與User2內容(equals)相等:" + up1.equals(up2));  
  28.         UserPojo up3 = new UserPojo(6"CodingMouse"false22"中華人民共和國四川成都"new Timestamp(System.currentTimeMillis()));  
  29.         UserPojo up4 = new UserPojo(13"Michael Jackson"false53"美利堅合衆國紐約市唐人街"new Timestamp(System.currentTimeMillis()));  
  30.         System.out.println("User3的內容:" + up3);  
  31.         System.out.println("User4的內容:" + up4);  
  32.         System.err.println("User3的散列碼:" +up3.hashCode());  
  33.         System.err.println("User4的散列碼:" +up4.hashCode());  
  34.         System.out.println("測試User3與User4地址(==)相等:" + (up3 == up4));  
  35.         System.out.println("測試User3與User4內容(equals)相等:" + up3.equals(up4));  
  36.           
  37.     }  
  38.   
  39. }  

 

 

在Eclipse3.3控制檯輸出的最終的運行結果爲:

 

User1的內容:[true, 25, 2009-04-10 18:39:54.557, 四川隆昌, 鄧超, 3]
User2的內容:[true, 25, 2009-04-10 18:39:54.557, 四川隆昌, 鄧超, 3]
User1的散列碼:524379269
User2的散列碼:524379269
測試User1與User2地址(==)相等:false
測試User1與User2內容(equals)相等:true
User3的內容:[false, 22, 2009-04-10 18:39:54.563, 中華人民共和國四川成都, CodingMouse, 6]
User4的內容:[false, 53, 2009-04-10 18:39:54.563, 美利堅合衆國紐約市唐人街, Michael Jackson, 13]
User3的散列碼:-715569909
User4的散列碼:956891732

測試User3與User4地址(==)相等:false
測試User3與User4內容(equals)相等:false

 

 

反正自我感覺挺良好的,如果您覺得有不妥的地方,還煩請幫忙指出爲感!

 

 

                                                                                            CodingMouse

                                                                                           2009年4月11日


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章