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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 E">FH >8K}  
_FR_6*C)5  
P|*c7+q  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 C@1B?OfJ  
K%iWUl;  
B|XrjI?  
wyJ+~  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 jrk48z  
jkTC/9AE|  
Zawnx=  
nI]8w6eCV  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 ]wb^5H  
e!k1GTH^  
Uq/FH@E=  
wX<w)@  
  一、常见错误1# :多次拷贝字符串 [QwEidX|  
)B'&XLK  
i7D[5!  
wr>[Eo@%\  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 ?i'N 9 /(  
F#NuZ'U  
4:wVT;?a  
v_^>*Vm*  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: U1nObA  
&GYnGrw?@  
%x{jmZ$}  
b$FK}D5  
String s = new String ("Text here"); F/p/&9  
)<YfLDgTs  
6.5E d-  
s R/z)U_  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: O?,Grn%'.  
Pa)'xfQ$Y6  
o0ky]9 P  
5?l8;xe`{f  
String temp = "Text here"; 9B3+$uP  
String s = new String (temp); tBU n KPT  
ak1?MKV.  
|Yb]@9 >vn  
4, EX2  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: ^Mvgm3hg  
6sjd:~J:  
cvOCBg38BH  
' _ZiZ4O  
String s = "Text here"; T8^`<gr.  
"t0l)P*C}  
2nra@  
UA4="/  
  二、常见错误2#: 没有克隆(clone)返回的对象 Z-%zR'-?*  
POXd,ON9  
xQUskjv/  
A4{14Y;?  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: ) KvGJo)("  
==#mlpi`S[  
u~c75Mk_v  
P*6h $T  
import java.awt.Dimension; B<$(Nb5<  
/***Example class.The x and y values should never*be negative.*/ ~#MXhhqB  
public class Example{ 6+ UTEw;  
  private Dimension d = new Dimension (0, 0); ^=Dz)95c  
  public Example (){ } LO;7NK  
)B*D\9\Z  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ Q6PaT@gs  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ je;C}4  
   if (height < 0 || width < 0) qt{lZ_$  
    throw new IllegalArgumentException(); )WNw0cV}J>  
    d.height = height; o[^nmHrM2  
     d.width = width; ~Vt?'v20@  
  } %fuV]  
/6 y9 u}  
  public synchronized Dimension getValues(){ F:7 d}Jx  
   // Ooops! Breaks encapsulation '2z1$zst,#  
   return d; ^V}c8 P|  
  } @ / .w%  
} Y;)l  
G!)Q"+  
f/95}6M  
c/lT S  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: T{So 2@_&  
yQcIfl]f  
1SF8D`3  
0fJz[;dV>n  
Example ex = new Example(); "|Gr3sD  
Dimension d = ex.getValues(); Np"~1z.(b  
d.height = -5; A('o &H  
d.width = -10; 3*zywcTH  
Lm8uN?  
D wfw|h  
v#|yr<  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 ?zuKVi? I  
sTS/ ]"l  
D_q"|D$SB  
~2;\)/E\  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 ^ItL_ 4  
!aB~G}'  
B ({g|}|G+  
;I9g;}  
  更好的方式是让getValues()返回拷贝: 5<XWbGW  
s^>  >]  
WES$B7y  
kBU`Q{.  
public synchronized Dimension getValues(){ S2jn  pf}  
return new Dimension (d.x, d.y); ) 7C+hQe  
} W m&*  
0`/CoP<U  
]f~!Qk!I7r  
dv Vz#  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 <v6W l\  
]JR2Av  
1'!D   
:b0|v`FU  
  三、常见错误3#:不必要的克隆 .?`8B9w  
@#=yC.s  
NTo[di\_  
Tb:6IC7="  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: ~ o=kW2Y  
U7''; w  
LAFxeo  
-^Qm_lN  
/*** Example class.The value should never * be negative.*/ "$/1.SX;]  
public class Example{ V x{   
  private Integer i = new Integer (0); |>RNIJ]  
  public Example (){ } Jot7 L%,TB  
Bg[_MDWc-P  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ J4x|Afp  
  public synchronized void setValues (int x) throws IllegalArgumentException{ hSz_e  
   if (x < 0) nAC>']K4$  
    throw new IllegalArgumentException(); mp)+wZAN&  
    i = new Integer (x); lc3N i<3v  
  } a!EW[|[Q  
;t M  
  public synchronized Integer getValue(){ U[?f@.&  
   // We can’t clone Integers so we makea copy this way. $>7T s>8  
   return new Integer (i.intValue()); )5NWUuH 5  
  } ^(s(4|  
} erKi*GssZ  
O!t=,F1j  
Ih N^*P:Fo  
lMl'+ yy  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 zGdYk-H3TH  
/'/i?9:  
t3AmXx  
nu)YN1 *  
  方法getValue()应该被写为: 6L;]5)#  
*aJO5&w<T  
p~NHf\  
][KlEE>W2  
public synchronized Integer getValue(){ (_]!}N  
// ’i’ is immutable, so it is safe to return it instead of a copy. _e/Bg~  
return i; { 1_ <\ ~J  
} YG /@=Z.  
n.i 8?:  
{V pk o  
mo+!79&  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: uq/Fapl  
l<p<\,nV$  
##%&*vh  
cF_`QRtO  
  ?Boolean artn _  
   ?Byte dz^b(q  
   ?Character + MD84YR  
   ?Class p6aR/gFkqv  
   ?Double 7n?yf_ je  
   ?Float Z- t&AH  
   ?Integer t3!OqM  
   ?Long -k@1# c+z  
   ?Short V;W{pd-I  
   ?String oHr0;4Lg6  
   ?大部分的Exception的子类 , @!X! L  
VR .t  
XUKlgl!+.  
>28l9U  
 四、常见错误4# :自编代码来拷贝数组 "h #/b}/  
w3 kkam"  
A*vuSQt(  
mP=[h |a$r  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: xjSzQ| k-  
lT*@f39~g  
LuHRB}W  
;aj;(Z.p)  
public class Example{ Alo L+eN@  
  private int[] copy; ^_i)XdPU  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ b;{"@b,Y  
  public void saveCopy (int[] data){ `N&*+!O%  
   copy = new int[data.length]; $2,tT;50g  
   for (int i = 0; i < copy.length; ++i) LR{bNV[i  
    copy = data; 0}"\3EdAbD  
  } W9pY=9]p+  
} 1C<d^D_!p  
V0rQtxE{F  
1Y&W>p  
-EE'xh-zD  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: `U b*rOMu  
W~2,J4=  
M^Y[Y@U=p  
D(;+my2  
void saveCopy (int[] data){ C #iZAR  
  try{ 2Wu`Dp;&l  
   copy = (int[])data.clone(); [\#ANA"  
  }catch (CloneNotSupportedException e){ G0|}s&$yL  
   // Can’t get here. R<Mc+{*>  
  } %8 D>aS U  
} `^,E4Qy  
oH+PlL  
XI ;] c5  
8nu!5 3  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: Pc=ei  
0O,;[l  
!mTq6H12 !  
LSA6*Q51  
static int[] cloneArray (int[] data){ !'~Ldl  
  try{ 6r`N\ :18  
   return(int[])data.clone(); FZn1$_Svr  
  }catch(CloneNotSupportedException e){  ?ueL'4Mm  
   // Can’t get here. ju'a Uzn  
  } j6EF0/_|e  
} >zDQt7+g;  
CuH4~6  
< K!r\^  
AWi>(wk<  
  这样的话,我们的saveCopy看起来就更简洁了: c+E\e]{  
T7 "QwA  
Sir1>YEm  
fv#ov+B  
void saveCopy (int[] data){ " acI:cl?,  
  copy = cloneArray ( data); 8b.k*,r>  
} P8}IDQ9  
k}F7Jw#.  
;Z"MO@9:  
R.*;] R>M  
  五、常见错误5#:拷贝错误的数据 <W!nlh  
2I}+AW!!=  
=.;ib6M  
Za1mI^ L1  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: [ i, [^  
z/`+jIB  
l^ay* H  
?8{Os;!je  
import java.awt.Dimension; x'|9A?ez@Z  
/*** Example class. The height and width values should never * be .`m|Uf#" _  
negative. */ $x`HmL3Sb  
public class Example{ !L{mE&  
  static final public int TOTAL_VALUES = 10; MKvmzLh$)  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; O5vfcX4>  
  public Example (){ } wkGr}  
Iy49o!  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ i8k} B o  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ fMFkA(Of^  
   if (height < 0 || width < 0) &"JC8  
    throw new IllegalArgumentException(); yQUrHxm  
    if (d[index] == null) jvsSP?]n  
     d[index] = new Dimension(); Be]z @E1x  
     d[index].height = height; & ;.rPU  
     d[index].width = width; $)"T9 $>$  
  } p@% Pdx  
  public synchronized Dimension[] getValues() j@(S7=^C6%  
   throws CloneNotSupportedException{ 5hy7} *dR  
    return (Dimension[])d.clone(); NZv8#  
  } BHAFO E  
} S$hxR  
I+;e#v,%U  
(E@;~7L  
DvQV_D  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: J.:  
lqv}~MC  
Q2Ey RFT  
#K:iB*  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ 1="]'!2Is  
  Dimension[] copy = (Dimension[])d.clone(); fqbeO9x  
  for (int i = 0; i < copy.length; ++i){ )cRHt:  
   // NOTE: Dimension isn’t cloneable. :FC)+OmJ  
   if (d != null) hNZ_= <D!  
    copy = new Dimension (d.height, d.width); 53:u6bb;  
  } N*|EfI|X  
  return copy; USN'-Ah  
} o g9|}E>  
?>*d82yO  
ty~Sf-Pri  
-M6vg4gf  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: EiC["M'}  
g]HxPq+O  
A\rY~$Vr  
T_c`=3aO  
public void store (int[] data) throws CloneNotSupportedException{ iUh7eR9  
  this.data = (int[])data.clone(); D9NRM;v  
  // OK  +qj Z;5(  
} vb0Ca+}}  
nRqP_*]  
ym6Emf]  
Q+K]:c  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: 85io %>&0  
,B/TqPP  
~h8k4eM  
,Aq, f$5V  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ J+ :3== ,  
  this.data = (int[][])data.clone(); // Not OK! 6Zw$F3 <  
} u;^H=7R  
public void rightStore (int[][] data){ 2N &B  
  // OK! }])j>E  
  this.data = (int[][])data.clone(); V?JmIor  
  for (int i = 0; i < data.length; ++i){ Pfvb?Hy  
   if (data != null) uv$5MwKU  
    this.data = (int[])data.clone(); M^ WoV }'  
  } |n,O!29  
} i=b'_SZ '  
"[["naa  
9mMQ  
B &Z0ZWx  
=r]_$r%gR  
   六、常见错误6#:检查new 操作的结果是否为null !K*3bY`#  
ZT&[:>upR  
Uhh[le2 %  
j^ 8Hjg  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: 7SkW!5  
0W6= '7  
x?{l<mc  
lxXF8c>U  
Integer i = new Integer (400); 5C`Vno~v  
if (i == null) ',FVT4OMw  
throw new NullPointerException(); SP2";,%/9  
;+f(1=x  
j/uMSE  
epk C '  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 L~|_)4  
.ejC#vB{KM  
t9W*N\  
fF/;BSq'  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 8j&1qJx)  
U .^%7.  
 七、常见错误7#:用== 替代.equals Q"pZPpl&  
-y&>&D  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: 0""%@X]m  
4yxf/X)  
!&KE">3Qu  
!h1|B7N  
int x = 4; =hh,yi  
int y = 5; t2.]v><  
if (x == y) {|zQ .s A  
  System.out.println ("Hi"); u?g;fh6  
// This ’if’ test won’t compile. Gqar5  
if (x.equals (y)) %7TG>tc  
  System.out.println ("Hi"); b7M)  
1?p:66WmR  
ABtv|0K  
gY-}!9kW]  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 JKYl  
R^ I4_ZA  
]Ah<kq2sk  
&s.-p_4w^D  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 r)qow.+&  
$I4J Kh  
J(,gLl  
}`$({\^w  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 XHuHbriI  
z*^vdi0  
Y5IQhV.  
Y-DHW/Z~  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 $*0XWrE  
rJd-e96  
tN;~.\TKg  
[ dVRVm0N  
  八、常见错误8#: 混淆原子操作和非原子操作 NTM.Vj -_h  
Wc##.qU  
[QT H~  
UUgc>   
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: ;2eZa|M*q  
PTA_erU  
vN)l3  
Kzfy0LWM  
public class Example{  #|l#  
  private int value; // More code here... -S $Y0FDV  
  public void set (int x){ g7LS  
   // NOTE: No synchronized keyword 7tT L,Nxe  
   this.value = x; wAF#N1-k  
  } r$d'[ZcX  
} 6CWm;%B#G  
{1wjIo"ptg  
g>f_'7F&  
H]f8W]"c[  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: M059"X="  
/@s(8{;  
Q S.w#"X[  
xb(y15R\I  
public void increment (){ iJ`v3PP  
  // This is effectively two or three instructions: llBW*4'  
  // 1) Read current setting of ’value’. 24_/JDz  
  // 2) Increment that setting. tj!~7lo  
  // 3) Write the new setting back. _ <pO<S  
  ++this.value; M*jn8OE  
} 1QuR7p  
v|r#  
klC48l  
ivl_=  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: UazUr=| e  
<Dp[F|r  
Nf{tC9l  
mt3j$r{_  
public synchronized void increment (){ }&*,!ES*  
  ++this.value; yYZ0o.<&T*  
} ]u O|YLWp  
<NX6m|DD  
M$GZK'%  
Jp`qE  
  九、常见错误9#:在catch 块中作清除工作 <Okl.Iz>  
ji|tc9#6  
v4x1=E  
V IU4QEW`x  
  一段在catch块中作清除工作的代码如下所示: RV+0C&0ff  
`zRm "G  
> 1&_-  
lMBXD?,,J  
OutputStream os = null; _NJq%-,'  
try{ . !;K5U  
  os = new OutputStream (); 7 S2QTRvH  
  // Do something with os here. +~\c1|f  
  os.close(); IOOAaa @(  
}catch (Exception e){ A4|a{\|$  
  if (os != null) .Cf`D tK  
  os.close(); {B lM<  
} G^Yg[*bJ^$  
z@em1W0?Z  
! .|\}=[e  
yY42+%P  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: ZiOL7#QWX  
|;_uN q9  
1 9C=' TMS  
dg]: JU  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 [8Zvs=1  
ep2#a#&'  
t<2B3&o1  
eE-@dU?  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 $]yHk  
'hi.$G_R  
=m?x|Zc_v  
9 nPc>O$  
  3.close()可能会抛出异常。 ^.@BD4/RPt  
hzjEO2  
564)ha/^(  
V<;w  
  上面代码的一个更优版本为: r/vRaOg>X  
iv/!c Mb  
noa =wy  
]2P*Z6Az  
OutputStream os = null; L.@o  
try{ .-g++f(_i  
  os = new OutputStream (); #{kwl|c   
  // Do something with os here. yqw#= fy  
}finally{ Zxwcj(d  
  if (os != null) IaLCWvHX  
   os.close(); #A2)]XvY  
} jQiK of>  
tb-:9*2j-  
AG$S;)Yl9c  
]dKLzW:l  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 ' 4nR^,  
eD4o8[s  
n_/_Y >{M0  
 hVB^:  
  十、常见错误10#: 增加不必要的catch 块 P+~{q.|._c  
vA*Ud;%R  
~)JNevLZ  
O+o1R24JI  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 VS lIeZ  
?x(]U+  
F#w= z/  
&O5W  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 @sAT#[j  
E$'Zd,|f=  
Sb&[V>!2^  
#;32(II  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: ']hB_ 4v  
 Wb/q&o  
U]Vu8$W  
[BpIzhy&}  
try{ L+&eY?A  
  // Nifty code here OXs-gC{b  
}catch(Exception e){ c.u$NnDU6  
  throw e; s3*h=5bX=  
}finally{ W~J>Srt  
  // Cleanup code here x05yU  
}  H)),~<s  
%/o8-N|_[  
 4_E{  
/^kZ}}9baU  
  不必要的catch块被删除后,上面的代码就缩短为: .'q0*Pe  
32r2<QrX  
dcgz<m  
>+w(%;i;  
try{ ,3t('SE  
  // Nifty code here 8()L}@y  
}finally{ hDp -,ag{  
  // Cleanup code here JwNG`M Gc  
} K>2mm!{  
8]N  
q89#Ftkt  
ztNm,1pnQ  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 `43`*=  
 Sxrbhnx  
4,!S?:7  
G H N  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 meHAa`  
aD aQ 7i  
0B^0,d(s  
CF`tNA3fxm  
  小结 Lzzf`jN]  
;hz"`{(JY  
<|_/i/H  
L {6y]t7^  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 z:hY{/-  
xHv<pza:  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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