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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 n\ZFPXP  
Q'N<jX[  
0-FbV,:;  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 +RM3EvglDQ  
mnePm{  
$T6<9cB@  
>&TktQO_T  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 al2v1.Y}  
>wn&+%i&  
W^x[ma z  
,/KHKLY7  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 =F`h2A;a  
gm8H)y,  
^a]:GPc  
FR&RIFy  
  一、常见错误1# :多次拷贝字符串 REw3>/=  
>TE&myZ?*  
[x&&N*>N  
1Dbe0u  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 # ;9KDt@  
`yhL11 ]~  
.C1^QY-wL  
} E=mZZ)  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: lIf Our  
U> >J_2  
o)$sZ{` ="  
,UP6.C14  
String s = new String ("Text here"); )z aMycW  
UY==1\  
@U&|38  
GV9"8M Z6  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: k";;Snk  
aRV<y8{9  
1F=x~FMvY  
6};Sn/ 8  
String temp = "Text here"; HdGy$m`  
String s = new String (temp); /f#sg7)  
NzEuiI}  
}b-?Dm_H  
[~J4:yDd=  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: N9i>81tY  
d&fENnt?h  
.{Xi&[jw  
k~?@~xm,R  
String s = "Text here"; Awj`6GeJ  
f_ ::?  
N>/U%01a  
wC[J=:]tA5  
  二、常见错误2#: 没有克隆(clone)返回的对象 !:>y.^O  
6 2LZ}yn_"  
Jlzhn#5c-  
}/=VnCfU  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: l-mUc1.S  
q3;HfZ  
h7*m+/O  
$ }&6p6|  
import java.awt.Dimension; |OC6yN *P)  
/***Example class.The x and y values should never*be negative.*/ wk3yz6V2  
public class Example{ )qKfTt N`  
  private Dimension d = new Dimension (0, 0); 6L2.88 i  
  public Example (){ } ^v,^.>P  
X<1# )xC  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ ~h1'_0t   
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ ]-O:|q>]  
   if (height < 0 || width < 0) L.8-nTg"y  
    throw new IllegalArgumentException(); s)-=l _4T  
    d.height = height; m\Dbb.vBvW  
     d.width = width; # wG}T .*  
  } E)`+1j  
FuD$jsEw  
  public synchronized Dimension getValues(){ 1|zo -'y  
   // Ooops! Breaks encapsulation ;}r#08I  
   return d; kY*D s;  
  } {6n B83BB  
} 5VISP4a  
N~a?0x  
|=SaI%%Be  
ua2SW(C@  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: En[cg  
*t~( _j  
#t;@x_2yD\  
-qs9a}iL  
Example ex = new Example(); d/"e3S1  
Dimension d = ex.getValues(); 7VR+EV  
d.height = -5; Fd3V5h  
d.width = -10; N5 g!,3  
L"AZ,|wIk  
&'R\yX<J)  
b,I$.&BD  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 D].1X0^hp  
w,^!kO0)~8  
Ix}:!L  
Jz3u r)|  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 ab6KK$s  
r=u>TA$  
=n9|r.\&uJ  
/ S]<MS  
  更好的方式是让getValues()返回拷贝: LA1UD+S  
^f@EDG8  
edA.Va|0  
}" A.[9 b  
public synchronized Dimension getValues(){ ZXp=QH+f  
return new Dimension (d.x, d.y); V,lz}&3L  
} F(mm0:lT  
)/Ul" QF  
c\7~_w2  
0*x  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 3PPN_Z  
 w;)@2}  
!A g W @  
85-00m ~  
  三、常见错误3#:不必要的克隆 )p 2kx  
H htAD Y  
%I?uO( @  
:H3qa2p  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: cR _ 8 5  
]H%y7kH8  
y1z4qSeM  
1^$ vmULj  
/*** Example class.The value should never * be negative.*/ E{|j  
public class Example{ %i$]S`A}  
  private Integer i = new Integer (0); 'f]\@&Np  
  public Example (){ } cjp H hoW  
n-0RA~5z  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ $fL2w^ @  
  public synchronized void setValues (int x) throws IllegalArgumentException{ "/g/Lc  
   if (x < 0) fn]f$n*`  
    throw new IllegalArgumentException(); ^GHA,cSf  
    i = new Integer (x); F^z&s]^~  
  } ,~>A>J  
CB\E@u,  
  public synchronized Integer getValue(){ n](Q)h'nlo  
   // We can’t clone Integers so we makea copy this way. "'~55bG  
   return new Integer (i.intValue()); .gzNdSE  
  } >Ta|#]{  
} {L4ta~2/T  
CWHTDao  
C/U^8,6\n  
M|6 l  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 B^Fe.ty  
4:WN-[xX  
3%p^>D\  
=*_T;;E  
  方法getValue()应该被写为: GB&<+5t2  
1#/>[B  
#+>8gq^5  
cA m>f[  
public synchronized Integer getValue(){ ===M/}r  
// ’i’ is immutable, so it is safe to return it instead of a copy. \c(R#*0,  
return i; unY+/p $  
} /-4rcC  
N D`?T &PK  
Y`.FSs  
fq-e2MCX5  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: ezS@LFaA  
f_I6g uDPz  
xJlf}LEyF  
* `1W})  
  ?Boolean ~|QhWgq  
   ?Byte Wo+fMn(O  
   ?Character sba+J:#w  
   ?Class Rw-!P>S$  
   ?Double 8&t3a+8l  
   ?Float xy;u"JY*  
   ?Integer 'So,*>]63  
   ?Long mO=bq4!  
   ?Short P+K< /i  
   ?String ^--kcTiR%  
   ?大部分的Exception的子类 V $Y=JK@  
rlV:% k  
ROqz$yY  
VI_8r5o  
 四、常见错误4# :自编代码来拷贝数组  <m7m  
}g&A=u_2  
!g=4\C`mY  
Jvac|rN  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: S+9}W/  
6N+]g/_a  
s_:7dD  
yUd>EnQna  
public class Example{ T8'm{[C  
  private int[] copy; WOkAma-  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ &BxDS .  
  public void saveCopy (int[] data){ p$.m=+K~  
   copy = new int[data.length]; &&SA/;F  
   for (int i = 0; i < copy.length; ++i) RKru hF  
    copy = data; 0[E}[{t`  
  } K;)(fc  
} "=H(\ V  
0Ez(;4]3  
/h6K"w=='!  
0K T^V R  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: (t[sSl  
Pnl+.?  
xs?Ska,N  
rlMahY"C  
void saveCopy (int[] data){ aq,Ab~V]  
  try{ ~[a6  
   copy = (int[])data.clone(); v_G1YC7TU  
  }catch (CloneNotSupportedException e){ L/*D5k%J  
   // Can’t get here. =2J^ '7  
  } 7H=V|Btnc  
} 9:9gam  
3:wN^!A}ve  
C6` Tck!  
3mP251"dIW  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: 2J;_9 g&M  
s]X0}"cz  
r{g8CIwGQ  
C!X"0]@FA  
static int[] cloneArray (int[] data){ ~$PY6s  
  try{ j;TXZ`|(  
   return(int[])data.clone(); JiN>sEAM  
  }catch(CloneNotSupportedException e){ W *.j=?)\[  
   // Can’t get here. >a%C'H.A9  
  } 0)Nu  
} +%sMd]$,n  
/Pv dP#!  
CNMcQP  
VPi*9(LS  
  这样的话,我们的saveCopy看起来就更简洁了: &d sXK~9M>  
xwSi.~.  
i(O+XQ}Fyx  
9Ib#A  
void saveCopy (int[] data){ `En>o~L;  
  copy = cloneArray ( data); y?Cq{(  
} 2r^G;,{  
;X;q8J^_K_  
{J~VB~('  
OrP i ("/  
  五、常见错误5#:拷贝错误的数据 BWF>;*Xro  
!FA[ ]d4  
-4Hf5!  
2QIx~Er  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: y?P4EVknM3  
>S}^0vNZX  
hEhvA6f,  
<rI8O;\H  
import java.awt.Dimension; C.`!?CW  
/*** Example class. The height and width values should never * be *N65B#  
negative. */ r7FFZNs!  
public class Example{ \DMZ M  
  static final public int TOTAL_VALUES = 10; c9O0YQ3&8  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; nq%GLUH   
  public Example (){ } 2'U+QK@  
&zV; p  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ @V=HY  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ 5c ($~EFr  
   if (height < 0 || width < 0) X+KQ%Efo  
    throw new IllegalArgumentException(); v{8W+  
    if (d[index] == null) NTV@,  
     d[index] = new Dimension(); Xn6'*u>+;[  
     d[index].height = height; PN"SBsc*j-  
     d[index].width = width; nnZM{< !hF  
  } +/ U6p!  
  public synchronized Dimension[] getValues() hM nJH_siY  
   throws CloneNotSupportedException{ wl5+VC*l0  
    return (Dimension[])d.clone(); "30R%oL]=  
  } hqc)Ydg_%  
} '*=kt  
5H!6m_,w  
E}lNb  
A}W}H;8x  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: 6 K-jje;)  
8~|tl,  
'U*Kb  
Y]neTX [ef  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ g9G 8;  
  Dimension[] copy = (Dimension[])d.clone(); |R3A$r#-  
  for (int i = 0; i < copy.length; ++i){ M _e^KF  
   // NOTE: Dimension isn’t cloneable. !n3J6%b9y/  
   if (d != null) >A.m`w  
    copy = new Dimension (d.height, d.width); 2)T.Ci cx  
  } W.m2`] &  
  return copy; (W'3Zv'f  
} rUDMQxLruV  
zlhI\jRdc  
p<8Ga.kiN  
3?r?)$Jk  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: 4l?"zv1  
/SKgN{tWe  
J_7&nIH7  
t|]2\6acuc  
public void store (int[] data) throws CloneNotSupportedException{ D<J, 3(Yu  
  this.data = (int[])data.clone(); $.KD nl^  
  // OK 4fL/,j/^  
} `VXC*A   
7-j=he/  
Om5+j:YM  
#,;X2%c  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: #xNXCBl]O  
\9%RY]TK3  
ICm/9Onh&  
4h$W4NJK  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ VWT\wA L  
  this.data = (int[][])data.clone(); // Not OK! s5&v~I;>e  
} XAb-K?)   
public void rightStore (int[][] data){ \[Q*d  
  // OK! |m>{< :  
  this.data = (int[][])data.clone(); 0u=FlQ }h  
  for (int i = 0; i < data.length; ++i){ k|; [)gE  
   if (data != null) o l8|  
    this.data = (int[])data.clone(); Dl=qss~g+  
  } 9#)&  
} fl *>m,  
i1ss}JJp*  
n]a/nv  
k*k 9hv?  
|YWX.-aeo  
   六、常见错误6#:检查new 操作的结果是否为null [fIElH<  
g3kF&+2i  
KiYz]IM$4  
m$H(l4wB>  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为:  IA{I|g<  
2 `nOYK  
-J(93@X 9  
'Ej&zh  
Integer i = new Integer (400); bFwc>  
if (i == null) 5o2|QL  
throw new NullPointerException(); ,%U'>F?  
,_!MI+o0  
3-U@==:T  
sHf.xc  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 e!p?~70  
3ox 0-+_  
@DniYt/  
FWl'='5L  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 m8NKuhu  
:uQ~?amM  
 七、常见错误7#:用== 替代.equals MtXTh*4  
xy Pz_9  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: [fl^1!3{  
SJsRHQ  
PNG!q}(c  
L0EF CQ7  
int x = 4; {/K_NSg+h  
int y = 5; ~[3B<^e  
if (x == y) B,avI&7M;S  
  System.out.println ("Hi"); Jwe9L^gL  
// This ’if’ test won’t compile. KV]8o'  
if (x.equals (y)) /><+[\q4LM  
  System.out.println ("Hi"); d!#qBn$*[  
Gb_y"rx?0  
Hl b%/&  
$|n#L6k  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 +9[s(E?SY  
k/mO(i%qi  
Hribk[99  
s2;b-0  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 _S3qPPo3l]  
tp?< e  
;nZN}&m   
0zrZrl  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 2-x#|9  
0pl |  
sEm064  
i2Cw#x0s  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 ;.|).y1/`  
Gk2R:\/Y  
7^*[ XH  
x/^,{RrPk  
  八、常见错误8#: 混淆原子操作和非原子操作 61=D&lb  
-1<*mbb0  
6%t>T~x  
eZk4 $y  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: 3PgiV%]  
zD%@3NA41  
HL34pmc  
CH4 ~9mmE  
public class Example{ {v~.zRW%]r  
  private int value; // More code here... 5&N55? G6  
  public void set (int x){ 1GI/gc\  
   // NOTE: No synchronized keyword  k.("<)  
   this.value = x; 1:VbbOu->V  
  } TaTs-]4  
} kZJ.G  
)ND%MYJSq  
g}Esj"7  
< rqFBq 8  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: "*N=aHsj  
Y1Sfhs )  
> nOU 8  
LJ+Qe%|  
public void increment (){ +ew9%={zB  
  // This is effectively two or three instructions: Ql.abU  
  // 1) Read current setting of ’value’. i_kKE+Q  
  // 2) Increment that setting. 76j5  
  // 3) Write the new setting back. xv:VW<  
  ++this.value; V detY\  
} C)8>_PY[M  
[6{o13mCWE  
%YbcI|i]<0  
'mR9Uqq\  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: eV)'@ 8p  
QM 'Db`B  
E0-<-w3'  
*eb2()B%  
public synchronized void increment (){ [K4wd%+  
  ++this.value; afNqK~  
} L] ce13K  
}Rx`uRx\  
r[Zg$CW  
w!N?:}P<N  
  九、常见错误9#:在catch 块中作清除工作 F,'rW:{HMt  
8,m3]Lg  
%}0B7_6B+@  
-T+7u  
  一段在catch块中作清除工作的代码如下所示: kjVJ!R\  
=%+O.  
()+PP}:$A  
'g7eN@Wh.z  
OutputStream os = null; By7? <A  
try{ d9kN @W  
  os = new OutputStream (); klwNeGF]N  
  // Do something with os here. _0: }"!Gq  
  os.close(); S#wy+*  
}catch (Exception e){ i9 Tq h  
  if (os != null) W`2Xn?g  
  os.close(); Y&JK*d  
} n13#}i {tm  
"x P2GZ  
1*o=I-nOa  
H:jx_  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: {ICW"R lcs  
d?Y|w3lB  
EBl?oN7E  
QaYUcma~n  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 Sh+$w=vC  
;"N4Yflz  
DbH"e  
. vJlTg  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 K,' v{wSr  
OqcM3#  
HX&G  k  
~R!M.gY[rK  
  3.close()可能会抛出异常。 y +2  
]#*S.  r]  
2\/,X CQV  
 5gZ6H/.  
  上面代码的一个更优版本为: ]:X# w0UR  
<*'%Xgm  
$wBF'|eU  
znxP.=GB   
OutputStream os = null; ]dj W^C]94  
try{ 2@~hELkk/E  
  os = new OutputStream (); `\vqDWh8-  
  // Do something with os here. *fj5$T-Z  
}finally{ >ukn<  
  if (os != null) uz%<K(:Ov  
   os.close(); &ap&dM0@%a  
} H/?@UJ5m  
RL|d-A+;  
do$+ Eh  
v+b#8  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 <QcQ.b  
.nG14i7C  
6J""gyK.  
PDi]zp9>H  
  十、常见错误10#: 增加不必要的catch 块 xB<^ar  
q<Sb>M/\,  
NZW)$c'  
.%x%b6EI  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 :Ou[LF.O  
b:6NVHb%  
f2f2&|7  
(.Th?p%>7  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 vi1 D<  
)oU%++cdo  
Wq}Y|0c  
 'K7m!y  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: 9z9\pXFQ  
&Fg|52  
bMp[:dw`y  
B}U:c]  
try{ +$;* "o  
  // Nifty code here  2.>aL  
}catch(Exception e){ M8{J  
  throw e; {IgL H`@  
}finally{ MX )mm^A  
  // Cleanup code here 9m+ejTK{U  
} km,I75o.  
!-cK@>.pE  
GVK c4HGt  
CAT{)*xc  
  不必要的catch块被删除后,上面的代码就缩短为: 5"WI^"6b:  
f]C`]qg  
@yj$  
KKcajN  
try{ 9 7Ua,  
  // Nifty code here #M5pQ&yZy  
}finally{ kIwq%c;  
  // Cleanup code here W Yo>Md 8  
} RE%25t|  
7RZ HU+  
5 !Ho[  
?l>Ra0  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 D_)N!,i  
!(8) '<t9  
IDK~ (t  
#Y%(CI  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 ?[!_f$50]P  
_fM=J+  
f>zd,|)At  
P|tNmv[;  
  小结 3'z L,WW  
nIEIb.-  
4L_AhX7  
n3" @E<rW  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 7I=vgT1F  
qp{3I("_  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

您目前还是游客,请 登录注册
批量上传需要先选择文件,再选择上传
认证码:
验证问题:
10+5=?,请输入中文答案:十五