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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 bMm3F%FFq&  
fPqr6OYz  
^:KO_{3E  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 ab.tH$:<  
F}Srn;V  
X(Qu{HhI  
$ 4m*kQ  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 $SY]fNJQ  
I4t*?  
TTZe$>f  
B{MaMf)  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 V'pqxjfd  
</[: 9Cl  
qf#)lyr<D6  
poT&-Ic[  
  一、常见错误1# :多次拷贝字符串 (=u'sn:s  
2eb1 lJdS  
3<:jx~y>  
!L$x:/R9M  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 ?X9U TOx  
8e&p\%1  
Kz?#C  
s{}]D{bc  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: eE(b4RCM  
skg|>R,kE  
fCw*$:O  
;11x"S  
String s = new String ("Text here"); /7"I#U^u/  
[k<1`z3  
ezm&]F`  
n3KI+I%nQ  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: (xpn`NA  
*O~e T  
= QO g 6  
5(m(xo6  
String temp = "Text here"; "ju'UOcS/  
String s = new String (temp); iE].&>w  
kmPYx)o  
646JDX[o  
vB'>[jvA|  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: 6%Mt  
pG3k   
g>JLDQdc  
;i<jhNA  
String s = "Text here"; ";SiL{Z  
o\VUD  
(s<s@`  
N2C7[z+l`  
  二、常见错误2#: 没有克隆(clone)返回的对象 $IQw=w7 p  
U/ od~29  
<0P5 o|  
8\.b4FNJ  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: }UwO<#  
tc+WWDP#"  
I\O\,yPhhP  
a_~=#]a  
import java.awt.Dimension; \ 0W!4D  
/***Example class.The x and y values should never*be negative.*/ zUJZ`seF  
public class Example{ c9"r6j2m5  
  private Dimension d = new Dimension (0, 0); ;&b.T}Nf06  
  public Example (){ } aB~S?.l  
C1kYl0 zR[  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ ]=pR  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ /YAJbr  
   if (height < 0 || width < 0) u\yVR$pQ  
    throw new IllegalArgumentException(); w;6bD'.>;  
    d.height = height; ]6r;}1c  
     d.width = width; zi9[)YqxPH  
  } w"Y` ]2  
4GdX/6C.  
  public synchronized Dimension getValues(){ 58Xzup_"  
   // Ooops! Breaks encapsulation NoE*/!Sr  
   return d; ^TY ;Zp  
  } "Jq8?FoT  
} B;>{0 s  
K<`osdp=&  
: 18KR*;p  
!9Z r;K~\  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码:  {^a36i  
\NZIEu)5?  
Yb3mP!3q8Z  
pKiZ)3U  
Example ex = new Example(); N["W I r  
Dimension d = ex.getValues(); nAIo{ F  
d.height = -5; *g}Yw  
d.width = -10; YHkcWz  
GPz(j'jU  
JF&$t}  
9I27TKy  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 i 9<pqQ  
Q_-_^J  
JxE53ev  
y$FW$Ka  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 fWfk[(M'9  
2WX7nK;I  
TxjYrzC  
nRL. ppUI  
  更好的方式是让getValues()返回拷贝: 6tHO!`}1  
'm1N/)F  
B~]5$-  
R'EUV0KX>Y  
public synchronized Dimension getValues(){ 7w,FX.=;cv  
return new Dimension (d.x, d.y); VVH.2&`I  
} Unj.f>U  
00v&lQBW  
]^':Bmq  
F'jWV5"*  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 ]H-S, lmV  
]D[DU]K  
gb ^?l~SS  
$3`>{3x$  
  三、常见错误3#:不必要的克隆 ;<yd^Xs  
{~!q`Dr3?q  
@1.QEyXG  
?0? R  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: Q_* "SRz  
7S/G B  
HEA#bd\  
\^ghdU  
/*** Example class.The value should never * be negative.*/ ]8q3>  
public class Example{ JlMT<;7\  
  private Integer i = new Integer (0); kB?al#`  
  public Example (){ } ]f+ csB  
5` Te \H  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ I2nF-JzD2a  
  public synchronized void setValues (int x) throws IllegalArgumentException{ g?-lk5  
   if (x < 0) |f~@8|MQP+  
    throw new IllegalArgumentException(); 3)-/`iy#  
    i = new Integer (x); j83p)ido  
  } u6>?AW1~  
G!K]W:m  
  public synchronized Integer getValue(){ hX `}Q4(k  
   // We can’t clone Integers so we makea copy this way. )* 4fzo  
   return new Integer (i.intValue()); dJT]/g  
  } |D, +P  
} @d Jr/6Yx  
a=M\MZK>  
;"(foY"L  
fRg`UI4w}  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 I%- " |]$  
t]7&\ihZi~  
n6s}ww)  
2M# r]  
  方法getValue()应该被写为: 3nZo{p:E  
,%\o4Rc'o  
D8 hr?:I9  
!rqF}d  
public synchronized Integer getValue(){ W6On9 3sa  
// ’i’ is immutable, so it is safe to return it instead of a copy. 9Xx's%U  
return i; Cvn#=6V3  
} ()~pY!)1/  
yAoe51h?  
LpR3BP@At  
| WvUq  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: w)Covz'uf  
Q}-~O1  
dtpoU&?6s  
=[F<7pvE  
  ?Boolean d&Ef"H  
   ?Byte (SKVuR%Jj  
   ?Character aN"DkUYZM  
   ?Class H$I =W>;  
   ?Double L!=QR8?@E  
   ?Float ]T%rjsN  
   ?Integer 6Cn+e.j@  
   ?Long 5nsq[Q`  
   ?Short ]Dw]p! @  
   ?String ) $PDo 7#  
   ?大部分的Exception的子类 lP<:tR~K  
y@\V +  
Yo[;W vu  
7)s^8+  
 四、常见错误4# :自编代码来拷贝数组 "~D]E7Q3y  
E9;|'Vy<E  
(\SA *.)  
_q~=~nub  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: ANgw"&&>(  
9<KAXr#  
1Tu *79A  
.'Vww  
public class Example{ 8']9$#  
  private int[] copy; s8}@=]aA  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ #5V9o KM  
  public void saveCopy (int[] data){ I'|$}/\`  
   copy = new int[data.length]; hZ.Z3`v70  
   for (int i = 0; i < copy.length; ++i) L:FoSCN Y(  
    copy = data; 'nF2aD%A  
  } vd8{c7g:n  
} 0}b tXh  
^<e.]F25M  
rwGKfoKI  
YCP) %}  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: z<yU-m2h  
q5?# 3T=  
JU4q zi  
^k]XEW{PG  
void saveCopy (int[] data){ *hw\35%P`?  
  try{ 2$Tj84'X  
   copy = (int[])data.clone(); #5f-`~^C{  
  }catch (CloneNotSupportedException e){ M@5?ZZ4L  
   // Can’t get here. f"<O0Qw  
  } xP[n  
} /n>qCuw  
^k9kJ+x^S2  
K"r*M.P>  
X-wf:h?i  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: 8O38# {[S  
&uO%_6J  
x@*SEa  
-]QD|w3dp  
static int[] cloneArray (int[] data){ HaP}Y :p  
  try{ W VI{oso#  
   return(int[])data.clone(); -?0qf,W.  
  }catch(CloneNotSupportedException e){ bua+I;b  
   // Can’t get here. gM _hi  
  } ]wtb-PC  
} QDu2?EYZq  
o#skR4lwe  
Rb.SY{}C  
uXKERzg  
  这样的话,我们的saveCopy看起来就更简洁了: Ry'= ke  
_ A=$oVe  
~m$Y$,uH  
={z*akn,  
void saveCopy (int[] data){ -:na: Vsi  
  copy = cloneArray ( data); mFi&YpH u3  
} %T~ig[GstX  
v&=gF/$  
o|$AyS{1  
@~%r5pz6  
  五、常见错误5#:拷贝错误的数据 kOed ]>H  
"T|PS 6R~  
9o+)?1\  
QDhOhGK  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: JhLgCnm  
AT%u%cE-  
07[_.i.l  
o}$ EG  
import java.awt.Dimension; 2* 2wY=  
/*** Example class. The height and width values should never * be }yz (xH  
negative. */ Jl&-,Vjb  
public class Example{ %oO4|JkJX  
  static final public int TOTAL_VALUES = 10; 2n|K5FR()  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; !Ze5)g%H  
  public Example (){ } 4 XAQVq5  
sashzVwJ-=  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ NB8/g0:=n&  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ 1A\OC  
   if (height < 0 || width < 0) H(Z88.OM  
    throw new IllegalArgumentException(); MerFZd 1  
    if (d[index] == null) Gy6l<:;  
     d[index] = new Dimension(); } x2DT8u  
     d[index].height = height; fc |GArL#}  
     d[index].width = width; @CT;g\4  
  } c|#8T*`C  
  public synchronized Dimension[] getValues() RE/~#k@a  
   throws CloneNotSupportedException{ 5Er2}KZJv,  
    return (Dimension[])d.clone(); *^:N.&]  
  } \Z+z?K O  
} #3+!ee27#  
TL}++e 7+  
(G[ *|6m  
)3>hhuaa  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: {qN 5MsY  
%'X[^W  
D"a~ #^  
|v({-*7  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ /!3@]xz*  
  Dimension[] copy = (Dimension[])d.clone(); PEW=@xj2y  
  for (int i = 0; i < copy.length; ++i){ 'LE =6{#  
   // NOTE: Dimension isn’t cloneable. \Hn>oonph  
   if (d != null)  [D<1 CF  
    copy = new Dimension (d.height, d.width); C,NJb+J  
  } /J WGifH  
  return copy; ybY]e; v*O  
} ZOZ+Y\uU  
eep1I :N  
;n&t>pBM  
OHhsP}/  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: +Zaj,oEE  
`1bv@yzq  
!Rhl f.x  
i}B2R$Z3  
public void store (int[] data) throws CloneNotSupportedException{ >kW@~WDMu  
  this.data = (int[])data.clone(); oz}+T(@O  
  // OK U G~ba  
} +,#$:fs u  
v%iof1 T'  
k\NMy#]Zt  
'*"vkgN  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: NnT1X;0W  
*1fb}C_  
% a@>_  
w%JTTru  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ e,Uo#T6J  
  this.data = (int[][])data.clone(); // Not OK! =5(>q5Z*  
} $w);5o  
public void rightStore (int[][] data){ {M^3m5.^  
  // OK! _s .G  
  this.data = (int[][])data.clone(); F*3j.lI  
  for (int i = 0; i < data.length; ++i){ p(/dBt[3k  
   if (data != null) JYW)uJ  
    this.data = (int[])data.clone(); .K p  
  } >8qQK r\"  
} @ CZ T  
E: $P=%b  
Lcg)UcB-#  
-T[lx\}  
[YUv7|\  
   六、常见错误6#:检查new 操作的结果是否为null J /f  
0a-0Y&lQm  
 y"H*%]  
/Z@tv .f  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: UHTvCc  
*fn*h[pV&  
W8KDX_vGJ  
4<lRPsvgc  
Integer i = new Integer (400); Wb?8j M  
if (i == null) [Z}9>~m  
throw new NullPointerException(); $D|e>U  
T<55a6NoK  
4DL)rkO  
Cc%LztP>  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 rU2%dkTa  
K"4>DaK2P  
ck.w 5|$  
\v.C]{Gzc  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 o1h={ao  
.U?'i<  
 七、常见错误7#:用== 替代.equals OslL~<  
JU^lyi!  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: ]Zyur`  
dAkgR~  
@jsDq Ln  
\4-"L>  
int x = 4; :"xzj<(  
int y = 5; "hzB9*"t  
if (x == y) t\%HX.8[;%  
  System.out.println ("Hi"); S'_-G;g.  
// This ’if’ test won’t compile. 7:)n$,31FW  
if (x.equals (y)) s3R(vd  
  System.out.println ("Hi"); DW_1,:,?7l  
}L#_\  
r0,:J   
F pa_qjL;  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 BE_ay-  
.7.b :Dn0  
|!"`MIw,  
r?Wk<>%>  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 .xH5fMj,"  
83Q 4On  
(+FfB"3]  
%|oJ>+  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 k|lcc^[0  
}DK7'K  
:=/>Vbd: )  
T QSzx%i2  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 [ji#U s:h  
o8-^cP1  
LS88.w\=S@  
|,!IZ- th  
  八、常见错误8#: 混淆原子操作和非原子操作 8$;=Uf,x  
]2\VweV  
79xx2  
)Ccq4i  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: -< &D  
g#t[LI9(F[  
!VI]oRgP  
D IzH`|Y  
public class Example{ b+&% 1C  
  private int value; // More code here... tjluk  
  public void set (int x){ 4bn(zyP  
   // NOTE: No synchronized keyword HY%i`]4X  
   this.value = x; ~R26  
  } ,~OwLWi-|X  
} kT'u1q$3Vo  
elFtBnL'  
*/|9= $54  
'zGo?a  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: 8@2OJ=`[  
p~,]*y:XT  
^k#P5oV  
_J? Dq  
public void increment (){ T3pmVl  
  // This is effectively two or three instructions: h_T7% #0  
  // 1) Read current setting of ’value’. %]8qAtV^3j  
  // 2) Increment that setting. %+K<<iyR|  
  // 3) Write the new setting back. |>JS!NM I  
  ++this.value; Wu_kx2h  
} Dqe^E%mc  
:"I E  
\8 h;K>=h  
iMP]W _  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: ^WNrGF  
[ zEUH:9D  
)_i qAqkS  
vbD{N3p)?n  
public synchronized void increment (){ YGPy@-,E  
  ++this.value; 5wh|=**/  
} (C@~3!AVa  
Oms. e  
8_6Q~  
Jh?dw3Ai^  
  九、常见错误9#:在catch 块中作清除工作 }=8B*  
qQsku;C?i  
4@ML3d/  
frT]5?{  
  一段在catch块中作清除工作的代码如下所示: S& \L-@  
.b-f9qc=  
2m35R&  
g;8jK 8 Kh  
OutputStream os = null; }woo%N P  
try{ h}cy D7Wn  
  os = new OutputStream (); N 0= ac5  
  // Do something with os here. ?hWwj6i&  
  os.close(); 9=V:&.L  
}catch (Exception e){ HOE_S!N  
  if (os != null) a8i]]1Blz  
  os.close(); W034N[9  
} |<.lW  
P5#r,:zL  
F>-B 3x  
.G)(0z("s  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: -:Ia^{YN  
'b#0t#|TM  
I9 mvt e  
EVVP]ND  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 cnFI &,FM  
\e'R @  
<p\6AnkMr  
YJ;j x0  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 Eg2[k.{P  
ae0> W  
RQ'H$r.7g  
'F _8j;  
  3.close()可能会抛出异常。 X(\fN[;  
weE/TW\e  
<Gt2(;  
o(r\E0 I  
  上面代码的一个更优版本为: R&Jm +3N  
CO2C{~Q5  
]zQo>W$  
w[ !^;#  
OutputStream os = null; +tk{"s^r*  
try{ .$%Soyr?,  
  os = new OutputStream (); 4)"n RjGg  
  // Do something with os here. bLQ ^fH4ww  
}finally{ I*IhwJFl/  
  if (os != null) 7_mw%|m6@  
   os.close(); =R Ah|e  
} ALNc'MW!  
-Gw$#!  
j|/]#@Yr  
Okm{Xx  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 C_n9T{k  
2;^y4ssg  
Nv/v$Z{k  
 y7$iOR  
  十、常见错误10#: 增加不必要的catch 块 6C-/`>m  
m"fNK$_d  
E !a|Xp  
\yd s5g!:  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 yfx7{naKC`  
e|p$d:#!  
ZP;j9 T!  
KTn}w:+B\  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 }p!HT6 tZ  
~d%Pnw|  
FFH_d <q  
NDs!a  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: niqN{  
+H9>A0JF  
gOr%!QaF  
`S2[5i  
try{ i 2sN3it  
  // Nifty code here -Y*bSP)\  
}catch(Exception e){ zD(`B+  
  throw e; H~+l7OhV  
}finally{ awOd_![c'  
  // Cleanup code here mFSw@CC  
} 0\:(ageY?  
H'LD}\K l  
j8fpj{hp  
0MkSf*  
  不必要的catch块被删除后,上面的代码就缩短为: =Uj-^qcE  
"v`   
Z7_ zMM  
)E,\H@A  
try{ y-j\zK  
  // Nifty code here 1xbK'i:-S  
}finally{ w7FW^6Zl  
  // Cleanup code here /,X[k !  
} *3&fqBg  
Ty<L8+B|  
AN24Sf'`  
K)-m*#H&uw  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 xw3YK!$sIF  
6X\ 2GC9  
7\9>a  
{qmdm`V[  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 :J :, m  
g=2Rqi5  
%^8^yZz  
RtCkVxaEx  
  小结 5e}A@GyC  
K,e w>U  
x#Q>J"g  
)DeA} e ?F  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 H.W E6  
#Ap;_XcKw  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

您目前还是游客,请 登录注册
温馨提示:欢迎交流讨论,请勿纯表情、纯引用!
认证码:
验证问题:
3+5=?,请输入中文答案:八 正确答案:八