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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 b`)^Ao:  
/~Zxx}<;  
GSj04-T"  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 ]{;=<t6  
[Dni>2@0  
uU=O0?'zq  
zZE 2%fqM  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 rf?Q# KM\W  
1A^~gYr  
`Hld#+R  
t`1E4$Bb\  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 Lwm2:_\_b  
wyvrNru<l4  
N,6(|,m  
1'@/ jR  
  一、常见错误1# :多次拷贝字符串 l( /yaZ`  
a/Z >-   
&>y[5#qOl  
=QXLr+ y@  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 !B_i~Rmg  
8DHohhN  
/2Q@M>  
{W*_^>;K  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: B/u0^!  
_PUgK\  
0 UdAF  
tp"dho  
String s = new String ("Text here"); Ad!= *n  
'i#m%D`dt  
x@/ N9*  
#Ux*":  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: !.9pV.~  
"5Orj*{  
f6@fi`U ,  
B@XnHh5y  
String temp = "Text here"; BBnW0vAZ*  
String s = new String (temp); PEqO<a1Z8  
,5`pe%W7  
~H"Q5Hr   
Ct2m l  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: FrXFm+8 F  
=8FV&|fP  
ClZ:#uMbN  
?`T< sk8c  
String s = "Text here"; `@?f@p$(B  
-ZwQL="t  
jzCSxuZ7O  
baJ(Iy$XT  
  二、常见错误2#: 没有克隆(clone)返回的对象 aBv3vSq> Q  
I!'PvIyO  
mRVE@ pc2X  
=xz Dpn>f  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: &-^|n*=g6  
Ws2SD6!4`  
|KEq-  
v[4A_WjT  
import java.awt.Dimension; GiI|6z!  
/***Example class.The x and y values should never*be negative.*/ #lDf8G|ST~  
public class Example{ )c~1s  
  private Dimension d = new Dimension (0, 0); fNjxdG{a  
  public Example (){ } {@j0?s  
H{}0- 0o  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ zzmZ`Ya  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ 'wh2787  
   if (height < 0 || width < 0) /O@'XWW  
    throw new IllegalArgumentException(); g+shz{3zvz  
    d.height = height; ={_.}   
     d.width = width; ' *hy!f]  
  } s%Ez/or(T  
Z(g9rz']0  
  public synchronized Dimension getValues(){ R7axm<PR=  
   // Ooops! Breaks encapsulation =~;SUO  
   return d; w D|p'N  
  } x\HHu]  
} K5X,J/n  
(Uk1Rt*h  
%N}O Mc.W  
FtBYPSGz  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: zQY|=4NP  
[Y@?l]&  
Q.SLiI  
v.Fq.  
Example ex = new Example(); V/%tFd1  
Dimension d = ex.getValues(); 00s&<EM  
d.height = -5; (S ~|hk^  
d.width = -10; 3a#X:?  
[AAG:`  
^(,qkq'u D  
< H1+qN=]`  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 o)w8 ]H /  
"r@f&Ssxb  
];@"-H  
MDETAd  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 j#KL"B_ A  
fAx7_}k/ m  
z=ItKoM*<  
lgR;V]^YX  
  更好的方式是让getValues()返回拷贝: N^,@s"g  
ZM)Y Rdh  
9OIX5$,S;  
L`"PaIMz  
public synchronized Dimension getValues(){ "zU}]|R  
return new Dimension (d.x, d.y); "YIrqk  
} "6.JpUf  
%EC{O@EAk  
i={ :6K?^  
Vs>/q:I  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 .}KY*y  
S@Rw+#QE  
\etuIFQ#U  
@8[3 ]<  
  三、常见错误3#:不必要的克隆 K@Z K@++  
FmU>q)  
t- Rp_2t  
!'-K>.B  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: gQ]WNJ~>  
zj G>=2  
X^L)5n+$X  
uXxc2}  
/*** Example class.The value should never * be negative.*/ R-xWZRl>  
public class Example{ 6.]x@=Wm  
  private Integer i = new Integer (0); RI,Z&kXj2o  
  public Example (){ } P38D-fLq  
%lL^[`AR  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ AEjkqG4qv  
  public synchronized void setValues (int x) throws IllegalArgumentException{ [Xy^M3  
   if (x < 0) %m8;Lh- X  
    throw new IllegalArgumentException(); eURy]  
    i = new Integer (x); FScE3~R  
  }  '%! '1si  
wd4wYk\  
  public synchronized Integer getValue(){ CuE>=y- "I  
   // We can’t clone Integers so we makea copy this way. SoS GQ&k  
   return new Integer (i.intValue()); *0 y|0J+ 0  
  } {? -@`FR-  
} aOWE\I c8  
WdXi  
=F>@z4[P-  
:5/P{Co (  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 rh;@|/<l  
yFfa/d  
3U#z {%  
b >R/=tx  
  方法getValue()应该被写为: XqD/~_z;  
Q}G'=Q]Juz  
Nr.maucny  
5w:   
public synchronized Integer getValue(){ oH/6  
// ’i’ is immutable, so it is safe to return it instead of a copy. z6B(}(D  
return i; R#ayN*  
} 5<-_"/_  
+J`EBoIo  
'qRK6}"T  
_sU|<1  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: h-rPLU;Bw  
"tu BfA+f  
?A|8J5E V  
a}N m;5K  
  ?Boolean ]fH U/%  
   ?Byte "zSi9]j  
   ?Character y#\jc4F_a  
   ?Class XW!a?aLNX  
   ?Double dQfVdqg  
   ?Float ~.4-\M6[  
   ?Integer aL\vQ(1zO  
   ?Long xpo<1Sr>S  
   ?Short LQVa,'  
   ?String :Mz$~o<  
   ?大部分的Exception的子类 #BQ7rF7CNE  
v ?b9TE  
; z_ZZ(W  
lSw9e<jYO  
 四、常见错误4# :自编代码来拷贝数组 F.P4c:GD  
K/;FP'.  
x\qS|q\N  
@WIcH:_w-  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: ,#G>&  
+ QcgLq  
R["2kEF  
%mR roR6  
public class Example{ l?CUd7P(a  
  private int[] copy; QG\lXY,  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ +hdD*}qauC  
  public void saveCopy (int[] data){ Kfnn;  
   copy = new int[data.length]; =j[zMO  
   for (int i = 0; i < copy.length; ++i) oY@4G)5  
    copy = data; dI!x Ai  
  } s^KUe%am0  
} m=&j2~<i  
$x&@!/&|pv  
_RG!lmJV  
XGE:ZVpW  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: @DgJxY|  
2.)@u~^Q  
h?wNmLre  
xEBiBsk d  
void saveCopy (int[] data){ mG[S"?C  
  try{ uLV@D r   
   copy = (int[])data.clone(); !4uTi [e  
  }catch (CloneNotSupportedException e){ mQt';|X@  
   // Can’t get here. olPV"<;+pO  
  } jRN*W2]V  
} n`";ctQT  
,<$6-3sC-  
<VD^f  
U~z`u&/  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: m%ZJp7C  
KE\p|Xi  
[Af&K22M(X  
%=x|.e@J  
static int[] cloneArray (int[] data){ Ojj:YLlY>  
  try{ n*-t =DF  
   return(int[])data.clone(); b#0y-bR  
  }catch(CloneNotSupportedException e){ )'8DK$.  
   // Can’t get here. :A35 ?9E?  
  } yoTbIQ  
} =jvM$  
e:H7ht:  
Zd@'s.,J  
?Dro)fH1  
  这样的话,我们的saveCopy看起来就更简洁了: Ws?BAfP  
e*_8B2da  
<{@?c  
PbOLN$hP  
void saveCopy (int[] data){ _+0l+a*D  
  copy = cloneArray ( data); GSg|Gz""J0  
} iMRb` \KH  
87%t=X  
&fA`Od6l"  
<N"t[N70;  
  五、常见错误5#:拷贝错误的数据 BQWEC,*N  
.#R\t 7m%  
K7}EL|Kx  
a5WVDh, cR  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: 2V(ye9  
f !t2a//  
dq%N,1.F  
AfKJa DKf  
import java.awt.Dimension; !!&H'XEJV  
/*** Example class. The height and width values should never * be K[RlR+j  
negative. */ T[M?:~  
public class Example{ >nmby|XtW  
  static final public int TOTAL_VALUES = 10; {\22C `9t  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; $Ch!]lJA  
  public Example (){ } $Tu%dE(OF  
\FN"0P(G  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ 6Uq@v8mh  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ 6'W79  
   if (height < 0 || width < 0) P<GY"W+r R  
    throw new IllegalArgumentException(); uyP)5,  
    if (d[index] == null) f XxdOn.  
     d[index] = new Dimension(); !\Jj}iX3_  
     d[index].height = height; /b,+YyWi%  
     d[index].width = width; *v3/8enf  
  } P\;lH"9  
  public synchronized Dimension[] getValues() pH.wCD:1n  
   throws CloneNotSupportedException{ LFy5tX#  
    return (Dimension[])d.clone(); p,}-8#K[  
  } B1m@  
} +]H9:ARI  
L*bUjR,C  
uRG0} >]|U  
o:#l r{  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: r/=v;4.W  
bTZ/$7pp9  
{'Nvs_{6  
*A2J[,?c  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ ug[|'tR8  
  Dimension[] copy = (Dimension[])d.clone(); ;[WW,,!Y  
  for (int i = 0; i < copy.length; ++i){ P, !si#  
   // NOTE: Dimension isn’t cloneable. YIb5jK `  
   if (d != null) CF9a~^+%  
    copy = new Dimension (d.height, d.width); qdh;zAMx  
  } '-3AWBWI1  
  return copy; ;RZ@t6^  
} w[\*\'Vm0  
6PT ,m  
K"Vv=  
 Z_F:H@-&  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: bO1J#bcZ  
Z `O.JE  
{g\Yy(r  
E+V^5Z:u  
public void store (int[] data) throws CloneNotSupportedException{ > C{^{?~u  
  this.data = (int[])data.clone(); :k8>)x] )  
  // OK 7{f&L '  
} 1g9Q vz3  
xc Wr hg  
0z/tceW'F  
G40,KCa  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: 62rTGbDbx  
-h^FSW($-R  
LtbL[z>]  
2<>n8K  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ 4;Z`u.1  
  this.data = (int[][])data.clone(); // Not OK! HxAq& J;xu  
} SSycQ4[{o  
public void rightStore (int[][] data){ @=`Dw/13  
  // OK! kt%9PGw  
  this.data = (int[][])data.clone(); "o#"u[W ,  
  for (int i = 0; i < data.length; ++i){ }$#e&&)n  
   if (data != null) [VIdw 92  
    this.data = (int[])data.clone(); 5+rYk|*D+k  
  } WGKN>nV  
} (G<"nnjK  
im9 B=D  
iN+Dmq5  
5^cPG" 4@  
mfFC@~|g  
   六、常见错误6#:检查new 操作的结果是否为null :Gqyj_|<  
*CA|}l  
xr?=gY3E;  
r J ?Y~Q  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: m'Z233Nt"  
?H eC+=/Z  
?|TVz!3  
Ks@S5:9sp  
Integer i = new Integer (400); 6:>4}WOP  
if (i == null) 6[wej$ u  
throw new NullPointerException(); yxQxc5/X)  
[%;LZZgl  
'cy35M  
nf+8OH7  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 -en:81a#  
MHVqRYz  
cKh{ s  
M|?qSFv:  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 g[*+R9'  
GBu&2}  
 七、常见错误7#:用== 替代.equals QX ishHk&  
R!W!8rr3  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: wX8T;bo&  
+`kfcA#pi  
?'CIt5n+\{  
IGEf*!  
int x = 4; ,Bp\ i  
int y = 5; T9KzVxHp5  
if (x == y) Z/sB72K1  
  System.out.println ("Hi"); m46Q%hwV  
// This ’if’ test won’t compile. AR`X2m '  
if (x.equals (y)) wblEx/FqE^  
  System.out.println ("Hi"); `j(\9j ok  
yU\&\fD>j  
!EF~I8d\]  
PP/M-Jql)  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 gZ   
CY)/1 # J  
!~04^(  
yY4*/w7*j4  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 HC*=E.J  
)^ R]3!v  
$Eg|Qc-1  
'Z+w\0}@  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 3qiE#+dC  
+8."z"i3lE  
qCSJ=T;  
yX$I<L<Suz  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 i]Lt8DiRq  
VxLq,$B76  
apa~Is1  
bsC~ 2S\o  
  八、常见错误8#: 混淆原子操作和非原子操作 UAnq|NJO  
(u@[}!  
vI{JBWE,S  
enj Ti5X  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: <_uLf9j a  
\U4O*lq  
kCL)F\v"iT  
Ym(^i h  
public class Example{ zKe&*tZ  
  private int value; // More code here... 'j !!h4  
  public void set (int x){ S6h=} V )  
   // NOTE: No synchronized keyword  8;4vr@EV  
   this.value = x; 8*6vX!Z|  
  } >J[g)$,  
} R/waWz\D  
hc5M)0d  
Oq(_I b)9  
Vg#s  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: _J}ce  
$B7<1{<=W  
@(JcM=  
sD1L P  
public void increment (){ muQH!Q  
  // This is effectively two or three instructions: _ YWw7q  
  // 1) Read current setting of ’value’. sT[)r]`T  
  // 2) Increment that setting. 7fLLV2  
  // 3) Write the new setting back. ;tf1 #6{  
  ++this.value; 4HVZ;,q  
} )@Z J3l.  
}e6Ta_Z~  
C (vi ns  
w (W+Y+up  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: +hpXMO%?  
[y\ZnoB  
9qIUBHe  
3%/]y=rA  
public synchronized void increment (){ xgrk>Fb|R  
  ++this.value; p/:)Z_  
} S6B(g_D|  
9U>ID{  
p^&' C_?  
O_ nk8  
  九、常见错误9#:在catch 块中作清除工作 |VB}Kv  
6TbDno/!'  
7ks09Cy  
#^gn,^QQ  
  一段在catch块中作清除工作的代码如下所示:  .LEQ r)  
Cnd*%CPZ  
CM>/b3nOW  
4))u*c/,  
OutputStream os = null; ^TyusfOz  
try{ DdJxb{y7  
  os = new OutputStream (); KunK.m  
  // Do something with os here. *;7&  
  os.close(); u'd+:uH  
}catch (Exception e){ |b;}' *  
  if (os != null) 79~,KFct  
  os.close(); !3DWz6u  
} b\7-u-   
I.fV_ H^  
n4 KiC!*i0  
qlIC{:E0  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: _c['_HC  
R$K.;  
Ig'Y]%Z0  
[zm&}$nnN  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 MnO,Cd6{%d  
D2[wv+#)  
U_ V0  
RI:x`do  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 Xf:CGR8_  
fNFdZ[qOd  
 &{7n  
r%xp^j}  
  3.close()可能会抛出异常。 uwj/]#`  
Oe'Nn250  
b)en/mz  
UY5ia4_D  
  上面代码的一个更优版本为: +N5G4t#.  
DvG.G+mo#  
DGC -`z  
V8NJ0fF  
OutputStream os = null; b*nyt F  
try{ [NFg9y;{h  
  os = new OutputStream (); 5dMIv<#T`  
  // Do something with os here. Qf414 oW  
}finally{ %{yr#F=t#]  
  if (os != null) @?aNvWeavH  
   os.close(); [{Fr{La`D'  
} (iP,F]  
dY~3 YD[  
HLN rI0  
V{HZ/p_Y  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 *-ZD-B*?  
A~ '2ki5$g  
3hmuF6y~  
x~$P.X7(~  
  十、常见错误10#: 增加不必要的catch 块 go]d+lhFB  
nOkX:5  
+; C|5y  
6O"Vy  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 3HO 4 h\mp  
$P^=QN5 Bb  
)FMpfC>An  
Ki^m&P   
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 J6hWcA6 g  
N,(@k[uta  
CUnZ}@?d  
8hQ"rrj+  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: (8GJLs 8  
*Sz`=U7n  
"3Dvc7V  
[^"e~  
try{ J%aW^+O  
  // Nifty code here 3 cT  
}catch(Exception e){ zO2<Igb  
  throw e; oFoG+H"&7\  
}finally{ Kw(/#C:$  
  // Cleanup code here U> e@m?  
} 8ji!FZf  
sG}}a}U1  
`*KS` z?  
>/6v` 8F  
  不必要的catch块被删除后,上面的代码就缩短为: 7vNS@[8  
 *X*D, VY  
qI<*Cze  
k,X)PQc  
try{ VEpIAC4  
  // Nifty code here h6)hZ'zV  
}finally{ 2}[rc%tV:?  
  // Cleanup code here I}n"6'*  
} cn<9!2a  
Y91TF'  
5c{=/}Y  
{ zlq6z  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 9rn!U2  
8Ral%I:gr  
aFyNm@a  
Jfo|/JQ  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 V *S|Qy!p  
J-Sf9^G  
m1\>v?=K  
jd]MC*%  
  小结 TCmWn$LeE  
nqgfAQsE)  
"9LPq  
DMfC(w.d  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 9tl Fbu  
GZI[qKDfB  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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