转自: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日