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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 ,3,(/%=k  
3$n O@rOS  
^V?W'~  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 A>Y#-e;<d  
sKniqWi  
sMDHg  
',l}$]y5  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 nl/~7({  
n1Fp$9%  
Mrly(*!U"@  
s;Sv@=\  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 RA3!k&8?#  
!WgVk7aP`  
py%_XL=w,  
^77X?nDz=h  
  一、常见错误1# :多次拷贝字符串 `&/~%>  
zcCGR Ee=  
.[:2M9Rx  
'nP;IuMP  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 |S.;']t+  
ykBq?Vr  
+2Wijrn  
Kq&JvY^  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: %(d0`9  
b Sm*/Q  
R.)w l  
3x3 =ke!  
String s = new String ("Text here"); CZ|h` ";P2  
,cwjieM  
~`!{5:v  
",>,t_J  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: m5kt O^EU  
)^[PW&=W|x  
5 K[MKfT  
CMviR<.  
String temp = "Text here"; K<~J*k<v  
String s = new String (temp); ^w+)A;?W  
.NRSBk  
6[S-%|f  
M0Vs9K=  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: x>eV$UJ  
%?i~`0-:n%  
y25L`b  
nf )y_5y  
String s = "Text here"; ! ~3zp L  
]>H'CM4JR  
c]>LL(R-7)  
:7DXLI|L#?  
  二、常见错误2#: 没有克隆(clone)返回的对象 2NvbQ 3c5  
&+Yoob]P  
Uk<2XGj  
0.!!rq,  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: /;9iDjG  
U%H6jVE  
@; 0t+  
&KP JB"0L  
import java.awt.Dimension; xQ~N1Y2W  
/***Example class.The x and y values should never*be negative.*/ HqoCl  
public class Example{ j@C*kj;-  
  private Dimension d = new Dimension (0, 0); b1Fd]4H3P  
  public Example (){ } ql2O%B.6?  
^Y?Y5`! Q  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ KreF\M%Ke  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ &N:`Rler  
   if (height < 0 || width < 0) 84jA)  
    throw new IllegalArgumentException(); *e%(J$t  
    d.height = height; a1 .+L  
     d.width = width; c!Wj^  
  } t;L7H E@Y  
}KFM8CbS  
  public synchronized Dimension getValues(){ o*Kl`3=]  
   // Ooops! Breaks encapsulation ' T%70)CM~  
   return d; 5KRI}f  
  } ) ={ H  
} D9;s%  
k\A[p\  
P2=u-{?~  
rO7[{<97m  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: Rb l4aB+   
mt*/%>@7R  
+hUz/G+3  
4">C0m;ks  
Example ex = new Example(); aa,^+^J  
Dimension d = ex.getValues(); DxJY{e9  
d.height = -5; >%i]p  
d.width = -10; -cs$E2 -  
Rn5{s3?F~2  
8rpr10;U  
$0P7^4)w:  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 thrv_^A  
;noZmPa  
i f!   
-Ta| qQa  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 |P(8T'  
tde&w=ec  
jzAXC^FS  
Fd1jElt  
  更好的方式是让getValues()返回拷贝: ;QqC c!b  
3R6=C~  
;$/]6@bqB  
;Wedj\Kkp  
public synchronized Dimension getValues(){  [kL`'yi  
return new Dimension (d.x, d.y); z\+Ug9Of  
} 2b^E8+r9  
6![}Jvu>  
lCiRvh1K  
8;5@5Au  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 /nzJ`d  
EN;4EC7tE  
f=T&$tZ<  
%%qg<iO_  
  三、常见错误3#:不必要的克隆 :[!b";pR  
-E>LB\[t)  
E<r<ObeRv`  
cxc-|Xori  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: ?eOw8Rom  
3%YDsd vQx  
s>ohXISB[  
]@ N::!m  
/*** Example class.The value should never * be negative.*/ R7 ^f|/l  
public class Example{ dr^MW?{a\  
  private Integer i = new Integer (0); ]!v\whZ>  
  public Example (){ } oN&U@N/>aU  
^\7GFpc  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ -I.BQ  
  public synchronized void setValues (int x) throws IllegalArgumentException{ \vF*n Z5/  
   if (x < 0) ujh`&GiB+  
    throw new IllegalArgumentException(); I T gzD"d  
    i = new Integer (x); (gjCm0#_%  
  } ?v}S9z  
`P?!2\/  
  public synchronized Integer getValue(){ W ![*0pL  
   // We can’t clone Integers so we makea copy this way. &FY7 D<  
   return new Integer (i.intValue()); }E#1Z\)  
  } h]DzX8r}  
} DT3koci(  
C(&3L[  
9@wmngvM*Y  
T~)R,OA7m  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 )uC5  
FZx.Yuv  
!XQ)>T^G5  
)M_|r2dDq3  
  方法getValue()应该被写为: lD6PKZ\RIj  
lt& c/xi_  
 J7p?9  
0`Y"xN`'i  
public synchronized Integer getValue(){ Y InPmR  
// ’i’ is immutable, so it is safe to return it instead of a copy. s_cur-  
return i; zszx~LSvIT  
} *}=z^;_oq  
RKb (  
iyP0;$  
ZG8Xr "  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: 2q ~y\fe  
S3ZI C\2  
{ZKXT8'  
\?"p]&2UcB  
  ?Boolean cBQ+`DXn5c  
   ?Byte 3 uJ?;  
   ?Character f{)nxd >#  
   ?Class Xp0S  
   ?Double [ps 5  
   ?Float 'Xu3]'m*  
   ?Integer r>g5_"FL  
   ?Long .~fov8  
   ?Short KN".0WU  
   ?String *{dMo,.eI  
   ?大部分的Exception的子类 Y'76!Y  
n a+P|'6  
U3BhoD#f\  
zo:NE0 0  
 四、常见错误4# :自编代码来拷贝数组 G(gZL%M6  
rY295Q  
1!Afq}|  
&{gy{npQ  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: ["sm7yQ  
s f8F h  
_&aPF/  
mb*|$ysPx  
public class Example{ <KFl4A~  
  private int[] copy; :*MR$Jf  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ O3tw@ &k  
  public void saveCopy (int[] data){ *vOk21z77d  
   copy = new int[data.length]; UdcrX`^.  
   for (int i = 0; i < copy.length; ++i) .,\^{.E  
    copy = data; =Y5_@}\0  
  } Nf%/)Tk  
} 7yUX]95y8  
=b#:j:r  
1 Q*AQYVY  
|z?c>.  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: {\gpXVrn_  
=s&ycc;-5}  
rUZ09>nDy  
:Z+J t=;  
void saveCopy (int[] data){ %H4>k#b@$  
  try{ <[?ZpG  
   copy = (int[])data.clone(); 1AQy 8n*  
  }catch (CloneNotSupportedException e){ -|I_aOC@  
   // Can’t get here. ^.c<b_(=h  
  } ef2)k4)"  
} !o\e/HGc!  
lYS*{i1^ '  
t&~*!w!+jH  
kAq#cLprG  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: eV j7%9  
Sl'{rol'  
U#|6n ,  
O]KQ]zN  
static int[] cloneArray (int[] data){ slaH2}$xR  
  try{ ]q/USVj{  
   return(int[])data.clone(); I.it4~]H  
  }catch(CloneNotSupportedException e){ a|z@5r%  
   // Can’t get here. %DM0Z8P$B-  
  } V=<AI.Z:w  
} "[0.a\ d<  
L~C:1VG5  
}=|plz}  
Y|buQQ|  
  这样的话,我们的saveCopy看起来就更简洁了: w-3Lw<  
?in)kL  
<6+T&Ov6  
@hy~H?XN  
void saveCopy (int[] data){ L%+mD$@u  
  copy = cloneArray ( data);  HlEHk'  
} A9MTAm{  
M0$E_*  
ij1YV2v  
V[n,fEPBr  
  五、常见错误5#:拷贝错误的数据 jB`:(5%RO  
wo;OkJKF  
sKk+^.K}|  
3-'3w,  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: j4u ["O3  
=.w~qL  
'V &Tlw|  
i!gS]?*DH  
import java.awt.Dimension; AH_qZTv0{Q  
/*** Example class. The height and width values should never * be XlnSh<e  
negative. */ Mmg~Fn  
public class Example{ BGHZL~  
  static final public int TOTAL_VALUES = 10; P](8Qrl  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; 20}w . V  
  public Example (){ } <@Vf:`a!P>  
)sNPWn8<Uy  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ %NM={X|'  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ |>Fz:b d  
   if (height < 0 || width < 0) %k~ezn  
    throw new IllegalArgumentException(); 4c[/%e:\-  
    if (d[index] == null) jw`05rw:  
     d[index] = new Dimension(); PQa0m)H@  
     d[index].height = height; crN*eFeW  
     d[index].width = width; noM=8C&U  
  } pb)kN%  
  public synchronized Dimension[] getValues() tP"6H-)X&  
   throws CloneNotSupportedException{ SN]g4}K-  
    return (Dimension[])d.clone(); &AWrM{e  
  } +vxOCN4}v  
} x/wgD'?  
Z4rk$K'=1w  
Cs y,3XG  
+I3O/=)  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: \P")Eh =d  
<i]0EE}%  
^;rjs|`K#  
S[zGA<}  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ ?$T^L"~  
  Dimension[] copy = (Dimension[])d.clone(); vhAgX0k  
  for (int i = 0; i < copy.length; ++i){ "j8)l4}  
   // NOTE: Dimension isn’t cloneable. H%gAgXHn  
   if (d != null) m C`*#[  
    copy = new Dimension (d.height, d.width); M=lU`Sm  
  } wDGb h=  
  return copy; }z$_!)/i  
} 0y+^{@lU  
y_w  <3  
*YL86R+U  
'Grii,  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: 6ulx0$[  
6 /5,n0  
xr7}@rq"U<  
Sk/@w[  
public void store (int[] data) throws CloneNotSupportedException{ 2l+L96  
  this.data = (int[])data.clone(); `[g$EXX  
  // OK Nw$OJ9$L>  
} [rWBVfm  
, ?U)mYhI  
4=/jh:h  
P.C?/7$7Z+  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: 1n[)({OQ  
yxHo0U  
vI0,6fOd6  
wbrOL(q.m  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ z k/`Uz  
  this.data = (int[][])data.clone(); // Not OK! +pwTM]bV  
} \t&! &R#  
public void rightStore (int[][] data){ _.Hj:nFHz  
  // OK! Um{) ?1  
  this.data = (int[][])data.clone(); sTl^j gV7j  
  for (int i = 0; i < data.length; ++i){ $"W[e"Q  
   if (data != null) 6qFzo1LO  
    this.data = (int[])data.clone(); 8wvHg_U6W  
  } @n?"*B  
} gv>DOez/  
<O0tg[ub  
  8sG?|u  
bE.<vF&  
Ig}hap]G  
   六、常见错误6#:检查new 操作的结果是否为null  x _>1x#  
r=0PW_r:  
t!Cz;ajNi  
hdx_Tduue  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: iL);bv W  
6E-eD\?I&  
slPr^)  
,HTwEq>-G  
Integer i = new Integer (400); @i!+Z  
if (i == null) 2PyuM=(Wt  
throw new NullPointerException(); bWp:!w#K  
yO7y`;Q(sF  
9)v]jk  
Es7 c2YdU  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 IC'+{3.m8  
\O"H#gt  
e8("G[P >  
F$tzsz,9n  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 hGY-d}npAJ  
oQ r.cKD ?  
 七、常见错误7#:用== 替代.equals / ^d9At614  
8QJr!#u  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: JseKqJ?g  
#;a+)~3*O  
E/oLE^yL  
ai$l7]7  
int x = 4; [k\VUg:P  
int y = 5; j[:70%X  
if (x == y) DSRc4 |L  
  System.out.println ("Hi"); W`9{RZ'  
// This ’if’ test won’t compile. ,dQ*0XO!  
if (x.equals (y)) \-]Jm[]^  
  System.out.println ("Hi"); I2CI9,0  
^2$b8]q  
fDns r" T  
/} PdO  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 /ojwOJ  
9D\E0YG X/  
.jqil0#)Y"  
4h!yh2c..  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 33lh~+C  
jm<^WQ%Cc  
]@z!r2[  
L@[}sMdq(  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 B"> Ko3  
t=Rl`1 =(K  
s-Gd{=%/q  
o'$-  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 GPh;r7xg6  
n-M6~   
&&<l}E  
U~yPQ8jD  
  八、常见错误8#: 混淆原子操作和非原子操作 t&43)TPb.  
sYXLVJ>b  
<ndY6n3  
Z<d=v3q  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: \Kui`X  
Z:h'kgG&  
C~>0K,C0^  
sSKD"  
public class Example{ NKFeND  
  private int value; // More code here... /<T{g0s  
  public void set (int x){ u{uqK7]+  
   // NOTE: No synchronized keyword Xl<*Fn?  
   this.value = x; y]R+/  
  } :_HdOm  
} `j'1V1  
3dZj<(.  
R!"`Po  
/: -&b#+  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: *=O3kUoL  
>u0XV"g$  
M).CyY;bm  
'?I3&lYz{  
public void increment (){ 8]#J_|A6Z  
  // This is effectively two or three instructions: MR4k#{:w  
  // 1) Read current setting of ’value’. FnOa hLS  
  // 2) Increment that setting. 3e-E/6zH6  
  // 3) Write the new setting back. .*"KCQGOgM  
  ++this.value; 2Gj)fMK38  
} 8yOhKEPX  
=_N $0  
#Ddo` >`&  
`l70i2xcj  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: H;AMRL o4z  
|eye) E:  
/Wl8Jf7'  
dqgr98  
public synchronized void increment (){ )Xt#coagS  
  ++this.value; 2*gB~Jn4  
} j-i>Jd7  
}=gD,]2x8  
I;?PDhDb  
mL#$8wUdt{  
  九、常见错误9#:在catch 块中作清除工作 5~[][VV^  
B=r+ m;(  
qv:DpK  
i3L2N~:V  
  一段在catch块中作清除工作的代码如下所示: 9`H4"H>yG  
K0I.3| 6C  
~(Q#G" t  
v/vPU  
OutputStream os = null; G~_D'o<r  
try{ f"AT@Ga]  
  os = new OutputStream (); 8y[Rwa  
  // Do something with os here. m' |wlI[lq  
  os.close(); <4zSh3  
}catch (Exception e){ 4OAR ["f  
  if (os != null) @1bl<27  
  os.close(); W|sU[dxZ  
} ~GJ;;v1b2  
U ){4W0  
![I|hB  
m9D Tz$S.  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: 6p&uifY}tR  
#IDLfQ5g  
3'0Jn6(  
g2M1zRm;  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 23'{{@30  
]!jfrj  
;"RyHow  
:hWG:`  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 *xj2Z,u  
\O,yWyU4  
bTAY5\wB  
-H|!KnR  
  3.close()可能会抛出异常。 4v{Ye,2  
2FO<Z %Y  
J*6B~)Sp@  
$ )2zz>4  
  上面代码的一个更优版本为: !tx.2m*5  
x'6i9]+r  
]n:R#55A  
WYcZD_  
OutputStream os = null; m0^~VK|  
try{ J{XRltI+  
  os = new OutputStream (); Zz 'g&ewo  
  // Do something with os here. z?UEn#E2  
}finally{ J'WOqAnPZ  
  if (os != null) v v5rA 6+  
   os.close(); WqCj;Tj|  
} LFYSur8  
*UG?I|l|I  
1C*mR%Q  
k!WeE#"(  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 G"Ey%Q2K  
I9#l2<DYlX  
Q?`s4P)14o  
muFWFq&yP  
  十、常见错误10#: 增加不必要的catch 块 #%#N.tB 5  
sP=^5K`g  
-0NkAQrg  
|}X[Yg=FG  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 A ;|P\V  
Aq>?G+  
E4_,EeC#  
x7jFYC  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 REsw=P!b  
:vaVghN\  
7lwFxP5QT  
#60gjHYaV  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: ;W\?lGOs{  
''z]o#=^9  
1jPh0?BY  
N'pYz0_H  
try{ VT#`l0I }  
  // Nifty code here G2P:|R  
}catch(Exception e){ W}%[i+  
  throw e; G)o:R iq  
}finally{ {/48n83n  
  // Cleanup code here Z:2%gU&W  
} <;1M!.)5  
3Jj&wHp]  
qc' ;<  
VAqZ`y  
  不必要的catch块被删除后,上面的代码就缩短为: zpzxCzU  
)YwLj&e4tf  
Ya!PV&"Z  
g{K \  
try{ g{kjd2  
  // Nifty code here xNLgcb@v>  
}finally{ Gj[5e w?@  
  // Cleanup code here WB `h)  
} ,TYFPulYcp  
LE?sAN  
Ev3,p`zS._  
> -(Zx  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 M(^ e)7a1  
DH4IF i>  
,VzbKx,  
,F "P/`i'  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 8VC%4+.FF  
T!^v^m@>y  
"x 3C3Zu.;  
h&~9?B  
  小结 ,zZ@QW5  
^nDa-J$  
:0bjPQj  
<5G 4|l  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 N0 mh gEA  
E;q+u[$  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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