轉自:http://blog.csdn.net/CodingMouse/article/details/4064007
今天實現了一個較實用的Pojo(實體)基類
呵呵!也許你會覺得就單單重寫了Object根類的equals、hashCode、toString這三個方法有什麼意義?
實質上,如果你封裝過泛型集合基類,並在泛型集合基類中玩過根據自定義屬性排序的話,那麼你會發現實現這樣的一個Pojo基類很有必要!
先看看代碼的實現:
- package com.china.codingmouse.cmsdk4j.pojo;
- import java.io.Serializable;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.util.List;
- import java.util.Vector;
- /**
- * BasePojo Pojo(實體)基類
- * @author CodingMouse
- * @version 1.0.0.1 2009-4-10
- */
- public class BasePojo implements Serializable {
- private static final long serialVersionUID = -5520682886492533483L; // 版本序列號
- /**
- * 指示其他某個對象是否與此對象“相等”
- */
- @Override
- public boolean equals(Object obj) {
- // 自身比較
- if (obj == this) {
- return true;
- }
- // 類型相同
- if (obj.getClass() == this.getClass()) {
- // 當前類反射方法組
- Method[] thisMethodGroup = this.getClass().getMethods();
- try {
- // 遍歷反射方法組並提取當前類屬性的getter方法
- for (Method method : thisMethodGroup) {
- // 過濾與當前類屬性無關的get方法
- if (method.getName().startsWith("get")
- && !method.getName().equals("getClass")) {
- // 將當前類屬性的getter方法與比較類屬性的getter方法值作比較
- Method currentMethod = obj.getClass().getMethod(method.getName());
- // 執行方法以獲取返回值比較(關鍵點:注意參數不相同)
- Object objReturnValue = currentMethod.invoke(obj);
- Object thisReturnValue = method.invoke(this);
- // 空值報異
- if (objReturnValue == null) {
- System.err.println("異常信息:類" + obj.getClass().getName()
- + "中的" + currentMethod.getName() + "方法爲null值!無法進行對象比較!");
- }
- if (thisReturnValue == null) {
- System.err.println("異常信息:類" + this.getClass().getName()
- + "中的" + method.getName() + "方法爲null值!無法進行對象比較!");
- }
- // 返回值不相等則返回邏輯假
- if (!objReturnValue.equals(thisReturnValue)) {
- return false;
- }
- }
- }
- } catch (SecurityException ex) {
- System.err.println("異常信息:參數錯誤,安全管理器檢測到安全侵犯!/r/n" + ex.getMessage());
- } catch (NoSuchMethodException ex) {
- System.err.println("異常信息:參數錯誤,無法找到某一特定的方法!/r/n" + ex.getMessage());
- } catch (IllegalArgumentException ex) {
- System.err.println("異常信息:參數錯誤,向方法傳遞了一個不合法或不正確的參數!/r/n" + ex.getMessage());
- } catch (IllegalAccessException ex) {
- System.err.println("異常信息:參數錯誤,對象定義無法訪問,無法反射性地創建一個實例!/r/n" + ex.getMessage());
- } catch (InvocationTargetException ex) {
- System.err.println("異常信息:參數錯誤,由調用方法或構造方法所拋出異常的經過檢查的異常!/r/n" + ex.getMessage());
- }
- }
- // 通過不相等比較則返回邏輯真
- return true;
- }
- /**
- * 返回該對象的哈希碼值
- */
- @Override
- public int hashCode() {
- // 生成簡單的位運算hash散列碼
- String key = this.toString();
- int prime = key.hashCode();
- int hash = prime;
- for (int i = 0; i < key.length(); i++) {
- hash ^= (hash << 23 >> 17) ^ key.charAt(i) * 13131;
- }
- // 返回結果
- return (hash % prime) * 33;
- }
- /**
- * 返回該對象的字符串表示(類似數組的toString方法輸出結果)
- */
- @Override
- public String toString() {
- // 當前類反射方法組
- Method[] methodGroup = this.getClass().getMethods();
- // 保存內容
- StringBuffer content = new StringBuffer("[");
- // 保存屬性的getter方法組
- List<Method> getMethodGroup = new Vector<Method>();
- try {
- // 遍歷反射方法組並提取屬性的getter方法
- for (Method method : methodGroup) {
- // 過濾與屬性無關的get方法
- if (method.getName().startsWith("get")
- && !method.getName().equals("getClass")) {
- // 保存屬性的getter方法
- getMethodGroup.add(method);
- }
- }
- // 處理僅包含屬性的getter方法
- for (int i = 0; i < getMethodGroup.size(); i++) {
- // 執行get方法並拼接獲取到的返回值(如果底層方法返回類型爲 void,則該調用返回 null)
- content.append(getMethodGroup.get(i).invoke(this)
- + ((i < getMethodGroup.size() - 1) ? ",/u0020" : "]"));
- }
- } catch (IllegalAccessException ex) {
- System.err.println("異常信息:參數錯誤,對象定義無法訪問,無法反射性地創建一個實例!/r/n" + ex.getMessage());
- } catch (IllegalArgumentException ex) {
- System.err.println("異常信息:參數錯誤,向方法傳遞了一個不合法或不正確的參數!/r/n" + ex.getMessage());
- } catch (InvocationTargetException ex) {
- System.err.println("異常信息:參數錯誤,由調用方法或構造方法所拋出異常的經過檢查的異常!/r/n" + ex.getMessage());
- }
- // 返回結果
- return content.toString();
- }
- }
衆所周知,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”這樣讓人難以理解的文字內容。
我自己也編寫了一個測例:
- package com.china.codingmouse.cmsdk4j.example.pojo;
- import java.io.Serializable;
- import java.sql.Timestamp;
- import com.china.codingmouse.cmsdk4j.pojo.BasePojo;
- /**
- * UserPojo 用戶信息實體類
- * @author CodingMouse
- * @version 1.0.0.1 2009-4-10
- */
- public class UserPojo extends BasePojo implements Serializable {
- private static final long serialVersionUID = -2214074259397104603L; // 版本序列號
- private int id; // 用戶ID
- private String name; // 用戶姓名
- private boolean sex; // 用戶性別
- private int age; // 用戶年齡
- private String address; // 用戶住址
- private Timestamp regTime; // 用戶註冊時間
- /**
- * 默認構造器
- */
- public UserPojo() {
- super();
- }
- /**
- * 參數化構造器
- * @param id 用戶ID
- * @param name 用戶姓名
- * @param sex 用戶性別
- * @param age 用戶年齡
- * @param address 用戶住址
- * @param regTime 用戶註冊時間
- */
- public UserPojo(int id, String name, boolean sex, int age, String address, Timestamp regTime) {
- super();
- this.setId(id);
- this.setName(name);
- this.setSex(sex);
- this.setAge(age);
- this.setAddress(address);
- this.setRegTime(regTime);
- }
- /**
- * 用戶ID取值方法
- * @return 用戶ID
- */
- public int getId() {
- return id;
- }
- /**
- * 用戶ID賦值方法
- * @param id 用戶ID
- */
- public void setId(int id) {
- this.id = id;
- }
- /**
- * 用戶姓名取值方法
- * @return 用戶姓名
- */
- public String getName() {
- return name;
- }
- /**
- * 用戶姓名賦值方法
- * @param name 用戶姓名
- */
- public void setName(String name) {
- this.name = name;
- }
- /**
- * 用戶性別取值方法
- * @return 用戶性別
- */
- public boolean getSex() {
- return sex;
- }
- /**
- * 用戶性別賦值方法
- * @param sex 用戶性別
- */
- public void setSex(boolean sex) {
- this.sex = sex;
- }
- /**
- * 用戶年齡取值方法
- * @return 用戶年齡
- */
- public int getAge() {
- return age;
- }
- /**
- * 用戶年齡賦值方法
- * @param age 用戶年齡
- */
- public void setAge(int age) {
- this.age = age;
- }
- /**
- * 用戶住址取值方法
- * @return 用戶住址
- */
- public String getAddress() {
- return address;
- }
- /**
- * 用戶住址賦值方法
- * @param address 用戶住址
- */
- public void setAddress(String address) {
- this.address = address;
- }
- /**
- * 用戶註冊時間取值方法
- * @return 用戶註冊時間
- */
- public Timestamp getRegTime() {
- return regTime;
- }
- /**
- * 用戶註冊時間賦值方法
- * @param regTime 用戶註冊時間
- */
- public void setRegTime(Timestamp regTime) {
- this.regTime = regTime;
- }
- }
- package com.china.codingmouse.cmsdk4j.example.pojo.test;
- import java.sql.Timestamp;
- import com.china.codingmouse.cmsdk4j.example.pojo.UserPojo;
- /**
- * 用戶信息實體類Equals與HashCode方法測試類
- * @author CodingMouse
- * @version 1.0.0.1 2009-4-10
- */
- public class UserPojoEqualsAndHashCodeTest {
- /**
- * 測試類主方法
- * @param args
- */
- public static void main(String[] args) {
- UserPojo up1 = new UserPojo(3, "鄧超", true, 25, "四川隆昌", new Timestamp(System.currentTimeMillis()));
- UserPojo up2 = new UserPojo(3, "鄧超", true, 25, "四川隆昌", new Timestamp(System.currentTimeMillis()));
- System.out.println("User1的內容:" + up1);
- System.out.println("User2的內容:" + up2);
- System.err.println("User1的散列碼:" + up1.hashCode());
- System.err.println("User2的散列碼:" +up2.hashCode());
- System.out.println("測試User1與User2地址(==)相等:" + (up1 == up2));
- System.out.println("測試User1與User2內容(equals)相等:" + up1.equals(up2));
- UserPojo up3 = new UserPojo(6, "CodingMouse", false, 22, "中華人民共和國四川成都", new Timestamp(System.currentTimeMillis()));
- UserPojo up4 = new UserPojo(13, "Michael Jackson", false, 53, "美利堅合衆國紐約市唐人街", new Timestamp(System.currentTimeMillis()));
- System.out.println("User3的內容:" + up3);
- System.out.println("User4的內容:" + up4);
- System.err.println("User3的散列碼:" +up3.hashCode());
- System.err.println("User4的散列碼:" +up4.hashCode());
- System.out.println("測試User3與User4地址(==)相等:" + (up3 == up4));
- System.out.println("測試User3與User4內容(equals)相等:" + up3.equals(up4));
- }
- }
在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日