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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 #9}KC 9f  
\$ ^z.  
Z!G_" 3  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 " jn@S-  
7oA$aJQ  
"UKX~}8T  
n|lXBCY7K  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 h'^7xDw  
2/=CrK  
)`F? {Sg  
#Bj{ 4OeV  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 LdR}v%EH  
*ntq;]  
4Cke(G  
~cy/\/oO  
  一、常见错误1# :多次拷贝字符串 WRZi^B8 @  
`GC7o DL  
ir qlU  
J)A1`(x&T  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 'e02rqip{  
HKv:)h{ ?  
#6fp "  
H&E c *MT  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: dm,7OQ  
,$Qa]UN5Q  
QX ishHk&  
.x$+R%5U  
String s = new String ("Text here"); J6Hw05%0=  
. l RW  
] M "{=z  
?'CIt5n+\{  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: pA"x4\s   
|4YDvDEJi  
:N\*;>  
!cE>L~cza  
String temp = "Text here"; kLR4?tX!  
String s = new String (temp); m46Q%hwV  
sI/Hcm  
\ lP c,8)  
oc?,8I[P5  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: Ge@./SGT  
d{hb gUSj  
D#x D-c  
~-GgVi*I  
String s = "Text here"; *PMvA1eN=#  
Mr<2I  
oaHg6PT!  
@Rj&9/\L  
  二、常见错误2#: 没有克隆(clone)返回的对象 =DvFY]9{  
dl'pl  
e{:P!r aM  
d,iW#,  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: Zq2dCp%  
24Z7;'  
Y"D'|i  
~;aSX1   
import java.awt.Dimension; '{\VO U  
/***Example class.The x and y values should never*be negative.*/ Hhr/o~?;}#  
public class Example{  "@Bc eD  
  private Dimension d = new Dimension (0, 0); Xlw&hKS  
  public Example (){ } ,G e7 9(  
cn v4!c0  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ gH Q[D|zu  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ :1q+[T/ @  
   if (height < 0 || width < 0) A1{P"p!  
    throw new IllegalArgumentException(); -_ .f&l8  
    d.height = height; bRJYw6oA<  
     d.width = width; ~1`.iA  
  } SOE#@{IXBa  
a)MjX<y  
  public synchronized Dimension getValues(){ )W:`Q&/G  
   // Ooops! Breaks encapsulation lu`\6  
   return d; mG7Wu{~=U  
  } Z6!MX_ep  
} UA!h[+Z  
}C/u>89%q  
C#emmg!a\  
/YR*KxIx  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: i?z3!`m  
Kw3fpNd  
@SDsd^N{2P  
ElZ'/l*\  
Example ex = new Example(); /v: g' #n  
Dimension d = ex.getValues(); DOaEz?2)  
d.height = -5; Vs]+MAL  
d.width = -10; X |.'_6l.  
Id *Gs>4U  
jx!)N>  
pB@8b$8(Z  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 'BpK(PlUh  
pNcNU[c  
L=iaL[zdJ  
+)^F9LPl  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 [N$da=`wv  
:J@q Xa  
muQH!Q  
8js5/G+  
  更好的方式是让getValues()返回拷贝: Z=sy~6m+v  
$R2T)  
im>Sxu@  
;tf1 #6{  
public synchronized Dimension getValues(){ WiH%URFB  
return new Dimension (d.x, d.y); m( C7Fa  
} S]KcAz(fX  
@BbZ(cZ*  
d;Z<")  
>T%Jlj3ZG  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 iJ~5A'?6  
Dn) =V.  
&9$0v"`H  
fa=#S  
  三、常见错误3#:不必要的克隆 SDcxro|8i  
Vl{CD>$,  
/u<lh. hPW  
K7F uMB  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: },2-\-1  
DIB Az s  
W8,XSUl  
hmtRs]7  
/*** Example class.The value should never * be negative.*/ _U1~^ucV  
public class Example{ `)`_G!a  
  private Integer i = new Integer (0); J#L-Slav%  
  public Example (){ } o$'Fz[U  
@CP"AYB #  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ jC*(ZF1B  
  public synchronized void setValues (int x) throws IllegalArgumentException{ q]0a8[]3  
   if (x < 0) (ivV[  
    throw new IllegalArgumentException(); 8 2&JYx  
    i = new Integer (x); V5i_\A  
  } D7X-|`kH  
#StD]d  
  public synchronized Integer getValue(){ X"(!\{ySI;  
   // We can’t clone Integers so we makea copy this way. I--WS[  
   return new Integer (i.intValue()); *;7&  
  } r62x*?/  
} ;Z-Cn.  
NZ e3 m  
xB68RQe)  
!3DWz6u  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 U; ?%rM6  
LbJ tU !  
P|v;'9  
O4@Ki4f3A%  
  方法getValue()应该被写为: Wcz{": [  
oIt.Pc~;'#  
Ig'Y]%Z0  
K)]7e?:Wu  
public synchronized Integer getValue(){ FZ #ngrT  
// ’i’ is immutable, so it is safe to return it instead of a copy. WVftLIJ  
return i; r[eZV"  
} U_ V0  
8d-; ;V  
25l6@7q.  
1T%Y:0  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: G#HbiVH9  
H.7gSB1  
Z9i,#/  
L4zSro:Si  
  ?Boolean ldM [8  
   ?Byte 3Ym5SrKK  
   ?Character w^ui%9 &6H  
   ?Class K-)*S\<}  
   ?Double 5hB&]6n  
   ?Float ~B:Lai4"  
   ?Integer %+w>`k3(N  
   ?Long req=w;E:  
   ?Short ?f1%)]>   
   ?String YdV5\!  
   ?大部分的Exception的子类 j^1T3 +  
tRS^|??  
Ve2z= 6(  
,YSQog  
 四、常见错误4# :自编代码来拷贝数组  k1L GT&  
}Tu_?b`RUm  
nqBZp N ^  
bFVz ;  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: -]Z!_[MlDF  
vROl}s;  
8doT`rI1  
UX41/# 4  
public class Example{ .Y&_k  
  private int[] copy; U# -&%|b$  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ ~1S7\e7{  
  public void saveCopy (int[] data){ A~ '2ki5$g  
   copy = new int[data.length]; `kwyF27v]  
   for (int i = 0; i < copy.length; ++i) *na7/ysT<  
    copy = data; ynw^nmM  
  } E,xCfS)  
} xii*"n~  
zr&K0a{hc  
L-Xd3RCD  
iEr|?,  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: 7_S+/2}U*  
$P^=QN5 Bb  
<.l5>mgkCw  
Y3-Tg~/~W  
void saveCopy (int[] data){ eoR@5OA&  
  try{ mZ/?uPIa  
   copy = (int[])data.clone(); ,'Y*e[  
  }catch (CloneNotSupportedException e){ 6"|PJ_@P  
   // Can’t get here. |E53 [:p  
  } 6aM`qz)  
} lDe9EJR  
#Q^mdv?  
Cs^o- g!L  
HNY{%D  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: '$ s:cS`=  
(dpBGt@  
L0UAS'hf  
-njxc{b  
static int[] cloneArray (int[] data){ vO]gj/SaT  
  try{ ,T|iA/c  
   return(int[])data.clone(); k|BY 7C  
  }catch(CloneNotSupportedException e){ Xvi{A]V  
   // Can’t get here. 8ji!FZf  
  } ,G"?fQ7zR  
} m]Z+u e  
>7vSN<w~m  
-hQ=0h~\B.  
$ ohwBv3S  
  这样的话,我们的saveCopy看起来就更简洁了: ^dZ,Itho  
g|"z'_  
>Eik>dQ a  
HjGT{o  
void saveCopy (int[] data){ /p<mD-:.M  
  copy = cloneArray ( data); ^P"t "  
} a+A/l  
2}[rc%tV:?  
$]|_xG-6{  
q1r\ 60M  
  五、常见错误5#:拷贝错误的数据 tK g%5;v  
*%B%BJnX  
mrFMdpaHl%  
cAVe(:k)  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: 6jCg7Su]  
;NRm ,  
Jfo|/JQ  
)lB-D;3[_  
import java.awt.Dimension; |g8 ]WFc  
/*** Example class. The height and width values should never * be g\rujxHlH  
negative. */ .a;-7|x  
public class Example{ I #1_  
  static final public int TOTAL_VALUES = 10; *fSa8CV  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; }9Y='+.%^  
  public Example (){ } ~`*:E'/5k]  
U!3nn#!yE  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ 6XFO@c}d  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ dMRwQejY{7  
   if (height < 0 || width < 0) /PPk p9H{  
    throw new IllegalArgumentException(); #kLM=a/_NO  
    if (d[index] == null) g0g/<Tv[  
     d[index] = new Dimension(); d`({z]W;  
     d[index].height = height; *'d5~dz=  
     d[index].width = width; IdzF<>;W  
  } &bBp`h  
  public synchronized Dimension[] getValues() h=`rZC  
   throws CloneNotSupportedException{ lba*&j]w=  
    return (Dimension[])d.clone(); j|lg&kN  
  } eC[g"Ef  
} o|^0DYb  
168U-<  
F b`V.  
oJ6 d:  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: J)'6 z  
:JW~$4  
"q#(}1Zd  
Bfi9%:eG  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ KC}B\~ +  
  Dimension[] copy = (Dimension[])d.clone(); ~+CNED0z+  
  for (int i = 0; i < copy.length; ++i){ 8f8+3  
   // NOTE: Dimension isn’t cloneable. KO{}+~,.6  
   if (d != null) Kz$Ijj  
    copy = new Dimension (d.height, d.width); +Tq _n@  
  } ip1jY!   
  return copy; bpUN8BI[T  
} ;pAkdX&b  
<=A&y5o  
_QXo4z!a8  
eRVu/TY  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: <9@VY  
YI%S)$  
Ls|;gewp  
yMo@ka=v  
public void store (int[] data) throws CloneNotSupportedException{ b#82G`6r  
  this.data = (int[])data.clone(); >V;<K?5B`W  
  // OK t{?_]2vl  
} n>#h(  
Rw$ @%o%  
[K"v)B'  
>!bYuVHA  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: U$Ew,v<  
>D-$M_  
<a$cB+t  
r9*{)"  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ XZKOBq B]  
  this.data = (int[][])data.clone(); // Not OK! ghms-.:b8  
} mcr71j  
public void rightStore (int[][] data){ 9F,jvCM63  
  // OK! foL`{fA  
  this.data = (int[][])data.clone(); <JKPtF2b  
  for (int i = 0; i < data.length; ++i){ }jIb ^|#CD  
   if (data != null) K"g[%O<  
    this.data = (int[])data.clone(); #jDO?Y Sa  
  } 55,vmDd  
} QFX|ZsmK  
rbP.N ?YU%  
<D&75C#  
Q{$2D&  
>+$1 p_  
   六、常见错误6#:检查new 操作的结果是否为null ;zh|*F>  
3J:!8Gmk  
+,PBhB  
vWj|[| <rX  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: F] ~`57  
I[F.M}5:z  
uvm=i .  
| @mZ]`p  
Integer i = new Integer (400); ap=M$9L'  
if (i == null)  =v8#@$  
throw new NullPointerException(); nE/T)[1|  
t`Hwq   
xpSMbX{e  
8ALYih7"W  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 *_^AK=i  
nQ/El&{  
Sc*p7o: A  
4Ly!:GH3T  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 -bE{yT)7  
5HJ6[.HO  
 七、常见错误7#:用== 替代.equals f+F /`P%  
wddF5EcK0  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: ? 8'4~1g`}  
"lUw{3  
Va !HcG1^:  
FTk!Mn88  
int x = 4; B04Br~hel*  
int y = 5; w"aD"}3  
if (x == y) 3RGVH,  
  System.out.println ("Hi"); b$M? _<G  
// This ’if’ test won’t compile. ]Oe#S"-Oo  
if (x.equals (y)) B)Gm"bLCOZ  
  System.out.println ("Hi"); XmXHs4  
y]@_DL#J=  
$TR[SMj  
tq1h1  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 0p~:fm  
#V~r@,  
bup;4~g  
Ig S.U  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 O":x$>'t  
:~`E @`/  
 LqU]&AAh  
+F`! Jt  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 Z*kg= hs^  
w3B*%x)  
YK_a37E{F  
LQR9S/?Ld  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 p+yU!Qj  
tn:9  
69CH W&  
V! ~uGf  
  八、常见错误8#: 混淆原子操作和非原子操作 W;,Jte<'Nm  
J 9>uLz  
}Z%*gfp  
\O\onvEa  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: r@iGM Jx$  
6Zkus20  
rTK/WZs8  
unP7("A0D  
public class Example{ N?R1;|Z]  
  private int value; // More code here... R3.tkFZq]  
  public void set (int x){ x'n J_0  
   // NOTE: No synchronized keyword |-2,k#|  
   this.value = x; l |\Q~ D!o  
  } ^<ayPV)+  
} kOJs;k  
[UFLL:_sC  
fMhMB |W.  
@hg1&pfxZ<  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: Elm/T]6  
pdmeB  
L?0dZY-"  
&]uhPx/  
public void increment (){ ,mjwQ6:Ny  
  // This is effectively two or three instructions: "r.pU(uxt  
  // 1) Read current setting of ’value’. Ugrcy7  
  // 2) Increment that setting. 5C2 *f 4|  
  // 3) Write the new setting back. _ @ \  
  ++this.value; !^B`7  
} e WcS>N  
e7 5*84  
"y>l2V,4j%  
-/KVZ  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: Fi1gM}>py  
Nluy]h &  
6g( 2O[n.  
;^t<LhN:  
public synchronized void increment (){ QH#|R92:  
  ++this.value; O"_FfwO a  
} *H:;pI WP  
4l>/6LNMF  
PNc^)|4^Q  
m {wMzsQ  
  九、常见错误9#:在catch 块中作清除工作 obS|wTG~  
iK'bV<V&7  
"4}{Z)&R2  
*\:u}'[  
  一段在catch块中作清除工作的代码如下所示: :] {+ 3A  
wD}[XE?S  
}.MJVB3  
o= N=W  
OutputStream os = null; ~kw[Aw3?D\  
try{ -=O9D- x=  
  os = new OutputStream (); `'.u$IBW  
  // Do something with os here. )!){4c/  
  os.close(); sf7'8+wj>  
}catch (Exception e){ !,INrl[  
  if (os != null) ~h  tV*R  
  os.close(); |"vqM)V$  
} Y0aO/6  
gx@b|rj;  
jA<v<oV  
Ah) _mxK  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: cYmMO[4YG'  
:Y ~fPke  
IHMZE42  
Z/6B[,V  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 )r5QOa/  
]X;Ty\UD&  
_U%!&_m6  
7AHEzJh"  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 pZ%/;sxYa  
95[yGO>ZYz  
~'=s?\I  
C3hnX2";  
  3.close()可能会抛出异常。 ,]42v?  
91}QuYv/_  
! E#XmYhX=  
bu,Z'  
  上面代码的一个更优版本为: VQ{}S $jQ  
thl{IU  
d]$z&E  
|:L<Ko  
OutputStream os = null; _:?)2NV  
try{ ]aXCi"fMs  
  os = new OutputStream (); 8'@pX<  
  // Do something with os here. W2qW`Ujo{  
}finally{ -U'6fx) +  
  if (os != null) L&][730  
   os.close(); z?Hvh  
} 4:y;<8+j\  
q --NLm@;  
w<.{(1:v  
`oXUVr  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 G@BF<e{  
Fpzps!(;=  
"ALR)s,1,  
Z,! w.TYo  
  十、常见错误10#: 增加不必要的catch 块 g\OPidY  
AhiZ0W"  
CJ%bBL'.  
$DJp|(8  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 BO5F6lyQ0P  
v%VCFJ  
)PVX)2P_C  
593D/^}D  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 %o.{h  
4?jXbC k~x  
{~.h;'m  
i$?i1z*c}  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: XTXRC$B  
q{[}*%  
?r"m*fY%  
Xv|~1v%s7  
try{ X0* y8"  
  // Nifty code here 9@nX 6\ ,  
}catch(Exception e){ _6;T /_R=  
  throw e; "9Sxj  
}finally{ *+vS f7  
  // Cleanup code here w(]Q `  
} 1X.5cl?V  
&D\~-fOGb  
<2HI. @^  
q UY;CEf  
  不必要的catch块被删除后,上面的代码就缩短为: 9(dbou  
.-k\Q} D  
Ps4spy0Fp  
J'sVT{@GS  
try{ ^!3Sz1  
  // Nifty code here k$9oUE,  
}finally{ N0,.cd]y`  
  // Cleanup code here d/k&f5  
} 7N+No.vR.  
uZ&,tH/  
Ia*eb%HG  
6! \a8q'z  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 _S7GkpoK  
~Yv"=  
t \kI( G  
<VS\z(K  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 QHUoAa`6v  
vZ\~+qV,A  
EGf9pcUEO&  
rQC{"hS1  
  小结 f`*Ip?V-  
79AOvh  
 P 1X8  
`r & IA  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 MoIVval/  
RAxAy{  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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