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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 k_%"#  
x9B{|+tIoc  
u'Ua ++a\  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 Hwm] l`E]  
f6-OR]R5  
8<Yqpb  
;p4|M  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 .q7|z3@,  
2W 9N-t2 1  
%nkP?gn"a  
5 U{}A\q  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 A ^wIsAxT  
gx)!0n;  
Y$ To)qo  
D 4fHNk)kZ  
  一、常见错误1# :多次拷贝字符串 .q^+llM  
}Kc03Ue`%e  
%RF9R"t$  
U4hFPK<  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 [ym ynr3M  
+)eI8o0#  
5bKm)|4z6  
 "0( _  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: K_X10/#b&  
W~e/3#R\=  
[Q5>4WY  
6+hx64 =  
String s = new String ("Text here"); } JePEmj  
2!+saf^-,  
fxKhe[;  
L)`SNN\ipR  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: +jPs0?}s  
3h-C&C  
<.yL&$9  
A=S_5y  
String temp = "Text here"; .0b$mSV[  
String s = new String (temp); [aF^D;o  
O4mSr{HCp  
bm+ #OI  
'4""Gz  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: L(bYG0ZI5C  
r#xq 8H=_m  
; n)9  
`?fY!5BA  
String s = "Text here"; n7r )wy  
.(7 end<  
ph;ds+b  
~x:B@Ow  
  二、常见错误2#: 没有克隆(clone)返回的对象 6/Pw'4H9$  
"?<$>\@; q  
(rM-~h6g  
|H LU5=Y  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: !BocF<UE  
KG! W,tB  
E mUA38  
Sas &P:# r  
import java.awt.Dimension; |NsrO8H   
/***Example class.The x and y values should never*be negative.*/ jC;^ 2e  
public class Example{ rX-V0  
  private Dimension d = new Dimension (0, 0); HX(Z(rcI  
  public Example (){ } &ZmHR^Flz  
{g%F 3-  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ L1'#wH  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ R*v~jR/   
   if (height < 0 || width < 0)  t dl Y  
    throw new IllegalArgumentException(); 0gs0[@  
    d.height = height; |)7dh B  
     d.width = width; nMniHB'  
  } Hkcr+BQ  
>8pmClVvmR  
  public synchronized Dimension getValues(){ Zr&~gXmVS  
   // Ooops! Breaks encapsulation y2>XLELy  
   return d; Bbb_}y|CA  
  } 8b^v@|)N  
} L7_qs+  
)<.BN p  
HW{si]~q  
,r&:C48 dI  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: "I)*W8wTn  
aetK<9L$  
v-V#?+#  
DPU%4te  
Example ex = new Example(); 5BL4VGwJ  
Dimension d = ex.getValues(); ^* xhbM;  
d.height = -5; M!{;:m28X!  
d.width = -10; <$f7&6B  
; W/K7}  
(-RZ|VdYg  
4oY<O  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 'w'P rM,:  
pwiXA{  
986y\9Zu  
Po%+:0oX  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 (BB&ZUdyv  
h83;}>  
N W/RQ(  
kl0!*j  
  更好的方式是让getValues()返回拷贝: X-tc Ud  
;"*\R5 a  
-QUr|:SK:  
B,@<60u  
public synchronized Dimension getValues(){ : MOr?"  
return new Dimension (d.x, d.y); CXb-{|I}d  
} W[5a'}OV  
tD G[}j  
EJdl%j  
{,Y?+F  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 (<= &#e?  
Km5#$IiP;  
c$cb2V7,  
9WXJz;  
  三、常见错误3#:不必要的克隆 ^ -s'Ad3  
aV^wTs#2I  
Hs%;uyI@$  
Ueq*R(9>  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: + _=&7  
l?F&I.{J  
[ n[!RddY  
uQ^r1 $#  
/*** Example class.The value should never * be negative.*/ rf2+~B{$,  
public class Example{ mSn>  
  private Integer i = new Integer (0); z9@Tg= #i  
  public Example (){ } V3<baxdE  
.hYrE5\-  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ ?tWcx;h:>  
  public synchronized void setValues (int x) throws IllegalArgumentException{ NVEjUt/  
   if (x < 0) J3oEN'8S  
    throw new IllegalArgumentException(); 8PQn=k9  
    i = new Integer (x); +*dG 'U6  
  } k4|9'V&1*6  
()< E?D=  
  public synchronized Integer getValue(){ jOJ$QT  
   // We can’t clone Integers so we makea copy this way. }GIwYh/  
   return new Integer (i.intValue()); )7U^&I,  
  } v/n4Lp$W^  
} %SKp<>;9  
9:|z^r  
XcOfQ s  
"}_b,5lkGK  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 .ZuRH_pI  
Ls8@@b,t2  
:]EAlaB4Q  
up[9L|  
  方法getValue()应该被写为: oF1{/ERS  
r-M:YB  
k)Y}X)\36  
/.Yf&2X\  
public synchronized Integer getValue(){ `6`p~  
// ’i’ is immutable, so it is safe to return it instead of a copy. hf6=`M}>i  
return i; l^.d 3b  
} Dj Z;LE>  
8(yZX4OH>  
j]-0m4QF  
?gknJ:  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: VrV )qfG  
W'aZw9  
~7!=<MW  
42`%D  
  ?Boolean {M`yYeo  
   ?Byte 6f"jl  
   ?Character _|f1q  
   ?Class &|/@;EA$8  
   ?Double Ttr)e:  
   ?Float AYhWeI+  
   ?Integer ]} D^?g^  
   ?Long p`/"e<TP  
   ?Short kF%EJuu  
   ?String [}xVz"8V  
   ?大部分的Exception的子类 {1 VHz])I  
@vsgmz  
[v@3|@  
bjn: e!}  
 四、常见错误4# :自编代码来拷贝数组 W<f-  
k|C~qe3E  
eAU0 8gM.  
wM$N#K@  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: '}4z=f`}  
a ~s:f5S>  
jL9g.q4^  
zKh^BwhO|X  
public class Example{ L/ L#[  
  private int[] copy; s$%t*T2J>  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ {Ad4H[]|]  
  public void saveCopy (int[] data){ nt 9LBea  
   copy = new int[data.length]; BiFU3FlTf  
   for (int i = 0; i < copy.length; ++i) *~h@KQm7  
    copy = data; z3n273W>6  
  } ae%Bl[  
} jHPkfwfAF  
oI\ Lepl*  
,6J{-Iu  
~Q+E""  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: lEZODc+%Y  
WGmXq.  
:d AC:h  
c-GS:'J{  
void saveCopy (int[] data){ JXx[e  
  try{ ;[qA?<GJ  
   copy = (int[])data.clone(); &i(\g7%U  
  }catch (CloneNotSupportedException e){ *VUD!`F  
   // Can’t get here. Vn=K5nm  
  } 539f B,  
} w"Q/ 6#!K  
x?"+Or.h  
T`zUgZ]  
yG' 5:  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: WMw|lV r  
.4[\%r\i  
&Z3u(Eb  
}'KHF0   
static int[] cloneArray (int[] data){ `i `F$;  
  try{ ^)nIf)9}7  
   return(int[])data.clone(); g[c_rty  
  }catch(CloneNotSupportedException e){ h5o6G1ur  
   // Can’t get here. YHp]O+c  
  } N,3 )`Vm  
} tLE7s_^  
JBI>D1`"  
)KdEl9o  
yC:C  
  这样的话,我们的saveCopy看起来就更简洁了: _x`oab0@  
, 3&D A  
D7lRZb  
: GdLr  
void saveCopy (int[] data){ Z ~3  
  copy = cloneArray ( data); shZEE2Dr  
} #rI4\K  
O<`N0  
;%Zu[G`C  
f q&(&(|  
  五、常见错误5#:拷贝错误的数据 uj~(r=%  
hnnVp_<]  
Ods~tM  
df)S}}#H  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: ="('  #o  
5v[2R.eT-  
X/f?=U  
O~OM.:al&  
import java.awt.Dimension; WkMB  
/*** Example class. The height and width values should never * be l+#uQo6cqQ  
negative. */ W_L;^5Y;m  
public class Example{ 'j9x(T1M1  
  static final public int TOTAL_VALUES = 10; <u}[_  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; -KL5sK  
  public Example (){ } {U)q)  
V4ybrUWK  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ X?$"dqA  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ >aAsUL5W  
   if (height < 0 || width < 0) XI>HC'.0  
    throw new IllegalArgumentException(); ^@ Xzh:  
    if (d[index] == null) n >@Qx$-  
     d[index] = new Dimension(); cS%;JV>C  
     d[index].height = height; 6(/*E=bOKV  
     d[index].width = width; gs(ZJO1 /L  
  } QT4&Ix,4T1  
  public synchronized Dimension[] getValues() he|.Ow  
   throws CloneNotSupportedException{ N (0%C?  
    return (Dimension[])d.clone(); R9-Uoc/  
  } @6Z6@Pq(xQ  
} /pWKV>tjj  
;TL>{"z`x  
1b<[/g9  
hO2W!68  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: BUUc9&f3o  
K$wxiGg8P  
qS?^(Vt|R  
qb$M.-\ne  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ s)#TT9BbV  
  Dimension[] copy = (Dimension[])d.clone(); L\q-Z..  
  for (int i = 0; i < copy.length; ++i){ p@eW*tE  
   // NOTE: Dimension isn’t cloneable.  = Atyy  
   if (d != null) A&{eC C  
    copy = new Dimension (d.height, d.width); ,+v>(h>q  
  } N j:W6? A  
  return copy; ) 3ZkKv;zY  
} )O8w'4P5  
l6N"{iXU  
Fr#QM0--B  
z8j7K'vV1  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: eC@b-q   
/k KVIlO  
|=[. _VH1  
jP.dQj^j&  
public void store (int[] data) throws CloneNotSupportedException{ Qp-P[Tc  
  this.data = (int[])data.clone(); \"9ysePI  
  // OK 71Y3.1+  
} A3|X`X  
%>1C ($^  
2]Ei4%jo  
k/i&e~! \  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: cA~bH 6  
jpZ 7p ;  
:Aj[#4-=   
(Ft#6oK"  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ 91UC>]}H  
  this.data = (int[][])data.clone(); // Not OK! _pkmHj(  
} lr=quWDY  
public void rightStore (int[][] data){ ]'<"qY  
  // OK! 9 u>X,2gUR  
  this.data = (int[][])data.clone(); b@B\2BT  
  for (int i = 0; i < data.length; ++i){ REPI >-|  
   if (data != null) yY!@FGsA  
    this.data = (int[])data.clone(); :M.]-+(  
  } 'JJ :  
} ufN`=IJ%  
J!H)[~2/  
h5lngw  
%Lom#:L'  
8aVj@x$'  
   六、常见错误6#:检查new 操作的结果是否为null WyUa3$[gO  
rEj Ez+wu  
54Vb[;`Kkb  
e09QaY  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: vkLyGb7r<  
Is!+ `[ma  
 kZ=yb-~  
rfOrh^  
Integer i = new Integer (400); S^r[%l<'n  
if (i == null) _r`(P#Hy  
throw new NullPointerException(); uCj)7>}v{M  
`&J=3x  
+XAM2uN5_.  
LG=_>:~t>  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 72dd%  
&&Otj-n5  
$S U<KNMZ  
\o5/, C  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 'Ecd\p  
6G:7r [  
 七、常见错误7#:用== 替代.equals T5aeO^x  
]E1|^[y  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: AZnFOS  
L/,M@1@R  
G$jw#a[L  
rrl{3 ?  
int x = 4; Kp[5"N8  
int y = 5; BWzo|isv  
if (x == y) 2K2_-  
  System.out.println ("Hi"); J:\O .F#Fi  
// This ’if’ test won’t compile. q ;e/gP2  
if (x.equals (y)) oMKGM@V  
  System.out.println ("Hi"); ]"i^ VVw  
VKy3tW/_&  
muKjeg'b  
7qg. :h  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 Jg@eGs\*  
20)8e!jP  
|q 0iX2W  
8e_9u@p+w  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 |qb-iXW=  
LP} j0)n  
'^BTa6W}m  
B &)wJG  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 ~ \ Udl  
w=ZK=@  
$3(E0\#O  
CMYkxU  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 1P/4,D@  
$wqi^q*)  
)"s(;kU!  
&FDWlrG g  
  八、常见错误8#: 混淆原子操作和非原子操作 (E \lLlN  
j8?rMD~  
!3Q^oR  
O2Tna<cR&  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: :qvaI,  
z_87 ;y;=  
Y^'mBM#j  
:Q>{Y  
public class Example{ Y NGS"3F  
  private int value; // More code here... ^%/d]Zwb  
  public void set (int x){ h5@JS1cY  
   // NOTE: No synchronized keyword TL([hR _  
   this.value = x; ,/..f!bp  
  } vFsl]|<;8  
} '];=1loD  
RGLi#:0_.x  
u]Dds;~"b  
;h7O_|<%  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: Ufe@G\uyI  
]}BT'fky#  
O+o_{t\R  
8#15*'Y  
public void increment (){ X=pPkgW  
  // This is effectively two or three instructions: p}h9>R  
  // 1) Read current setting of ’value’. ]s~%1bd  
  // 2) Increment that setting. u'aWvN y+  
  // 3) Write the new setting back. TRQH{O\O  
  ++this.value; PA>su)N$  
} /7Ft1f  
go5l<:9  
R5MY\^H/A  
NU!B|l  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: @]B 7(j<'R  
3H@29TrJ+  
U,GY']J  
`r.  
public synchronized void increment (){ *\F,?yU  
  ++this.value; #[NNb?`F  
} uzWz+atH  
1TL~I-G&n  
I07_o"3>qr  
^0T[V-PgiD  
  九、常见错误9#:在catch 块中作清除工作 %B-m- =gz  
!gFUC<4bu  
=bB7$#al  
i{6wns?KMj  
  一段在catch块中作清除工作的代码如下所示: I xE }v%&  
o|7 h  
f)!7/+9>  
Y!lc/[8  
OutputStream os = null; xsSX~`  
try{ Af7&;8pM  
  os = new OutputStream (); + +G %~)S:  
  // Do something with os here. =hB0p^a  
  os.close(); J9yB'yE8  
}catch (Exception e){ 2q*aq%  
  if (os != null) \P7y&`|  
  os.close(); !x1ivP  
} 9EKc{1 z  
f3HleA&&  
k;?E,!{  
d3K-|  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: @m Id{w z  
"4e{Cq  
>PMLjXK  
1# X*kF  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 TkSeDP  
uV+.(sjH  
j9/Ev]im|F  
'ai!6[|SD  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 ~7Jj\@68  
Y+'522er  
;f)o_:(JJ  
ZP^7`q)6  
  3.close()可能会抛出异常。 Ig M_l=  
cJ!C=J  
Wx-vWWx*Q  
;IYH5sG{  
  上面代码的一个更优版本为: |=W=H6h*  
z^=e3~-J  
{r2fIj~V  
W:z!fh-  
OutputStream os = null; +1 j+%&).  
try{ N"wp2w  
  os = new OutputStream (); EU"J'?  
  // Do something with os here. Oe[qfsdW  
}finally{ jwd{CN%  
  if (os != null) '{(/C?T  
   os.close(); ^HasT4M+x  
} `[zd  
K0Zq )<  
XU19+mW=P  
 {!9i8T  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 9x40  
C5^eD^[c  
qTl/bFD  
@lnM%  
  十、常见错误10#: 增加不必要的catch 块 p>@S61 & [  
6Y[|xu:N8Y  
OK^0,0kS3  
s"solPw  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 ,$qqHSd1M  
8xEOR!\!`k  
{UR&Y  
ef;Ta|#  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 a|v}L,  
t9Nu4yl  
yw( E}   
M"t=0[0DM:  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: 9IRvbE~2  
_2,eS[wP  
!tNJLOYf  
A' \jaB  
try{ E(&GZ QE  
  // Nifty code here ;I+"MY7D  
}catch(Exception e){ aumXidb S  
  throw e; Q#i^<WUpg  
}finally{ iC{~~W6  
  // Cleanup code here $.z~bmH"D  
} |=frsf~?  
%YaUc{.%  
'%m0@5|hCD  
Pj!{j)-tS  
  不必要的catch块被删除后,上面的代码就缩短为: 7Pc0|Z/  
H<SL=mb;  
WR*|kh  
YORFq9a{R  
try{ 3PonF4  
  // Nifty code here V+dfV`*k  
}finally{ 0)8QOTeT  
  // Cleanup code here B=;pyhc  
} t|X |67W  
m\lSBy6  
1 ]@}+H  
4JHQ^i-aY  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 /)i)wxi  
tH:K6^oR  
?CS jn  
 :*M\z3`k  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 vII8>x%*  
$p4aNC  
^gFjm~2I  
oT5xe[{yj  
  小结 rI}E2J  
f*~ 4Kv  
34C ^vBp  
 hh"0z]  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 Q:~w;I  
-aNTFt~|[  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

您目前还是游客,请 登录注册
如果您在写长篇帖子又不马上发表,建议存为草稿
认证码:
验证问题:
3+5=?,请输入中文答案:八 正确答案:八