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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 iq5-eJmq  
[] R8VC>Ah  
880T'5}S :  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 %~N| RSec  
\M*c3\&~,e  
Qo80u? *  
C0&ZQvvy1:  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 Z|d+1i  
#_:%Y d  
A!a.,{fZ  
Xzqx8Kd  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 mC'<Ov<eJ  
v/,,z+%-  
"[CR5q9Pr  
gc W'  
  一、常见错误1# :多次拷贝字符串 YOY2K%o  
@680.+Kw  
T~d_?UAw$  
UvL=^*tm  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 2hb>6Z;r]K  
D#d/?\2  
6<YAoo  
2UTmQOm  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: -LlS9[r0  
k jx<;##R8  
:79u2wSh  
]'0}fuV  
String s = new String ("Text here"); <Q_E3lQy/  
48.4GwL7  
1CS\1[E  
i8=+ <d  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: <qBM+m$|)  
xqv&^,ic  
#eKH'fE  
"?'9\<>  
String temp = "Text here"; M|UCV_omN  
String s = new String (temp); IJLuu@kRm,  
H4W!@"e  
<#)Q.P  
g!`^!Q/($  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: sLc,Dx"+  
N <M6~  
 bDq<]h_7  
xr31< 4B  
String s = "Text here"; {^R>H|~  
Dt'bbX'edw  
t* =i8`8  
aoGns46Y  
  二、常见错误2#: 没有克隆(clone)返回的对象 <}}u'5;^?x  
*d-JAE  
,9$|"e&  
<_SdW 5BF<  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: <lRjh7  
*EFuK8 ;  
$ou/ Fn  
9r 5(  
import java.awt.Dimension; <jh=W9.N_  
/***Example class.The x and y values should never*be negative.*/ SgQ(#y|vV  
public class Example{ FMT_X  
  private Dimension d = new Dimension (0, 0); ##s :Ww  
  public Example (){ }  *1 *i5c  
m1RjD$fM  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ =Nr?F '<  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ >oapw5~5  
   if (height < 0 || width < 0) KB gFS%-W  
    throw new IllegalArgumentException(); u&e?3qKX(  
    d.height = height; ]<u%jTQREd  
     d.width = width; cEK#5   
  } pV$A?b"?*  
 }D!o=Mg^  
  public synchronized Dimension getValues(){ 'T]Ok\  
   // Ooops! Breaks encapsulation gj4ONmY  
   return d; eRg;)[#0>$  
  } R+9 hog  
} l7(p~+o?h>  
0hq\{pw_y*  
XLlJ|xhY-K  
-~ Dn^B1^  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: a@! O}f*  
m(CsO|pz  
C-/+n5J  
_!%@V=  
Example ex = new Example(); va!fJ  
Dimension d = ex.getValues(); xj JoWB  
d.height = -5; 5oo6d4[  
d.width = -10; /pJr%}sc  
jV#1d8qm  
f+2mX"Z[F  
fdgjTX  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 0<nW nD,z  
j+2-Xy'  
jgBJs^JgYG  
|oR#j `  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 srH.$Y;~  
o)H| #9h5  
PrF('PH7i  
6ciA|J'MR  
  更好的方式是让getValues()返回拷贝: sfsK[c5bm  
/Ur]U w  
$LtCI  
VA%4ssy  
public synchronized Dimension getValues(){ +xNq8yS  
return new Dimension (d.x, d.y); kMg[YQ]OC  
} +>:[irf  
U \jFB*U  
X&!($*/  
QV _a M2  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 f5'vjWJ30  
Q>uJ:[x+  
-c %'f&P  
XKvH^Z4h{l  
  三、常见错误3#:不必要的克隆 +SkfT4*U  
ePTxuCf>  
>vNE3S_  
8[oZ>7LMzC  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: !)FKF7'  
J$,bsMIX  
J?f7!F:8  
:v^OdW  
/*** Example class.The value should never * be negative.*/ /Y| <0tq  
public class Example{ zn5|ewl@"  
  private Integer i = new Integer (0); |43Oc:Ah+  
  public Example (){ } i \@a&tw  
D*ZswHT{y  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ #}[NleTVt  
  public synchronized void setValues (int x) throws IllegalArgumentException{ U+ V yH4"  
   if (x < 0) y.::d9v  
    throw new IllegalArgumentException(); iL'j9_w,  
    i = new Integer (x); l^rQo_alk  
  } ne=CN!=  
Bu4@FIK!C  
  public synchronized Integer getValue(){ j_SUR)5  
   // We can’t clone Integers so we makea copy this way. Xkf|^-n  
   return new Integer (i.intValue()); [vxHsY3z  
  } "nU] 2  
} P-X2A2  
^N O4T  
MK <\:g  
P5v;o9B&  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 LVJn2t^  
VhU,("&pm  
&,$N|$yK}|  
ra^"Vr  
  方法getValue()应该被写为: Jl ?_GX}ZY  
^(7Qz&q  
Z5[g[Q  
Ce} m_  
public synchronized Integer getValue(){ y_2B@cj  
// ’i’ is immutable, so it is safe to return it instead of a copy. ym2"D?P (  
return i; U=[isi+7  
} xn1, o MY=  
{X-a6OQj  
d/\ajQ1::  
Am#Pa,g  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: dHtEyF  
+_ny{i`'  
X5=I{eY}  
fD%20P`.  
  ?Boolean 2j$~lI  
   ?Byte [iC]Wh%  
   ?Character .L.9e#?3  
   ?Class 5X:3'*  
   ?Double STz@^A  
   ?Float yn.[-  
   ?Integer TpxAp',#7  
   ?Long X5+$:jq&  
   ?Short CM)V^k*  
   ?String <>V~  
   ?大部分的Exception的子类 Ka$lNL3<j  
LXf|n  
40 zO4  
%Hu.FS5'  
 四、常见错误4# :自编代码来拷贝数组 }l_8~/9  
n'!x"O7  
 Au*1-  
c~!ETwpHQ  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: .>Fpk7  
877Kv);  
p Moza8  
& 5QvUn  
public class Example{ x|g2H.n  
  private int[] copy; 8[:G/8VI  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ Nop61zj  
  public void saveCopy (int[] data){ "_:6v64Gx  
   copy = new int[data.length]; yh.WTgcW  
   for (int i = 0; i < copy.length; ++i) 'a>D+A:  
    copy = data; -0<ZN(?|  
  } SUD~@]N1  
} :)%cL8Nz]$  
Yh{5O3(;  
wcGv#J],  
n/YnISt  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: dcfwUjp[  
4|DN^F~iut  
JY3!jtv  
n D}<zj$D2  
void saveCopy (int[] data){ :0Fc E,1  
  try{ ;Pvnhy  
   copy = (int[])data.clone(); 1D%E})B6  
  }catch (CloneNotSupportedException e){ 8tzL.P^  
   // Can’t get here. a>k9& w  
  } <]*Jhnx/  
} \8USFN~(Y  
Is9.A_0h  
Y\F4  
CiTWjE?|7  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: <eZrb6a'  
)M@^Z(W/a  
F1p|^hYDW  
L+0:'p=  
static int[] cloneArray (int[] data){ n%!50E6*:  
  try{ %1)JRc  
   return(int[])data.clone(); zbfe=J4c  
  }catch(CloneNotSupportedException e){ .`oKd@I*"  
   // Can’t get here. j?VHR$  
  } Ii>#9>!F  
} }d@;]cps  
S`vw<u4t  
J!}R>mR  
S(lqj6aa}  
  这样的话,我们的saveCopy看起来就更简洁了: 845\u&  
C#RueDa.  
bnV)f<  
]PbwG  
void saveCopy (int[] data){ Bo ??1y  
  copy = cloneArray ( data); o%K1!'  
} a&[>kO  
^UyN)eX  
^[v>B@p*{  
\!k\%j 9  
  五、常见错误5#:拷贝错误的数据 4thPR}DH}  
FPEab69  
=.,XJIw&  
l s%'\}  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: ,,-j5Y  
am/D$ (l1  
A$?o3--#]G  
zoj w^%W  
import java.awt.Dimension; 5"D\n B%  
/*** Example class. The height and width values should never * be {(h!JeQ  
negative. */ H-8_&E?6m  
public class Example{ ""jl  
  static final public int TOTAL_VALUES = 10; _HkQv6fXpE  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; SA [(1dy;  
  public Example (){ } FQcm =d_s  
Sf t,$  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ jjg&C9w T  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ 2gnz=  
   if (height < 0 || width < 0) \]Rmq_O  
    throw new IllegalArgumentException(); XOEf,"  
    if (d[index] == null) O\w-hk  
     d[index] = new Dimension(); e{.P2rnh  
     d[index].height = height; s2K8|q=  
     d[index].width = width; UO-,A j*wW  
  } t_qX7P8+'  
  public synchronized Dimension[] getValues() A$TF a:O|  
   throws CloneNotSupportedException{ TaZlfe5z  
    return (Dimension[])d.clone(); TH+TcYqO  
  } Y7`Dx'x  
} u<HJFGLzI  
^1XnnQa  
`>`{DEDx{5  
sA+( |cEh  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: 'WwD$e0=  
X&,a=#C^  
QW&@>i  
mMad1qCi7  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ S?Uvt?  
  Dimension[] copy = (Dimension[])d.clone(); XMpE|M! c  
  for (int i = 0; i < copy.length; ++i){ _Tf0L<A'R  
   // NOTE: Dimension isn’t cloneable. B|rf[EI>  
   if (d != null) 9bD ER  
    copy = new Dimension (d.height, d.width); N'8}5Kx5  
  } J(`(PYo\i  
  return copy; }k6gO0z  
} , ins/-3  
h8HA^><Xr  
z4(Q.0x7  
\p!mX|  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: )(`,!s,8)  
T2k# "zD  
!^w}Sp  
}vQ Y+O  
public void store (int[] data) throws CloneNotSupportedException{ /x&52~X5-  
  this.data = (int[])data.clone(); wdEQB-dA  
  // OK yzJTNLff  
} 0+_:^z  
yzz(<s:o/  
)H<F([Jri  
y;tX`5(fe  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: d~O)mJ J  
m[&pR2T  
AO0aOX8_+D  
tR-rW)0K3Q  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ WOf*1C  
  this.data = (int[][])data.clone(); // Not OK! MT.D#jv&  
} t8S,C4  
public void rightStore (int[][] data){ S d]`)  
  // OK! 2@pEuB3$?!  
  this.data = (int[][])data.clone(); 2L?Pw   
  for (int i = 0; i < data.length; ++i){ N x/_+JWje  
   if (data != null) ]a\HgFp@  
    this.data = (int[])data.clone(); uJ%XF*>_D  
  } 1.q a//'RW  
} %;YERO!  
@4j!M1} 4  
:JG2xtn  
YDiru  
'M3V#5l)@|  
   六、常见错误6#:检查new 操作的结果是否为null SWMi+)  
o%?~9rf]]  
M\bea  
8f-B-e?k  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: YN5p@b=FX  
__,}/|K2  
@m ?&7{y#?  
-wn(J5NnR  
Integer i = new Integer (400); Xq.G vZS`  
if (i == null) A*+KlhT  
throw new NullPointerException(); 8J+:5b_?  
9rQw~B<S  
^+Stvj:N  
t+ O7dZt%r  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 sqk$q pV6  
,2^zX]dgM  
1$rrfg  
7Dwf0Re`  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 H%`|yUE(  
vhu5w#]u*  
 七、常见错误7#:用== 替代.equals <!.Qn Y  
5SmgE2}  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: 1N\-Ku  
9N{"ob Z  
*6 1G<I  
agxR V  
int x = 4; @1G`d53N  
int y = 5;  Q~AK0W  
if (x == y) 73'.TReK  
  System.out.println ("Hi"); 99..]  
// This ’if’ test won’t compile. 'P<T,:z?  
if (x.equals (y)) =;@?bTmqD  
  System.out.println ("Hi"); BX6]d:S  
A+1>n^^_<  
:ODG]-QF  
{w|KWGk2  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 B3iU#   
9W@ Tf  
Fwv(J_'q  
fW.)!EPO  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 p}R3A J  
qox31pnS  
i2+vUl|;Z  
>6zXr.  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 a76`"(W  
V61.UEN  
zWEt< `1M  
Ww$ ?X LF  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 f8?c[%br  
\3v}:E+3  
2zN%Z!a#J  
?.b.mkJ  
  八、常见错误8#: 混淆原子操作和非原子操作 \Z%V)ZRi=  
%["V "{ z  
"<I*ViZ  
ISl-W1u}  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: 7BDoF!kCx  
*/yR _f  
L;s,xV  
{!rpE7P-  
public class Example{ -R-|[xN  
  private int value; // More code here... -?&s6XA%#  
  public void set (int x){ .~Z@y#  
   // NOTE: No synchronized keyword M]$_>&"  
   this.value = x; `jyBF  
  } pJ 7="n  
} >rb8A6  
2pQdDbm  
C [h^bBq  
W6[# q%o  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: z?i{2Fz6  
X6g{qzHg_  
8o4?mhqV  
S;FgS:;  
public void increment (){ 8h| 9;%  
  // This is effectively two or three instructions: O'} %Bjl  
  // 1) Read current setting of ’value’. X0QLT:J b  
  // 2) Increment that setting. %;{R o)03  
  // 3) Write the new setting back. A#P]|i  
  ++this.value; 17{$D ,P  
} YjM_8@ <  
C%y!)v_x  
QL4BD93v  
#b?)fqRJL  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: 7 -Yn8Gq  
RY]Vo8  
;_vo2zl1  
9:tn! <^=I  
public synchronized void increment (){ #fR~ 7 KR  
  ++this.value; XY1e eB-  
} nm597WeZp  
8hx 3pvmk  
E)=X8y  
[nnX,;  
  九、常见错误9#:在catch 块中作清除工作 j[Xc i<m  
dW8M^A&  
3l8k O  
:>'4@{'   
  一段在catch块中作清除工作的代码如下所示: n!K<g.tjW  
{v>orP?  
D7"RZF\)  
H tu}M8/4  
OutputStream os = null; oTqv$IzqP  
try{ )KPQ8y!d  
  os = new OutputStream (); Q7-d]xJ^  
  // Do something with os here. x.OCE`  
  os.close(); t$W~X~//  
}catch (Exception e){ R%Y#vUmBV{  
  if (os != null) T8ftBIOi  
  os.close(); dUg| {l  
} zz 1e)W/  
]VU a $$  
;^K4kK&f  
Mmu>&C\  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: 7u9!:}Tu  
Y79{v nlGk  
X( H-U q*(  
g^dPAjPQ  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 sZ!/uN!6  
$stBB  
hn bF}AD  
C/{tvY /o  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 eZ^-gk?  
aF~ 0\XC  
{IlX@qWr  
`1eGsd,f  
  3.close()可能会抛出异常。 z` :uvEX0  
JWuF ?<+k  
!VJ5(b  
9<ev]XaSl  
  上面代码的一个更优版本为: rprtp5Cg  
xxN=,p  
wwtk6;8@  
mz~aSbb|  
OutputStream os = null; i9FHEu_  
try{ 0WjPo  
  os = new OutputStream (); m:1f7Z>  
  // Do something with os here. P{-f./(JD  
}finally{ FB-_a  
  if (os != null) .Y"H{|]Mnh  
   os.close(); ,%FBELqOW  
} P,ox) )+6  
E9L)dMZSpj  
*Q@%< R  
^mu?V-4  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 >lRa},5(  
_k,/t10  
^\X-eeA  
,`3kDqS_4  
  十、常见错误10#: 增加不必要的catch 块 ;be2sTo  
<opBOZ d  
`6.rTs $<  
Wy2 pa #Q  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 S]7RGzFe  
JY|f zL  
];.H]TIc6  
Xy>+r[$D:  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 '7!b#if  
AC$:.KLI  
q5irKT*Hs  
wi]F\ q"Y^  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: :CQ-?mT^LA  
_dT,%q  
W+&w'~M  
k|^e=I   
try{ m{/?6h 1  
  // Nifty code here b|cUKsL5  
}catch(Exception e){ ng-g\&-  
  throw e; z]NzLz9VfL  
}finally{ `|1#Vuk  
  // Cleanup code here |g3a1El  
} F0O/SI(cA  
a| *{BlY  
ov{  
uIG,2u,  
  不必要的catch块被删除后,上面的代码就缩短为: rI\G&OqpP  
6dRxfbL  
6w d0"  
h|_E>6d)  
try{ R).?lnS  
  // Nifty code here Jv*(DFt!v  
}finally{ [dK5kO  
  // Cleanup code here GgoPwl#{  
} a)+;<GZ~  
H0zKL]D'>  
Fu*~{n  
C0xj M0  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 X  8V^  
t,*hxzD"  
&TmN^R>  
#PzRhanX  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 +Mb;;hb  
- I$qe Xy  
6gLk?^.  
t,mD{ENm&  
  小结 y{.s 4NT  
%<|w:z$vp  
*CHLs^)   
nQ*9|v4  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 E,]G Ek  
9'tElpDJ6#  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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