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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 U#w0E G  
EKN<KnU%  
1;{nU.If  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 k 7@:e$7  
/P46k4M1U  
i|/G!ht^e  
ux6)K= ]  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 MU `!s b*  
0Ny +NE:6M  
d|~'#:y@  
@;{ZnRv14  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 t.O~RE  
7 TM-uA$  
#}~?8/h!  
5 /oW/2"  
  一、常见错误1# :多次拷贝字符串 #u\~AO?h  
rxJl;!7G  
S+mBVk"-~S  
I4UsDs*BD  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 d>#X+;-k  
? IlT[yMw  
h. 4#C}> )  
4R8Qn^  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: Ic&YiATj  
IeA/<'U s  
Ro<5c_k  
J_|%8N{[x  
String s = new String ("Text here"); };Df ><  
7`)RB hGB  
gA1j'!\6l9  
\S?-[v*{  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: 8 K)GH:a  
6e5A8e8"]  
8-kR {9r  
BV/ ^S.~  
String temp = "Text here"; m@L>6;*  
String s = new String (temp); If'N0^'W  
meThjCC  
Z R~2Y?Wt9  
Y=<zR9f`  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: #KHj.Vg  
P+_1*lOG  
"^ dMCS@  
]z=dRq  
String s = "Text here"; B(eiRr3  
T0b/txS  
R@>^t4#_Q0  
JLu$UR4  
  二、常见错误2#: 没有克隆(clone)返回的对象 !Bg^-F:N  
Su +<mW  
NQiu>Sg  
43,*.1;sz  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: el<[Ng[  
.]<gm9l  
x1Gc|K/-  
|A0U 3$S=  
import java.awt.Dimension; 9>&p:+D  
/***Example class.The x and y values should never*be negative.*/ !g|[A7<|  
public class Example{ h{jm  
  private Dimension d = new Dimension (0, 0); W>b\O">  
  public Example (){ } fti0Tz'  
}y(cv}8Y  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ KxFA@3  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ p-!/p#  
   if (height < 0 || width < 0) LCHw.  
    throw new IllegalArgumentException(); Pe11a zJ  
    d.height = height; ]]_c3LJ2`  
     d.width = width; 889^P`Q5  
  } 8LuU2Lo  
Go]y{9+(7  
  public synchronized Dimension getValues(){ I.SMn,N  
   // Ooops! Breaks encapsulation GFnwj<V+{  
   return d; LJ z6)kz  
  } 1NrNTBI@  
} EVLDP\w{  
*rV{(%\m  
R.GDCGAL  
N];K  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: 9Nz}'a;?>  
8`I,KkWg   
(Bpn9}F-V.  
R-4#y%k<  
Example ex = new Example(); <p` F/p-  
Dimension d = ex.getValues(); Dv^M/z2&[  
d.height = -5; -y$<fu9 e  
d.width = -10; lx ~C{tl2  
 2q9$5   
]4lC/ &nm  
{9Q**U`w  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 -xyY6bxL  
nVP|{M  
Udjn.D  
R"z}q (O:  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 ^ZBTd5t#  
UZ:z|a3  
T/hz23nH  
#.,LWL]  
  更好的方式是让getValues()返回拷贝: q+?q[:nR-  
Y%zWaH  
;1r|Bx<5  
}`76yH^c  
public synchronized Dimension getValues(){ \t=#MzjR  
return new Dimension (d.x, d.y); .^ba*qb`{  
} >Wd_?NaI  
G6\`Iy68/v  
S]&aDg1y}  
lXVh`+X/l  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 - Sn]`  
`C^0YGO%  
PT4iy<  
yRp&pUtb  
  三、常见错误3#:不必要的克隆 _0iV6Bj  
3A! |M5  
n6 c+Okj  
$KoGh_h   
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: <?Z]h]C^o  
e%=SgXl2t  
LGYg@DR  
cCG!X%9  
/*** Example class.The value should never * be negative.*/ B,ao%3t  
public class Example{ 6_;n bqY&  
  private Integer i = new Integer (0); b<E78B+Aax  
  public Example (){ } u})8)  
sM9utR  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ nHLMF7\  
  public synchronized void setValues (int x) throws IllegalArgumentException{ xd4~[n\hm  
   if (x < 0) =W gzj|Kr  
    throw new IllegalArgumentException(); 0R-W 9qP  
    i = new Integer (x); 7H,)heA  
  } M~.1:%khM  
W*u$e8i7  
  public synchronized Integer getValue(){ m,rkKhXP  
   // We can’t clone Integers so we makea copy this way. 'W&ewZH_h  
   return new Integer (i.intValue()); A5s;<d0  
  } -x!JTx[K  
} dvAz}3p0]  
^--8 cLB n  
r\C"Fx^  
ey n-bw  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 Fg i;%  
!R[~Z7b6  
@"aqnj>+  
c+##!_[9  
  方法getValue()应该被写为: PJ<9T3Fa  
#w!ewCvt  
*}>)E]O@  
=8Z-ORW51  
public synchronized Integer getValue(){ {_Fh3gjb/  
// ’i’ is immutable, so it is safe to return it instead of a copy. Bf3 QB]9  
return i; @oD2_D2  
} NjO_Y t  
1 q|iw  
!-JvVdM;(  
M'pIAm1p  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: j.\0p-,  
{}H/N   
>H,E3Z  
vm =d?*cR  
  ?Boolean \9R=fA18  
   ?Byte 5>VX]nE3!  
   ?Character 4)p ID`  
   ?Class JOwu_%  
   ?Double ,}l|_GGj  
   ?Float ;Qq7@(2y  
   ?Integer $gCN[%+j  
   ?Long *bzqH2h8  
   ?Short mp*?GeV?M  
   ?String {"|la;*I  
   ?大部分的Exception的子类 _]L]_Bh  
Zlrbd  
D"IxQ2}k  
)OK"H^}f  
 四、常见错误4# :自编代码来拷贝数组 h%sw^;\!  
0y2zjXM;3  
'#jZ`  
!Yz CK*av1  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: Rt@O@oDI  
` ^;J<l  
#9{2aRCJ  
b&RsxW7  
public class Example{ 02-% B~oP  
  private int[] copy; n|B<rx?v  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ |*l^<==  
  public void saveCopy (int[] data){ ~m[Gp;pL  
   copy = new int[data.length]; 1yFIIj:^|  
   for (int i = 0; i < copy.length; ++i) G7r.Jm^q  
    copy = data; b)r;a5"<5  
  } lWBewnLKE  
} LyG`q3@  
lcVG<*gf-  
$v5 >6+-n  
~JP3C5q  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: *] !r T&E  
.fS{j$  
9ZuKED  
CV2#G*  
void saveCopy (int[] data){ gJ>#HEkMB  
  try{ 59~mr:*sF  
   copy = (int[])data.clone(); 4E+8kz'  
  }catch (CloneNotSupportedException e){ o[q|dhrANh  
   // Can’t get here. 8fK/0u^`d  
  } gu&W:FY  
} |\94a  
}]^/`n  
3#eAXIW[  
-vc ,O77z"  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: +x<OyjY5?]  
L^K,YlNBR  
bgkBgugZhX  
3Zwhv+CP[  
static int[] cloneArray (int[] data){ _9?v?mL5;  
  try{ 5f2=`C0_  
   return(int[])data.clone(); }'Ph^ %ox  
  }catch(CloneNotSupportedException e){ OLoo#HW  
   // Can’t get here. p[)yn%uh  
  } ^AERGB\36  
} zjzEmX  
-z%->OUu  
b1%w+*d<z  
[ u ^/3N  
  这样的话,我们的saveCopy看起来就更简洁了: +-|}<mq  
XD80]@\za  
9Q\RCl_1  
n(C M)(ozU  
void saveCopy (int[] data){ ;Eh"]V,e  
  copy = cloneArray ( data); VKg9^%#b`[  
} kYR ^  
b;NVvc(  
fUPYCw6F  
c{qTVi5e  
  五、常见错误5#:拷贝错误的数据 1K'cT\aFm  
"~Zdv}^xS  
md|I?vk  
}vg|05L  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: EYi{~  
</R@)_'  
Qh* }v!3Jo  
5xU}}[|~-  
import java.awt.Dimension; fA=Lb^,M  
/*** Example class. The height and width values should never * be ezri9\Ju  
negative. */ {\|XuCF#  
public class Example{ fuWAw^&  
  static final public int TOTAL_VALUES = 10; vFeR)Ox's  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; Pon0(:#1  
  public Example (){ } ;alt%:$n  
~RZN+N  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ nP|ah~ q  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ JOs kf(  
   if (height < 0 || width < 0) {wO .nOB  
    throw new IllegalArgumentException(); rd"!&i  
    if (d[index] == null) p4kK" \ln  
     d[index] = new Dimension(); 7Q,<h8N\5  
     d[index].height = height; u#Bj#y!  
     d[index].width = width; ]I]G3 e  
  } B~,?Gbl+g  
  public synchronized Dimension[] getValues() /;xrd\du  
   throws CloneNotSupportedException{ +?{LLD*2e  
    return (Dimension[])d.clone(); /AY q^  
  } K <WowU  
} =l6W O*  
"hZ `^ "0b  
9NZq k  
$_e{Zv[  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: rA @|nL{  
9Vt6);cA-]  
jwI1 I{x  
-O?A"  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ <TS ps!(#  
  Dimension[] copy = (Dimension[])d.clone(); A5[kYD,_  
  for (int i = 0; i < copy.length; ++i){ lLK||2d  
   // NOTE: Dimension isn’t cloneable.  Bgai|l  
   if (d != null) OC\cN%qlw  
    copy = new Dimension (d.height, d.width); ^;?w<9Y  
  } P$3!4D[  
  return copy; L3j ~Ooo  
} S(rnVsW%Ki  
B}aW y&D  
T8x/&g''  
0rif,{"  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: > :0N)Pj  
auM1k]  
#W8c)gkG9  
F>E_d<m  
public void store (int[] data) throws CloneNotSupportedException{ brL u~]I  
  this.data = (int[])data.clone(); {nS(B  
  // OK RusiCo!r  
} D>`{f4Y  
w2^s}NO  
C[+?gQJ[9  
aD~S~L!  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: [~;wCW,1  
/Wdrpv-%,1  
,eL&Ner  
J|cw9u  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ Cn.dv-  
  this.data = (int[][])data.clone(); // Not OK! .I>CL4_  
} #;m^DX QZn  
public void rightStore (int[][] data){ $lJ!f  
  // OK! b0tbS[j  
  this.data = (int[][])data.clone(); 7JY9#+?p>  
  for (int i = 0; i < data.length; ++i){ :JXcs39  
   if (data != null) 0|4R8Dh*-  
    this.data = (int[])data.clone(); j9cB<atL  
  } g1B P  
} R80|q#h,]  
QqXaXx;  
PC%_^BDW  
B E#pHg  
ol_&epG;ST  
   六、常见错误6#:检查new 操作的结果是否为null 3;!a'[W&p  
/N@NT/.M<  
mmMiA@0  
Yt r*"-  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: P)ne^_   
-'i[/{  
h[ C XH"  
Aiqb*v$  
Integer i = new Integer (400); M2.*]AL  
if (i == null) 6O@Lx ]t  
throw new NullPointerException(); l 5f'R  
U1kW1L}B  
48!F!v,j)x  
475jmQ{q  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 zD s V"D8  
&d"s cM5  
= @FT$GQ  
u4[JDB7tH  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 #O'g*]j  
YKx+z[A/p  
 七、常见错误7#:用== 替代.equals _ CzAv%  
aecvz0}@R  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: EE qlsH  
q"LT8nD\  
6-nf+!#G  
frWY8&W^H  
int x = 4; g~OG~g@  
int y = 5; uLN.b339  
if (x == y) 4XeO^#  
  System.out.println ("Hi"); 4U[X-AIY&  
// This ’if’ test won’t compile. nH[>Sff$  
if (x.equals (y)) HaOSFltf#  
  System.out.println ("Hi"); Qk^}  
ork{a.1-_w  
:vC+}.{p  
a$LoQ<f_  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 "&mwrjn"T  
Q%t _Epe  
wJ7Fnj>u%  
ASNo6dP 7  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 E7]a#  
#|Je%t}~  
[bN_0T.YI  
<H1e+l{8$  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 V("T9g  
N/E=-&E8  
Ge76/T%{Q  
"(:8 $Fb  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 wee5Nirw6  
b/=>'2f  
M/}i7oS]  
0LP>3"Sm  
  八、常见错误8#: 混淆原子操作和非原子操作 r \} O{ZO  
a$G hb]  
M!\6Fl{ b  
J!zL)u|  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: o1Wf#Zq   
-}Rh+n`  
'gk^NAG2^E  
N&u(9Fxn  
public class Example{ hud'@O"R+  
  private int value; // More code here... p vu% p8  
  public void set (int x){ VO*fC  
   // NOTE: No synchronized keyword yIS&ZtBA  
   this.value = x; ab<7jfFIa  
  } 77G4E ,]  
} Ude)$PAe%  
P;e@<O  
?/KkN3Y_j[  
H"|oI|~  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: ;{g>Z|  
A@w9_qo  
v<?k$ e5  
 PO=A^b  
public void increment (){ 8noo^QO  
  // This is effectively two or three instructions: xllmF)]*Y  
  // 1) Read current setting of ’value’. 75']fFO@!  
  // 2) Increment that setting. ;B"S*wYMN  
  // 3) Write the new setting back. &F +hh{  
  ++this.value; {^K&9sz  
} e73zpF  
HOVzpj  
p2 m`pT  
Wt! NLlN8  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: E%)3{# .z  
1.j;Xo/+:V  
8#a2 kR<b  
$yMNdBI[  
public synchronized void increment (){ ?w@KF%D  
  ++this.value; x]:B3_qR  
} B{Lcx~  
!p4FK]B/u  
P/dT;YhL  
"J3n_3+  
  九、常见错误9#:在catch 块中作清除工作 "ODs.m oq  
RSf*[2  
l' a<k"  
n UD;y}}n  
  一段在catch块中作清除工作的代码如下所示: w;T?m,"  
HQ3kxOT  
*lp{,  
PvS\  
OutputStream os = null; ]N1gzHaS  
try{ D-\z'gS  
  os = new OutputStream (); ,SoqVboRl  
  // Do something with os here. &n& ndq  
  os.close(); 5E\&O%W"  
}catch (Exception e){ ixo?o]Xb`  
  if (os != null) Qx[ nR/  
  os.close(); C.{z+  
} n0=[N'Tw3  
0'^zIL#.  
V?Ye^ -29  
}9(:W</}  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: a(eUdGJ  
hjY)W;  
 =u Ieur  
Pb@9<NXm'  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 bA3pDt).p  
gA:N>w&<X  
Twr<MXa  
~,P."  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 #5W-*?H  
n4Eqm33  
z8n]6FDiE  
=Ev* Q[  
  3.close()可能会抛出异常。 P/hIJV[  
\BxE0GGky  
v8o{3wJ  
%NfbgJcL_  
  上面代码的一个更优版本为: swT/ tesj  
1\BQq  
0%<x>O  
%$I@7Es>  
OutputStream os = null; {afR?3GK  
try{ Qxh 1I?h  
  os = new OutputStream (); iKuSk~  
  // Do something with os here. bZ*J]1y(.  
}finally{ 0=Z_5.T>  
  if (os != null) D<*#. >  
   os.close(); 66l$}+|Zzc  
} xk8P4`;d$  
&+V|Ldh  
vFGFFA/K}N  
kkE1CHY  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 7tr;adjs  
Z'L}x6  
Y;WHjW(K  
O(oGRK<xM  
  十、常见错误10#: 增加不必要的catch 块 ~Fd<d[b?  
4zM$I  
?Wm.'S'to  
SB' $?Kh  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 }J&[Uc  
OTe0[p6v  
Y!|* `FII  
@I^LmB9*  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 <kr%ylhIu  
rwUKg[ 1N  
2,O;<9au<  
Lg[_9 `\  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: h tn?iLq  
]OKs 65  
vo_m$/O  
e8"?Qm7 J  
try{ kmXaLt2Z  
  // Nifty code here { f@k2^  
}catch(Exception e){ s'/ g:aJ  
  throw e; jP9)utEm6  
}finally{ [EETx-  
  // Cleanup code here A12#v,  
} I?mU_^no  
{]w @s7E  
t K+K lz  
Vg)]F+E  
  不必要的catch块被删除后,上面的代码就缩短为: RRGCO+)*  
`_{^&W WS  
3+/{}rv  
T 6g(,xPcL  
try{ O67.DEu^  
  // Nifty code here R8 lBh Ls  
}finally{ 45;{tS.z,B  
  // Cleanup code here CYZx/r<  
} ?=;dNS@i@  
OJL?[<I  
/M;A)z  
MR@*09zP(?  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法  OBCRZ   
4M&6q(389  
M"eiKX  
PKhH0O\_U  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 mG!Rh  
0b0.xz\~U  
:N<ZO`l?  
7Xu.z9y  
  小结 )r#^{{6[v  
r1= :B'z  
]$'w8<D>t,  
1} {bHj  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 ^y,% Tv>  
i-'rS/R  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

您目前还是游客,请 登录注册
如果您在写长篇帖子又不马上发表,建议存为草稿
认证码:
验证问题:
3+5=?,请输入中文答案:八 正确答案:八