社区应用 最新帖子 精华区 社区服务 会员列表 统计排行 社区论坛任务 迷你宠物
  • 3472阅读
  • 0回复

审查Java代码的十一种常见错误

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 '-~~-}= sJ  
$%#!bV  
#)O6 5GI  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 V U3upy<  
S2&4g/  
@# l= l  
VpDbHAg  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 BQMpHSJ_  
T(Eugl"  
?Z/V~,  
2^ nxoye  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 /y}xX  
'5$b-x6F  
)Ql%r?(F+  
%>{0yEC  
  一、常见错误1# :多次拷贝字符串 @:#eb1 <S  
lt8|9"9<  
)+DmOsH  
kt:! 7  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 #@Jq~$N|  
s2'h  
}o`76rDN  
3 Za}b|  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: `b$.%S8uj=  
m9rp8r*e  
SwMc pNo  
/gP+N2o+}  
String s = new String ("Text here"); vr =#3>  
"4{r6[dn  
S"H2 7  
vEJbA  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: %EB/b  
cbTm'}R(G  
a&? :P1$  
N(yz k_~  
String temp = "Text here"; Y}wyw8g/  
String s = new String (temp); C,4e"yynb  
Cw&KVw*  
.Z *'d  
utV_W&  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: Z,PPu&lmE/  
tKXIk9e  
u>vL/nI  
vnuN6M{  
String s = "Text here"; JB<t6+"rD  
CU!Dhm/U  
tQ#n${a@f  
#Gi$DMW  
  二、常见错误2#: 没有克隆(clone)返回的对象 do'GlU oMC  
< =IFcN  
4#Jg9o   
oQJtUP%  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: =Dj#gV  
Tw<q,O  
[dz _R  
MF'JeM;H  
import java.awt.Dimension; N?8!3&TiV  
/***Example class.The x and y values should never*be negative.*/ .T`%tJ-Em  
public class Example{ /PKNLK  
  private Dimension d = new Dimension (0, 0); ) )Za&S*<  
  public Example (){ } Kc\fu3Q  
}x ,S%M-  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ \Vk:93OH21  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ ;n;p@Uu[ b  
   if (height < 0 || width < 0) );YDtGip J  
    throw new IllegalArgumentException(); +b 6v!7_  
    d.height = height; 2~[juWbz  
     d.width = width; +kD R.E:  
  } (cO:`W6.  
7d\QB (~  
  public synchronized Dimension getValues(){ noj0F::m`j  
   // Ooops! Breaks encapsulation nJ;.Td  
   return d; e|r`/:M  
  } # f\rt   
} EdX$(scu~B  
0n'_{\yz  
 ~$J2g  
-G=]=f/'  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: yt2PU_),  
J[kTlHMD  
CvdN"k  
/mZE/>&~ ,  
Example ex = new Example(); 2Khv>#l  
Dimension d = ex.getValues(); !<h)w#>en  
d.height = -5; s#GLJl\E_P  
d.width = -10; 7|H$ /]  
G+m }MOQP7  
xYB{;K  
E*lxVua  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 K)P%;X  
ji= "DYtL  
QsW/X0YBv  
"Y.y:Vv;  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 ajpX L  
,fRq5"?  
#LNED)Vg  
KY^Z  
  更好的方式是让getValues()返回拷贝: *K; ~!P  
D43z9z-:L  
" Yy n/  
fB,_9K5i  
public synchronized Dimension getValues(){ Q59W#e)  
return new Dimension (d.x, d.y); _ x*3PE  
} !_(Tqyg&  
fXB0j;A  
Qwc"[N4H  
fc@A0Hf  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 4GM6)"#d  
DV{=n C  
,u!sjx  
:Tq~8!s  
  三、常见错误3#:不必要的克隆 I}Q2Vu<  
~)'k 9?0  
DTs;{c  
c`Wa^(  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: [Nq*BrzF  
|cY`x(?yP  
&.ACd+Cd  
\j.:3X r  
/*** Example class.The value should never * be negative.*/ {XHh8_ ^&  
public class Example{ h7@6T+#WoT  
  private Integer i = new Integer (0); ]%(2hY~i  
  public Example (){ } I 2DpRMy  
H[|~/0?K  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ -Qe Z#w|  
  public synchronized void setValues (int x) throws IllegalArgumentException{ /7LR;>Bj  
   if (x < 0) 'ig'cRD6N  
    throw new IllegalArgumentException(); |&jXp%4T  
    i = new Integer (x);  C.QO#b  
  } U<-D(J  
"I TIhnE  
  public synchronized Integer getValue(){ )CyS#j#=  
   // We can’t clone Integers so we makea copy this way. GJUL$9  
   return new Integer (i.intValue()); ZG@q`<:j  
  } ?hM64jI|  
} h1(4Ic  
Qei" '~1a  
]N[ 5q=A5  
c"xK`%e  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 |D.ND%K&  
c:g'.'/*  
u-C)v*#L  
'3H_wd  
  方法getValue()应该被写为: QdC<Sk!G  
vcd\GN*4f  
2px|_)i  
PxE3K-S)G  
public synchronized Integer getValue(){ 4Xv*wB1  
// ’i’ is immutable, so it is safe to return it instead of a copy. hpJ-r  
return i; &"q=5e2  
} ]+$?u&0?w  
'%`:+]!  
l lsfTrp  
snikn&  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: 'P}0FktP`  
9)=ctoZ'  
]0\MmAJRn  
8KNZ](Dj  
  ?Boolean A+)`ZTuO  
   ?Byte a@*\o+Su  
   ?Character as_PoCoss  
   ?Class C'X!\}f.b/  
   ?Double ;({W#Wa  
   ?Float I!?}jo3  
   ?Integer S Z$Kz n  
   ?Long PiYxk+N  
   ?Short 7\q~%lDE  
   ?String 8&aq/4:q0  
   ?大部分的Exception的子类 \'bzt"f$j  
(!N|Kl  
m9Hit8f@Q  
ia 73?*mXT  
 四、常见错误4# :自编代码来拷贝数组 >e"#'K0?\  
mdg i5v  
VM,]X.  
"FKOaQ%IH  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: {Dmjm{   
uR r o?m<  
Ue~CwFOc  
UZsH9 o  
public class Example{ ^ovR7+V  
  private int[] copy; \K!VNB>h  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ 8pgEix/M5o  
  public void saveCopy (int[] data){ f`=-US  
   copy = new int[data.length]; ox (%5c)b|  
   for (int i = 0; i < copy.length; ++i) d{7 +w/Zi  
    copy = data; <3hRyG@vB  
  } N' `A?&2ru  
} )%@J=&G8TT  
qm o9G  
]wG{!0pl  
*ppffz  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: Tyf`j,=  
%F4%H|G  
'ub@]ru|  
A=wh@"2  
void saveCopy (int[] data){ ';=O 0)u  
  try{ %Qdn  
   copy = (int[])data.clone(); Ak"m 85B  
  }catch (CloneNotSupportedException e){ ;x@~A^<el  
   // Can’t get here. [ ~&/s:Vvo  
  } exUu7& *:  
}  O+Y6N  
b?QoS|<e?  
lv+TD!b   
Y#P%6Fy  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: 8C9-_Ng`  
VZmLS 4E  
(rm?jDm   
l&Q`wR5e  
static int[] cloneArray (int[] data){ -0x #  
  try{ oEKvl3Hz_  
   return(int[])data.clone(); wyO4Y  
  }catch(CloneNotSupportedException e){ xRLT=.ir  
   // Can’t get here. 'Xq| Kf (  
  } 'op|B@y  
} 8i#2d1O  
O5nD+qTQ#  
^N{h3b8  
'XP7" N47O  
  这样的话,我们的saveCopy看起来就更简洁了: *kDCliL  
A1>OY^p3%  
hAnPXiD  
~^fZx5  
void saveCopy (int[] data){ My[pr_xg  
  copy = cloneArray ( data); zFff`]^`  
} c>:wd@w  
62o:,IcoG  
k],Q9  
Q%tXQP.r  
  五、常见错误5#:拷贝错误的数据 0 e ~JMUb  
:Cs4NF   
7XLtN "$$  
k(7&N0V%zz  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: 9FYUo  
YdC6k?tzS  
ZWU)\}}_R  
=O_4|7Zl  
import java.awt.Dimension; /quc}"__  
/*** Example class. The height and width values should never * be 1.{z3_S21:  
negative. */ [KaAXv .X  
public class Example{ $f=J2&D,Cz  
  static final public int TOTAL_VALUES = 10; `FDiX7M  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; Tc &z:  
  public Example (){ } A,hJIe  
j2.|ln"!  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ p Vw}g@<M  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ +tIF h'  
   if (height < 0 || width < 0) L<-_1!wh  
    throw new IllegalArgumentException(); 2E/"hQw  
    if (d[index] == null) a /l)qB#  
     d[index] = new Dimension(); 1AfnzGvA  
     d[index].height = height; |[ k.ii6iO  
     d[index].width = width; xU>WEm2  
  } [{<`o5qR  
  public synchronized Dimension[] getValues() 5Y'qaIFR  
   throws CloneNotSupportedException{ ?o4C;  
    return (Dimension[])d.clone(); 9_rYBX  
  } wb5baY9  
} w?k>:,'[  
zrL$]Oy}x  
K&ZtRRDd  
mPN@{.(j  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: &5spTMw8  
(*nT(Adk  
EZy)A$|  
3<Zq ]jk?n  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ ]&xk30  
  Dimension[] copy = (Dimension[])d.clone(); EQyC1j  
  for (int i = 0; i < copy.length; ++i){ { MSkHf=  
   // NOTE: Dimension isn’t cloneable. 4Lh!8g=/  
   if (d != null) j4qR(p(vC  
    copy = new Dimension (d.height, d.width); YpZ+n*&+  
  } F2dHH^  
  return copy; ^ft>@=K(|  
} U;V7 u/{  
uK#4(eY=W  
*1 ]uH e  
!M]uL&:  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: 1i"WDu*h3  
>#;.n(y  
{3p4:*}  
`d +Da=L  
public void store (int[] data) throws CloneNotSupportedException{ ?m=N]!n  
  this.data = (int[])data.clone(); ./k/KSR  
  // OK dh*ZKI^@(  
} 5C*Pd Wpl  
yAs> {6%-  
VT%NO'0  
='sHj4hU  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: Ic:(Gi- %  
+L| ?~p`V  
WZejp}x  
bv9]\qC]T<  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ C'@i/+  
  this.data = (int[][])data.clone(); // Not OK! N/'b$m5= S  
} } FlT%>Gw  
public void rightStore (int[][] data){ i2A81>68<  
  // OK! kr5">"7  
  this.data = (int[][])data.clone(); *74MWF@IY  
  for (int i = 0; i < data.length; ++i){ qu6D 5t  
   if (data != null) N1S{suic  
    this.data = (int[])data.clone(); (KD RkE|=  
  } &yTqZ*Yuk  
} <8i//HOE  
S6M}WR^,  
<X^@*79m  
K?Nhi^f"L  
|`,2ri*5A  
   六、常见错误6#:检查new 操作的结果是否为null \*y-g@-{W$  
7P5)Z-K[  
_LUhZlw  
b:m88AG  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: Y>T-af49  
wY % }  
LTCb@L{^i  
fS:&Ak ];  
Integer i = new Integer (400); S9D<8j^  
if (i == null) YQ)kRhFA  
throw new NullPointerException(); AW'0,b`v  
_QE qk@ql  
=tn)}Y.<e  
Jbg/0|1  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 ,vawzq[oSy  
ww3-^v  
NC`aP0S  
vjCu4+w($Z  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 w\u=)3qyVV  
O`\;e>!t  
 七、常见错误7#:用== 替代.equals EhvX)s  
(vnAbR#e  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: CL;}IBd a  
"ycJ:Xv49  
qLG&WB  
 -p-ZzgQ  
int x = 4; h0f;F@I  
int y = 5; o4,6.1}  
if (x == y) M `M5'f  
  System.out.println ("Hi"); FUb\e-Q=  
// This ’if’ test won’t compile. D%SlAzZ3  
if (x.equals (y)) 0<@KG8@hI;  
  System.out.println ("Hi"); n\'4  
xn(kKB.  
(gU!=F?#m  
T2W eE@o  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 |F[+k e  
hH 3RP{'=  
rfg'G&A(  
!hy-L_wL]  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 {duz\k2  
,PW'#U:  
7U"g3 a)=  
mdDOvm:&  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 783,s_  
o[w:1q7  
C2I_%nU Z1  
eJ-xsH*8  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 ]:-mbgW  
& h)yro  
hWLA<wdb  
ZvX*t)VjTz  
  八、常见错误8#: 混淆原子操作和非原子操作 8/cX]J  
i9|Sa6vuI  
BwpqNQN  
cwlRQzQ(  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: RSRS wkC  
ltSU fI  
d&Zpkbh"  
o5PO =AN  
public class Example{ X`K<>0.N  
  private int value; // More code here... :eCwY  
  public void set (int x){ !9e=_mY  
   // NOTE: No synchronized keyword J*A,o~U|  
   this.value = x; };zFJ6I8  
  } Wvh#:Z  
} n( yn<  
khxnlry  
+Kc  
v{4$D~I  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: ;igIZ$&  
H%td hu\e  
Z5n1@a __  
9.-S(ZO  
public void increment (){ 4p F*"B  
  // This is effectively two or three instructions: 1CZgb   
  // 1) Read current setting of ’value’. 9cF[seE"0  
  // 2) Increment that setting. q54]1TQ  
  // 3) Write the new setting back. cV6D<,)  
  ++this.value; tcI*a>  
} LH% F 8  
CAhXQ7w'Z  
2%m BK  
q]-r@yF  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: ouQ T  
p6V0`5@t  
g3y~bf  
g! |kp?  
public synchronized void increment (){ 0{D'n@veP  
  ++this.value; rb.N~  
} kTgEd]^&D  
_`T_">9r  
ZY+qA  
b4kgFA  
  九、常见错误9#:在catch 块中作清除工作 I\ob7X'Xu!  
{EQOP]  
W${Ue#w77  
)ez9"# MH'  
  一段在catch块中作清除工作的代码如下所示: <bWG!ZG  
PJH&  
/U9"wvg  
#"~<HG}bR/  
OutputStream os = null; Fx.=#bVX7  
try{ +mn[5Y}:  
  os = new OutputStream (); UaeXY+O  
  // Do something with os here. "chDg(jMZ  
  os.close(); ~9@UjQ^)F  
}catch (Exception e){ S,he6zS  
  if (os != null) hwv/AnX~O  
  os.close(); 5$k:t  
} rey!{3U  
? =Kduef  
G 3ptx! D  
JWxwJex  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: R6->t #n,  
 @q) d  
P*j|.63  
OneY_<*a<  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 |sE'XT4ag  
|A(Iti{v  
+N U G  
Nh44]*  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 K[YyBE id  
!4+<<(B=E  
>A"(KSNL  
OjA,]Gv6  
  3.close()可能会抛出异常。 xAm6BB c  
@6-jgw>W2  
[$UI8tV  
&rR2,3r=  
  上面代码的一个更优版本为: @H8EWTZ  
v3>UV8c'  
vl)l'  
OY({.uVdX  
OutputStream os = null; j?QDR  
try{ owv[M6lbD  
  os = new OutputStream (); F!K>Kz  
  // Do something with os here. e*1_8I#2  
}finally{ COlaD"Y  
  if (os != null) S+lqA-:  
   os.close(); uyx 2;f  
} #g!.T g'  
F:DrX_O%  
`Q,H|hp;k;  
7{Wny&[0  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 f*8DCh!r"  
-UT}/:a  
e+K^A q  
&%Tj/Qx  
  十、常见错误10#: 增加不必要的catch 块 Etm?'  
PPsE${!  
Z3!`J&  
T51 `oZ`  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 tq?!-x+>  
kG*~ |ma  
kU`r)=1"  
6W Ur QFK  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 =tY T8Q;al  
#.[k=dj   
? =+WRjF  
syK^<xa  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: Z/J y'$x  
k VQ\1!  
nQS|Lt_+  
rVsJ`+L  
try{ Z%\,w(o[h  
  // Nifty code here vr^qWn  
}catch(Exception e){ bN@ l?w  
  throw e; BsJC0I(  
}finally{ H>@+om  
  // Cleanup code here ;bhT@aB1  
} xkR0  
v*yuE5{  
7VI*N)OZ8  
{]|J5Dgfe  
  不必要的catch块被删除后,上面的代码就缩短为: f y8Uk;  
j <RrLn_  
FC"8#*x  
jF*j0PkNdb  
try{ & ZB  
  // Nifty code here ^sg,\zD 'X  
}finally{ 7"xd1l?zz  
  // Cleanup code here =mmWl9'mJ  
} @xZR9Z8]L  
xn|(9#1o  
M& CqSd  
<b<j=_3  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 76` .Y  
(_{y B[z>`  
.3!1`L3  
da~],MN  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 2VCI 1E  
W+1^4::+  
R_xRp&5  
Nl1D o:PY  
  小结 Vs{|xG7W D  
O<W_fx8_'  
@AuO`I@p=  
pcI uN  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 xi; `ecqS<  
ymcLFRu,  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

您目前还是游客,请 登录注册
如果您提交过一次失败了,可以用”恢复数据”来恢复帖子内容
认证码:
验证问题:
10+5=?,请输入中文答案:十五