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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 u#~RkY7s  
Jpq~  
t?gic9 q  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 T!{w~'=F  
fOrH$?  
kZ:ZtE  
re<{ >  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 t@;p  
wlvgg  
Z{d^-  
P+sW[:  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 3?yg\  
(C L%>5V  
l'qg8  
n@i HFBb  
  一、常见错误1# :多次拷贝字符串 T-L||yE,h  
r6qj7}\  
z<;HQX,  
Or+U@vAnk  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。  _[3D  
o|:b;\)b  
"sCRdx]_  
+\A,&;!SR  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: Qv-_ jZ  
rlLMT6r.8  
_VN?#J)o  
6 "sSoj  
String s = new String ("Text here"); ]6` %  
ObS3 M  
L*+@>3mu)  
ITBE|b  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: Llo"MO*sr  
/6* 42[r  
+'a^f5  
m0SlOgRsk  
String temp = "Text here"; tk`v:t!6U  
String s = new String (temp); _{KG 4+5\X  
ND;#7/$>  
cI*;k.KU  
m(!FHPvN  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: Fxz"DZY6  
xp{tw$  
[q -h|m  
q9_OGd|P  
String s = "Text here"; " 8MF_Gu):  
o.!Dq7 R  
M }D}K\)  
2ilQXy  
  二、常见错误2#: 没有克隆(clone)返回的对象 vE?G7%,  
FZlWsp=  
oc`H}Wvn  
F41=b4/  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: S\=Nn7"  
)t#W{Gzfmh  
TJRCH>E[a  
##*3bDf$-5  
import java.awt.Dimension; R 9\*#c  
/***Example class.The x and y values should never*be negative.*/ +<C!U'  
public class Example{ K%oG,-wdg  
  private Dimension d = new Dimension (0, 0); D,feF9  
  public Example (){ } bG#>uE J-  
5j(k:a+!H  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ 5]0 <9a  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ %h@EP[\  
   if (height < 0 || width < 0) &8lZNv8;(p  
    throw new IllegalArgumentException(); e7 o.xR  
    d.height = height; 3w'tH4C[Y  
     d.width = width; K0>zxqY  
  } o+'6`g'8  
0l6.<-f{  
  public synchronized Dimension getValues(){ f:} x7_Q  
   // Ooops! Breaks encapsulation sgFEK[w.y  
   return d; k,*XG$2h  
  } ?@86P|19  
} ~DwpoeYX  
e^voW"?%  
<5051U Eu  
2+XA X:YD  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: <P_-s*b  
WyiQoN'q  
|6- nbj  
2>%=U~5  
Example ex = new Example(); HRA|q  
Dimension d = ex.getValues(); <hyKu  
d.height = -5; GbI/4<)l}  
d.width = -10; 2,b$7xaf  
!nnC3y{G  
> (<f 0  
$& c*'3  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 *.[. {qG(  
Pm7}"D'/  
tw@X> G1z  
PJ#,2=n~  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 ~n_HP_Kf?  
He@KV=  
UN#S;x*  
|G<|F`Cj  
  更好的方式是让getValues()返回拷贝: ccxNbU  
h?U O&(  
i%?*@uj  
P%n>Tg80M  
public synchronized Dimension getValues(){ a<e[e>  
return new Dimension (d.x, d.y); SpBy3wd  
} DEgXQ[  
LghfM"g  
KI.hy2?e  
vY3h3o  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 n@3>6_^rwT  
Q>z8IlJ}  
y~V(aih}D  
*-X[u:  
  三、常见错误3#:不必要的克隆 i|kRK7[6B  
?Bmb' 3  
!4!~L k=  
 bN.Pex  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: Id9TG/H7  
er\|i. Y  
L~3Pm%{@A  
0jfuBj5!  
/*** Example class.The value should never * be negative.*/ 4+tEFxvX&  
public class Example{ ['D]>Ot68  
  private Integer i = new Integer (0); U<XG{<2  
  public Example (){ } "dlV k~  
XjBD{m(  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ 7_t'( /yu  
  public synchronized void setValues (int x) throws IllegalArgumentException{ zQ PQ  
   if (x < 0) #-J>NWdt  
    throw new IllegalArgumentException(); fP1! )po  
    i = new Integer (x); a+QpM*n7Lq  
  } !,PWb3S  
j>kqz>3  
  public synchronized Integer getValue(){ '3;b@g,  
   // We can’t clone Integers so we makea copy this way. q^nVN#  
   return new Integer (i.intValue()); W,u:gzmhw  
  } [Rb+q=z#  
} q3`u1S7Z7  
zuCSj~  
,!9zrYi}  
:!QAC@  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 L/[K"  
V]^$S"Tv  
jEwIn1  
An@t?#4gxi  
  方法getValue()应该被写为: ssL\g`xe  
xSu >  
,r}6iFu  
5V-I1B&  
public synchronized Integer getValue(){ wIgS3K  
// ’i’ is immutable, so it is safe to return it instead of a copy. Bw.i}3UT6  
return i; 4p wH>1  
} -\MG}5?!  
FI.\%x  
d(K +);!  
v[<T]1=LRC  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: Vvo 7C!$z  
6u%&<")4HP  
dN6?c'iN?2  
7p[n  
  ?Boolean qP ,EBE  
   ?Byte '"Nr,vQo  
   ?Character ~ri5zb20  
   ?Class 1~gCtBRM  
   ?Double PY'2h4IL  
   ?Float 2<6UwF  
   ?Integer CJyevMf'  
   ?Long +[ZY:ZQ  
   ?Short xD7]C|8o  
   ?String ?]5qr?W%  
   ?大部分的Exception的子类 OrW  
u? EN  
Y0> @vTUX  
n"8Yv~v*2j  
 四、常见错误4# :自编代码来拷贝数组 EX"yxZ~  
K NOIZj   
n{jGOfc  
[>9is=>o.  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: >mkFV@`  
u&e~1?R  
YkADk9fE  
A}w/OA97RO  
public class Example{ ?A0)L27UE&  
  private int[] copy; sos5Y}  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ >Gu M]qn  
  public void saveCopy (int[] data){ dWW.Y*339  
   copy = new int[data.length]; $Kd>:f=A  
   for (int i = 0; i < copy.length; ++i) |[lKY+26:{  
    copy = data; AFn7uW!9Gw  
  } HKeK<V  
} BLFdHB.$T  
=|9!vzG4  
^^Vg~){4  
d_ CT $  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: VaPG-n>Vf  
eH,or,r  
{)Xy%QV  
&j6erwaT  
void saveCopy (int[] data){ 62u4-}JzF  
  try{ #z42C?V  
   copy = (int[])data.clone(); cb bFw  
  }catch (CloneNotSupportedException e){ d5-qZ{W  
   // Can’t get here. _Ey5n!0:  
  } ,z6~?6m  
} 0`H# '/  
|a@L}m  
hGrdtsH?  
Zd&S@Z  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: ('~LMu_  
[Qr"cR^  
!m$jk2<  
,,TnIouy  
static int[] cloneArray (int[] data){ qP;OaM CX  
  try{ 4K74=r),i  
   return(int[])data.clone(); *ui</+  
  }catch(CloneNotSupportedException e){ 6B-16  
   // Can’t get here. W l4%GB  
  } =V5%+/r+f  
} 5-M-X#(  
'>" 4  
^@]3R QB  
`mqMLo *  
  这样的话,我们的saveCopy看起来就更简洁了: \NC3'G:Ii  
Mihg:  
>3bCTE   
,?3G;-  
void saveCopy (int[] data){ z{>Rc"%\  
  copy = cloneArray ( data); GthYzd:'hJ  
} Ho%CDz z  
Gh$^{  
Zc2PepIg  
0YHFvy)  
  五、常见错误5#:拷贝错误的数据 Dh*n!7lD`  
g&.=2uP  
I@3MO0V^  
e(yh[7p=  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: n`KY9[0U=  
@pxcpXCy  
G&dKY h\  
KSL`W2}  
import java.awt.Dimension; }\LQ3y"[  
/*** Example class. The height and width values should never * be 8ipez/  
negative. */ i9$ Av  
public class Example{ $8FUfJ1@  
  static final public int TOTAL_VALUES = 10; snJ129}A  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; 7o4\oRGV  
  public Example (){ } '<M{)?  
cnLro  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */  3CJwj  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ KTv$  
   if (height < 0 || width < 0) -YE^zzh  
    throw new IllegalArgumentException(); d'2A,B~_*  
    if (d[index] == null) ~5g~;f[4  
     d[index] = new Dimension(); `{Ul!  
     d[index].height = height; YS ][n_  
     d[index].width = width; qWw=8Bq  
  } o(HbGHIP  
  public synchronized Dimension[] getValues() j<x_&1  
   throws CloneNotSupportedException{ pXUSLs  
    return (Dimension[])d.clone(); (#'>(t(4  
  } @@%ataUSBT  
} 9VT;ep  
xkn;,`t^lJ  
Yw9GN2AG  
ry!!9Z>9n  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: W4N{S.#!  
F5Va+z,jg  
j@9T.P1  
Q20 %"&Xp]  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ he4(hX^  
  Dimension[] copy = (Dimension[])d.clone();  )*[3Vq  
  for (int i = 0; i < copy.length; ++i){ BzzTGWq\  
   // NOTE: Dimension isn’t cloneable. :Sma`U&  
   if (d != null) g5yJfRLxp  
    copy = new Dimension (d.height, d.width); ]?*wbxU0  
  } 7 3m1  
  return copy; /o[w4d8  
} Q;u pau  
HV.t6@\};  
O84i;S+-p  
&NWEqBz*2  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: m2o0y++TjW  
]tD]Wx%  
SdWV3  
&o*A {  
public void store (int[] data) throws CloneNotSupportedException{ <q SC#[xu  
  this.data = (int[])data.clone(); OY d !v`<  
  // OK  `]X>V,  
} 1qch]1 ^G  
0mnw{fE8_  
]! dTG  
JO;Uus{?  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: w@b)g  
(?c-iKGc  
pGZ8F  
xU vs:  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ 99S ^f:t  
  this.data = (int[][])data.clone(); // Not OK! w &(ag$p'  
} ,^:.dFH6  
public void rightStore (int[][] data){ . ^u,.  
  // OK! ;I*o@x_  
  this.data = (int[][])data.clone(); Ei|\3Kx  
  for (int i = 0; i < data.length; ++i){ ]q.0!lh+WL  
   if (data != null) Ngwb Q7)  
    this.data = (int[])data.clone(); s>en  
  } xmX 4qtAL  
} /B3iC#?  
G"6 !{4g  
O}P`P'Y|'  
:t[_:3@  
KP"+e:a%  
   六、常见错误6#:检查new 操作的结果是否为null Rv=YFo[B  
Vj-h;rB0z  
74u&%Rj  
<[phnU^ 8  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: sS Mh`4'  
(ZGbh MK  
 <Uur^uB  
y(&Ac[foS}  
Integer i = new Integer (400); 6mE\OS-I  
if (i == null) S 5U;#H  
throw new NullPointerException(); _&x%^&{  
C}X\|J  
#QPjk R|\  
qLCR] _*  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 2|,VqVb  
C+]I@Go'Tk  
-} +[  
u!s2 BC0}N  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 ~@!bsLSMU  
I|OoRq  
 七、常见错误7#:用== 替代.equals R/_&m$ZB  
%C0Dw\A*:  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: B[}6-2<>?C  
H.;Q+A,8^  
\!(zrfP{(  
ZC ?Xqp  
int x = 4; n|hNM?v  
int y = 5; G B^Br6  
if (x == y) 9$Y=orpWxr  
  System.out.println ("Hi"); 83m3OD_y  
// This ’if’ test won’t compile. H::bwn`Vc  
if (x.equals (y)) CAlCDfKW}  
  System.out.println ("Hi"); @d_M@\r=j  
KXrjqqXs  
Z,=1buSz_  
Y@v>FlqI{  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 YQ} o?Q$z  
~rm_vo  
/xQTxh1;K  
NRuNKl.v  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 yVfC-Z   
vX>)je5#  
{I ((p_  
<%^&2UMg  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 *i,%,O96Nz  
Smh,zCc>s  
vI?, 47Hj+  
7^Uv7< pw  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 SJLis"8  
> !JS:5|  
TvM~y\s  
2eogY#  
  八、常见错误8#: 混淆原子操作和非原子操作 [Pp'Ye~K@c  
k+ /6$pI  
46x'I(  
yauvXosX  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: LD?sh"?b  
@iiT<  
_aphkeqd  
xk5 ]^yDp  
public class Example{ #Y! a6h+  
  private int value; // More code here... VUc%4U{Cti  
  public void set (int x){ <b.D&  
   // NOTE: No synchronized keyword B?QIN]  
   this.value = x; s.rm7r@ #  
  } b>W %t  
} R_KH"`q  
$qiya[&G4  
9sP0D  
B~mj 8l4  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: :s,Z<^5a)g  
~u{uZ(~  
SM '|+ d  
0Gk<l{o?^  
public void increment (){ dr(*T  
  // This is effectively two or three instructions: m 5.Zu.  
  // 1) Read current setting of ’value’. v19-./H^ j  
  // 2) Increment that setting. ]'cs.  
  // 3) Write the new setting back. gR**@t=;j  
  ++this.value; DXo|.!P=3  
} #E?4E1bnB  
J,hCvm  
mw!F{pw  
R-:2HRaA  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: ?[AD=rUC  
0sqFF[i  
HQ g^ h  
sK{e*[I>W  
public synchronized void increment (){ 9x8fhAy}4  
  ++this.value; sB</DS  
} XSDpRo  
' %qr.T %  
Ri{=]$  
oRFq @g  
  九、常见错误9#:在catch 块中作清除工作 |>Vb9:q9Po  
ok[i<zl; '  
97]E1j]  
<} .$l  
  一段在catch块中作清除工作的代码如下所示: "g|#B4'e  
NUZl`fu1Z4  
6<]lW  
b-DvW4B  
OutputStream os = null; M+>u/fldV  
try{ UZMd~|  
  os = new OutputStream (); S!UaH>Rh  
  // Do something with os here. 3<!7>]A  
  os.close(); n]9$:aLZ  
}catch (Exception e){ Ey2^?  
  if (os != null) 'V{W-W<  
  os.close(); QY/w  
} zdYjF|  
r" y.KD^  
2:kH[#  
Ie_wHcM<  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: +R&gqja  
NJ<F>3  
Q?vlfZR`8  
(e~Nq  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 2 nCA<&  
6'/ #+,d'  
D^O@'zP=At  
y0#2m6u  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 \85i+q:LuA  
gJXaPJA{  
e= AKD#  
8=l%5r^cq  
  3.close()可能会抛出异常。 wp_0+$?s  
Upe%rC(  
u_enqC3  
b;n[mk  
  上面代码的一个更优版本为: J zl6eo[;  
T[gv0|+  
]DcFySyv  
HtFDlvdy]  
OutputStream os = null; [WmM6UEVS  
try{ zfU{Kd  
  os = new OutputStream (); U/U);frH  
  // Do something with os here. icgfB-1|i  
}finally{ l **X^+=$  
  if (os != null) t_^4`dW`  
   os.close(); )pa]ui\t  
} ~ }P,.QQ  
CTb%(<r  
]G\}k  
AH^/V}9H  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 w<#!h6Y=  
+[VXs~I q  
rp$'L7lrX  
kmW4:EA%  
  十、常见错误10#: 增加不必要的catch 块 Y4-t7UlS;  
J5qZFD  
vaLSH xi  
*w&e\i|7  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 ;u JMG  
7! Nsm  
It(_v  
&yg|t5o  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 V!Uc(  
TOt dUO  
& 21%zPm  
ZVBXx\{s  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: KO [Yi  
2G7Wi!J  
COlqcq'qAu  
*@5@,=d  
try{ 9;{C IMg&  
  // Nifty code here <I?Zk80  
}catch(Exception e){ -RwE%  cr  
  throw e; 0B2t"(&  
}finally{ 0kh6@y3  
  // Cleanup code here %J(:ADu]  
} I9Xuok!0>=  
ye&;(30Oq  
9*g Z-#  
jA1 +x:Wq  
  不必要的catch块被删除后,上面的代码就缩短为: 0J9x9j`&j  
P:c w|Q  
M3\AY30L  
54 T`OE =  
try{ /m1\iM\  
  // Nifty code here uRvP hkqm  
}finally{ k[xSbs'D  
  // Cleanup code here HPl<%%TI  
} pBHRa?Y5  
x5Bk/e'  
SUiOJ[5,  
>:-$+I  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 (`^1Y3&2  
04ui`-c(  
}2jn[${ pr  
@d'j zs  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 e'~3oqSvR  
zhQJy?>'m  
7!1S)dup  
3] Ct6  
  小结 (PL UFT  
m O_af  
cuX)8+  
!$ JT e  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 C%u28|  
KlEpzJ98  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

您目前还是游客,请 登录注册
欢迎提供真实交流,考虑发帖者的感受
认证码:
验证问题:
10+5=?,请输入中文答案:十五