转自:http://blog.csdn.net/CodingMouse/article/details/4064007
今天实现了一个较实用的Pojo(实体)基类
呵呵!也许你会觉得就单单重写了Object根类的equals、hashCode、toString这三个方法有什么意义?
实质上,如果你封装过泛型集合基类,并在泛型集合基类中玩过根据自定义属性排序的话,那么你会发现实现这样的一个Pojo基类很有必要!
先看看代码的实现:
packagecom.china.codingmouse.cmsdk4j.pojo;
importjava.io.Serializable;
importjava.lang.reflect.InvocationTargetException;
importjava.lang.reflect.Method;
importjava.util.List;
importjava.util.Vector;
/**
* BasePojo Pojo(实体)基类
* @author CodingMouse
* @version 1.0.0.1 2009-4-10
*/
publicclassBasePojoimplementsSerializable {
privatestaticfinallongserialVersionUID = -5520682886492533483L;// 版本序列号
/**
* 指示其他某个对象是否与此对象“相等”
*/
@Override
publicbooleanequals(Object obj) {
// 自身比较
if(obj ==this) {
returntrue;
}
// 类型相同
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)) {
returnfalse;
}
}
}
} 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());
}
}
// 通过不相等比较则返回逻辑真
returntrue;
}
/**
* 返回该对象的哈希码值
*/
@Override
publicinthashCode() {
// 生成简单的位运算hash散列码
String key = this.toString();
intprime = key.hashCode();
inthash = prime;
for(inti =0; i
hash ^= (hash <>17) ^ key.charAt(i) *13131;
}
// 返回结果
return(hash % prime) *33;
}
/**
* 返回该对象的字符串表示(类似数组的toString方法输出结果)
*/
@Override
publicString toString() {
// 当前类反射方法组
Method[] methodGroup = this.getClass().getMethods();
// 保存内容
StringBuffer content = newStringBuffer("[");
// 保存属性的getter方法组
List getMethodGroup = newVector();
try{
// 遍历反射方法组并提取属性的getter方法
for(Method method : methodGroup) {
// 过滤与属性无关的get方法
if(method.getName().startsWith("get")
&& !method.getName().equals("getClass")) {
// 保存属性的getter方法
getMethodGroup.add(method);
}
}
// 处理仅包含属性的getter方法
for(inti =0; i
// 执行get方法并拼接获取到的返回值(如果底层方法返回类型为 void,则该调用返回 null)
content.append(getMethodGroup.get(i).invoke(this)
+ ((i
}
} 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());
}
// 返回结果
returncontent.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”这样让人难以理解的文字内容。
我自己也编写了一个测例:
packagecom.china.codingmouse.cmsdk4j.example.pojo;
importjava.io.Serializable;
importjava.sql.Timestamp;
importcom.china.codingmouse.cmsdk4j.pojo.BasePojo;
/**
* UserPojo 用户信息实体类
* @author CodingMouse
* @version 1.0.0.1 2009-4-10
*/
publicclassUserPojoextendsBasePojoimplementsSerializable {
privatestaticfinallongserialVersionUID = -2214074259397104603L;// 版本序列号
privateintid;// 用户ID
privateString name;// 用户姓名
privatebooleansex;// 用户性别
privateintage;// 用户年龄
privateString address;// 用户住址
privateTimestamp regTime;// 用户注册时间
/**
* 默认构造器
*/
publicUserPojo() {
super();
}
/**
* 参数化构造器
* @param id 用户ID
* @param name 用户姓名
* @param sex 用户性别
* @param age 用户年龄
* @param address 用户住址
* @param regTime 用户注册时间
*/
publicUserPojo(intid, String name,booleansex,intage, 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
*/
publicintgetId() {
returnid;
}
/**
* 用户ID赋值方法
* @param id 用户ID
*/
publicvoidsetId(intid) {
this.id = id;
}
/**
* 用户姓名取值方法
* @return 用户姓名
*/
publicString getName() {
returnname;
}
/**
* 用户姓名赋值方法
* @param name 用户姓名
*/
publicvoidsetName(String name) {
this.name = name;
}
/**
* 用户性别取值方法
* @return 用户性别
*/
publicbooleangetSex() {
returnsex;
}
/**
* 用户性别赋值方法
* @param sex 用户性别
*/
publicvoidsetSex(booleansex) {
this.sex = sex;
}
/**
* 用户年龄取值方法
* @return 用户年龄
*/
publicintgetAge() {
returnage;
}
/**
* 用户年龄赋值方法
* @param age 用户年龄
*/
publicvoidsetAge(intage) {
this.age = age;
}
/**
* 用户住址取值方法
* @return 用户住址
*/
publicString getAddress() {
returnaddress;
}
/**
* 用户住址赋值方法
* @param address 用户住址
*/
publicvoidsetAddress(String address) {
this.address = address;
}
/**
* 用户注册时间取值方法
* @return 用户注册时间
*/
publicTimestamp getRegTime() {
returnregTime;
}
/**
* 用户注册时间赋值方法
* @param regTime 用户注册时间
*/
publicvoidsetRegTime(Timestamp regTime) {
this.regTime = regTime;
}
}
packagecom.china.codingmouse.cmsdk4j.example.pojo.test;
importjava.sql.Timestamp;
importcom.china.codingmouse.cmsdk4j.example.pojo.UserPojo;
/**
* 用户信息实体类Equals与HashCode方法测试类
* @author CodingMouse
* @version 1.0.0.1 2009-4-10
*/
publicclassUserPojoEqualsAndHashCodeTest {
/**
* 测试类主方法
* @param args
*/
publicstaticvoidmain(String[] args) {
UserPojo up1 = newUserPojo(3,"邓超",true,25,"四川隆昌",newTimestamp(System.currentTimeMillis()));
UserPojo up2 = newUserPojo(3,"邓超",true,25,"四川隆昌",newTimestamp(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 = newUserPojo(6,"CodingMouse",false,22,"中华人民共和国四川成都",newTimestamp(System.currentTimeMillis()));
UserPojo up4 = newUserPojo(13,"Michael Jackson",false,53,"美利坚合众国纽约市唐人街",newTimestamp(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
反正自我感觉挺良好的,如果您觉得有不妥的地方,还烦请帮忙指出为感!