📄 对象相等比较深入equals方法-java面向对象 - it电子教育门户 高端java培训.htm
字号:
= new MyTest(10);<BR>
System.out.println(fp.equals(fp1));<BR>
System.out.println(fp1.equals(fp));<BR> }<BR>}<BR>class
MyTest extends FieldPosition{<BR> int x = 10;<BR>
public MyTest(int x){<BR>
super(x);<BR> this.x = x;<BR>
}<BR> public boolean equals(Object
o){<BR> if(o==null) return
false;<BR> if(!(o instanceof MyTest ))
return false;<BR> return ((MyTest)o).x ==
this.x;<BR> }<BR>}</P>
<P>运行一下看看会打印出什么:</P>
<P>System.out.println(fp.equals(fp1));打印true<BR>System.out.println(fp1.equals(fp));打印flase</P>
<P>两个对象,出现了不对称的equals算法.问题出在哪里(脑筋急转弯:当然出在JDK实现的BUG)?</P>
<P>我相信有太多的程序员(除了那些根本不知道实现equals方法的程序员外)在实现equals方法<BR>时都用过instanceof运行符来进行短路优化的,实事求是地说很长一段时间我也这么用过。<BR>太多的教程,文档都给了我们这样的误导。而有些稍有了解的程序员可能知道这样的优化可能<BR>有些不对但找不出问题的关键。另外一种极端是知道这个技术缺陷的骨灰级专家就提议不要这<BR>样应用。</P>
<P>我们知道,"通常"要对两个对象进行比较,那么它们"应该"是同一类型。所以首先利用instanceof<BR>运行符进行短路优化,如果被比较的对象不和当前对象是同一类型则不用比较返回false,但事实<BR>上,"子类是父类的一个实例",所以如果
子类 o instanceof
父类,始终返回true,这时肯定<BR>不会发生短路优化,下面的比较有可能出现多种情况,一种是不能造型成子类而抛出异常,另一种<BR>是父类的private
成员没有被子类继承而不能进行比较,还有就是形成上面这种不对称比较。可能<BR>会出现太多的情况。</P>
<P><BR>那么,是不是就不能用
instanceof运行符来进行优化?答案是否定的,JDK中仍然有很多实现是正<BR>确的,如果一个class是final的,明知它不可能有子类,为什么不用
instanceof来优化呢?</P>
<P>为了维护SUN的开发小组的声誉,我不说明哪个类中,但有一个小组成员在用这个方法优化时在后加<BR>加上了加上了这样的注释:</P>
<P> if (this ==
obj)
// quick
check<BR>
return true;<BR> if
(!(obj instanceof
XXXXClass)) //
(1) same
object?<BR>
return false;<BR>可能是有些疑问,但不知道如何做(不知道为什么没有打电话给我......)</P>
<P>那么对于非final类,如何进行类型的quick check呢?</P>
<P>if(obj.getClass() != XXXClass.class) return false;</P>
<P>用被比较对象的class对象和当前对象的class比较,看起来是没有问题,但是,如果这个类的子类<BR>没有重新实现equals方法,那么子类在比较的时候,obj.getClass()
肯定不等于XXXCalss.class,<BR>也就是子类的equals将无效,所以if(obj.getClass() !=
this.getClass()) return false;才是正<BR>确的比较。<BR></P>
<P><FONT face=Verdana>另外一个quick check是if(this==obj) return
true;</FONT></P><FONT face=Verdana>
<P><BR>是否equals方法一定比较的两个对象就一定是要同一类型?上面我用了"通常",这也是绝大多数程序<BR>员的愿望,但是有些特殊的情况,我们可以进行不同类型的比较,这并不违反规范。但这种特殊情况<BR>是非常罕见的,一个不恰当的例子是,Integer类的equals可以和Sort做比较,比较它们的value是不<BR>是同一数学值。(事实上JDK的API中并没有这样做,所以我才说是不恰当的例子)</P>
<P>在完成quick
check以后,我们就要真正实现你认为的“相等”。对于如果实现对象相等,没有太高<BR>的要求,比如你自己实现的“人”类,你可以认为只要name相同即认为它们是相等的,其它的sex,<BR>ago都可以不考虑。这是不完全实现,但是如果是完全实现,即要求所有的属性都是相同的,那么如<BR>何实现equals方法?</P>
<P>class Human{<BR> private String name;<BR> private
int ago;<BR> private String
sex;<BR>
....................<BR>
public boolean equals(Object obj){<BR> quick
check.......<BR> Human other = (Human)ojb;<BR>
return this.name.equals(other.name) <BR>
&& this.ago == ohter.ago<BR> &&
this.sex.equals(other.sex);<BR> }<BR>}</P>
<P>这是一个完全实现,但是,有时equals实现是在父类中实现,而要求被子类继承后equals能正确的工<BR>作,这时你并不事实知道子类到底扩展了哪些属性,所以用上面的方法无法使equals得到完全实现。<BR>一个好的方法是利用反射来对equals进行完全实现:</P>
<P> public boolean
equals(Object obj){<BR> quick check.......<BR>
Class c = this.getClass();<BR> Filed[] fds =
c.getDeclaredFields();<BR> for(Filed
f:fds){<BR>
if(!f.get(this).equals(f.get(obj)))<BR>
return false;<BR> }<BR> return
true;<BR> }<BR>为了说明的方便,上明的实现省略了异常,这样的实现放在父类中,可以保证你的子类的equals可以按<BR>你的愿望正确地工作。</P>
<P>关于equals方法的最后一点是:如果你要是自己重写(正确说应该是履盖)了equals方法,那同时就一<BR>定要重写hashCode().为是规范,否则.............<BR>我们还是看一下这个例子:</P>
<P>public final class PhoneNumber {<BR>
private final int areaCode;<BR> private
final int exchange;<BR> private final int
extension;</P>
<P> public PhoneNumber(int areaCode, int
exchange, int extension)
{<BR>
rangeCheck(areaCode, 999, "area
code");<BR>
rangeCheck(exchange, 99999999,
"exchange");<BR>
rangeCheck(extension, 9999,
"extension");<BR>
this.areaCode =
areaCode;<BR>
this.exchange =
exchange;<BR>
this.extension = extension;<BR> }</P>
<P> private static void rangeCheck(int arg,
int max, String name)
{<BR> if(arg < 0
|| arg >
max)<BR>
throw new IllegalArgumentException(name + ": " +
arg);<BR> }</P>
<P> public boolean equals(Object o)
{<BR> if(o ==
this)<BR>
return true;<BR>
if(!(o instanceof
PhoneNumber))<BR>
return false;<BR>
PhoneNumber pn =
(PhoneNumber)o;<BR>
return pn.extension == extension && pn.exchange ==
exchange && pn.areaCode ==
areaCode;<BR> }<BR>}</P>
<P>注意这个类是final的,所以这个equals实现没有什么问题。</P>
<P>我们来测试一下:</P>
<P> public static void main(String[] args)
{<BR> Map hm = new
HashMap();<BR>
PhoneNumber pn = new PhoneNumber(123, 38942,
230);<BR> hm.put(pn,
"I love you");<BR>
PhoneNumber pn1 = new PhoneNumber(123, 38942,
230);<BR>
System.out.println(pn);<BR>
System.out.println("pn.equals(pn1) is " +
pn.equals(pn1));<BR>
System.out.println(hm.get(pn1));<BR>
System.out.println(hm.get(pn));<BR>
}<BR>既然pn.equals(pn1),那么我put(pn,"I love
you");后,get(pn1)这什么是null呢?<BR>答案是因为它们的hashCode不一样,而hashMap就是以hashCode为主键的。</P>
<P>所以规范要求,如果两个对象进行equals比较时如果返回true,那么它们的hashcode要求返回相等的值。</FONT></P></FONT></DIV></SPAN></TD></TR>
<TR>
<TD width="71%"> </TD>
<TD width="29%">【 <A
href="http://www.mldn.cn/print.jtml?articleid=1283">打印</A> 】【
<A
href="http://www.mldn.cn/member/favlist.jtml?action=add&postid=1283">收藏</A>
】【 <A href="http://www.mldn.cn/email.jtml?articleid=1283"
target=_blank>推荐</A> 】 </TD></TR>
<TR>
<TD colSpan=2>
<TABLE cellSpacing=3 cellPadding=0 width="100%" align=center
bgColor=#ffffff border=0>
<TBODY>
<TR height=25>
<TD align=middle width="33%"><IMG height=95 alt=java视频教程
src="对象相等比较深入equals方法-JAVA面向对象 - IT电子教育门户 高端JAVA培训.files/javavideo.gif"
width=642></TD></TR>
<TR>
<TD class=content-table vAlign=top align=middle>
<TABLE>
<TBODY>
<TR>
<TD>
<TABLE>
<TBODY>
<TR>
<TD><A
href="http://www.mldn.cn/articleview/2007-8-21/article_view_2251.htm"
rel=external><IMG class=midImg alt=struts2.0入门视频
src="对象相等比较深入equals方法-JAVA面向对象 - IT电子教育门户 高端JAVA培训.files/struts2.gif"
width=90></A></TD></TR>
<TR>
<TD style="HEIGHT: 22px"> <A
title=struts2.0入门视频
href="http://www.mldn.cn/articleview/2007-8-21/article_view_2251.htm"
rel=external>struts2.0入门视频</A></TD></TR></TBODY></TABLE></TD>
<TD>
<TABLE>
<TBODY>
<TR>
<TD><A
href="http://www.mldn.cn/articleview/2007-6-15/article_view_2141.htm"
rel=external><IMG class=midImg
alt=JAVAEE学习流程和学习方法
src="对象相等比较深入equals方法-JAVA面向对象 - IT电子教育门户 高端JAVA培训.files/j2eejc.gif"
width=90></A></TD></TR>
<TR>
<TD style="HEIGHT: 22px"> <A
title=JAVAEE学习流程和学习方法
href="http://www.mldn.cn/articleview/2007-6-15/article_view_2141.htm"
rel=external>JAVAEE学习流程和学习方..</A></TD></TR></TBODY></TABLE></TD>
<TD>
<TABLE>
<TBODY>
<TR>
<TD><A
href="http://www.mldn.cn/articleview/2007-6-5/article_view_2091.htm"
rel=external><IMG class=midImg
alt=1-Java介绍及JDK配置
src="对象相等比较深入equals方法-JAVA面向对象 - IT电子教育门户 高端JAVA培训.files/javase.gif"
width=90></A></TD></TR>
<TR>
<TD style="HEIGHT: 22px"> <A
title=1-Java介绍及JDK配置
href="http://www.mldn.cn/articleview/2007-6-5/article_view_2091.htm"
rel=external>1-Java介绍及JDK配置..</A></TD></TR></TBODY></TABLE></TD>
<TD>
<TABLE>
<TBODY>
<TR>
<TD><A
href="http://www.mldn.cn/articleview/2007-4-19/article_view_2012.htm"
rel=external><IMG class=midImg alt=Oracle中的多表连接
src="对象相等比较深入equals方法-JAVA面向对象 - IT电子教育门户 高端JAVA培训.files/Oracle.gif"
width=90></A></TD></TR>
<TR>
<TD style="HEIGHT: 22px"> <A
title=Oracle中的多表连接
href="http://www.mldn.cn/articleview/2007-4-19/article_view_2012.htm"
rel=external>Oracle中的多表连接</A></TD></TR></TBODY></TABLE></TD>
<TD>
<TABLE>
<TBODY>
<TR>
<TD><A
href="http://www.mldn.cn/articleview/2007-4-11/article_view_1978.htm"
rel=external><IMG class=midImg
alt=Struts中logic标签的使用
src="对象相等比较深入equals方法-JAVA面向对象 - IT电子教育门户 高端JAVA培训.files/struts.gif"
width=90></A></TD></TR>
<TR>
<TD style="HEIGHT: 22px"> <A
title=Struts中logic标签的使用
href="http://www.mldn.cn/articleview/2007-4-11/article_view_1978.htm"
rel=external>Struts中logic标签..</A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD colSpan=2>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -