一个较实用的Pojo(实体)基类

来源:互联网 发布:长春工业大学知乎 编辑:程序博客网 时间:2024/04/30 06:51


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


今天实现了一个较实用的Pojo(实体)基类

 

 

呵呵!也许你会觉得就单单重写了Object根类的equals、hashCode、toString这三个方法有什么意义?

实质上,如果你封装过泛型集合基类,并在泛型集合基类中玩过根据自定义属性排序的话,那么你会发现实现这样的一个Pojo基类很有必要!

 

先看看代码的实现:

 

 

view plain
  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”这样让人难以理解的文字内容。

 

 

我自己也编写了一个测例:

 

view plain
  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. }  

 

view plain
  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日


原创粉丝点击