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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 !}#> ky!t  
dD=$$( je  
a3tcLd|7J  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 89g a+#7  
JfIXv  
]HuB%G|t1V  
_9 ]:0bDUo  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 Y \-W`  
<f>w"r  
\7r0]& _  
Wye* ~t  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 ]VRa4ZB{u  
>|E]??v  
5M0Q'"`F:  
A51 a/p#  
  一、常见错误1# :多次拷贝字符串 zVq!M-e  
f\]?,  
gwRB6m$  
<46&R[17M  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 FklR!*oL,)  
i}sAF/  
G`Nw]_ Z_  
1^![8>u"  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: "w'pIUQ3,  
,PTM'O@aU#  
j|k/&q[St  
s)a-ky(  
String s = new String ("Text here"); 6]?mjG6  
>v.f H6P,}  
P1Hab2%+  
` kT\V'  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: *c$[U{Px  
S\g9 @g.  
I'4(Ibl+  
zjQ746<&)i  
String temp = "Text here"; 73;Y(uh9  
String s = new String (temp); Q[biy{(b8  
H9/!oI1P?  
rx1u*L  
D_DwP$wSo  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: ub-3/T  
&zdS9e-fF  
""0 Y^M2I  
Rql/@j`JX  
String s = "Text here"; mgAjD.  
yYA*5 7^A  
u 2 s  
,t9EL 21  
  二、常见错误2#: 没有克隆(clone)返回的对象 yV(#z2|  
79v+ze  
,|:.0g[n  
qzUiBwUi@  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: y2jv84 M  
S hI1f  
.~f )4'T 9  
mr\,"S-`  
import java.awt.Dimension; (p-q>@m  
/***Example class.The x and y values should never*be negative.*/ Kjd3!%4mB  
public class Example{ >Ku4Il+36  
  private Dimension d = new Dimension (0, 0); :?6HG_9X  
  public Example (){ } ~)U50. CH  
&Hb%Q! ^Kb  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ Z<nNk.G  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ lYG`)#T  
   if (height < 0 || width < 0) NN*L3yx  
    throw new IllegalArgumentException(); o$*(N  
    d.height = height; #n>U7j9`O  
     d.width = width; P0l fK}  
  } 1<r!9x9G  
V~*Gk!+f  
  public synchronized Dimension getValues(){ gk%nF  
   // Ooops! Breaks encapsulation dk|LC-]`A  
   return d; 72dRp!J U  
  } 7;EDU  
} @]l|-xGCWn  
;g0Q_F@;p  
Q,3kaR@O  
W%L'nR~w$  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: wQ+pVu?6_  
,1sbY!&ekL  
yYP_TuNa  
"ko*-FrQ  
Example ex = new Example(); [bhKL5l  
Dimension d = ex.getValues(); @+b$43 ^  
d.height = -5; f24W*#IX  
d.width = -10; 9\Jc7[b  
]-\68bN  
Z/q6Q#  
yB UQ!4e  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 YSP\+ZZ  
]Dq6XR  
n _K1%  
d{S'6*`D  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 Tb i?AJa}  
YV.' L  
`K{}  
q} e#L6cM  
  更好的方式是让getValues()返回拷贝: >(RkoExO/  
!Cr3>tA  
:^)?AO#J  
|+ F ~zIu'  
public synchronized Dimension getValues(){ 1#d2 +J*  
return new Dimension (d.x, d.y); W.j^L;  
} _k@cs^  
*tqD:hiF  
[7I:Dm  
cW%)C.M  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 Y#F.{ i  
cGV%=N^BE<  
KQf WpHwfj  
xuXPVJdi  
  三、常见错误3#:不必要的克隆 <XLae'R  
`etw[#~N  
|vs5N2_  
vb>F)X?b_  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: Ae>+Fcv  
poQ_r <I  
o +$v0vg%T  
)g@+ MR  
/*** Example class.The value should never * be negative.*/ NY.Cr.}  
public class Example{ rI$NNk'A  
  private Integer i = new Integer (0); >?^oxB"<Gc  
  public Example (){ } c(Dp`f,  
n #X~"|U`  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ wkp2A18n  
  public synchronized void setValues (int x) throws IllegalArgumentException{ eo]nkyYDP  
   if (x < 0) A%D 'Z85 -  
    throw new IllegalArgumentException(); 1/-3m Po  
    i = new Integer (x); %0Ur3  
  } nah?V" ?Y  
,WyEwc]  
  public synchronized Integer getValue(){ ._rPM>B?  
   // We can’t clone Integers so we makea copy this way. '4'Z  
   return new Integer (i.intValue()); 0|AgmW_7 .  
  } s@Q7F{z  
} p"0#G&-  
c,1  G+.  
}b2YX+/e$f  
v2x+_K}J  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 }b1G21Dc!  
[c B^6v  
H'WYnhU&  
/9_%NR[  
  方法getValue()应该被写为: l#[Z$+!09  
AS;Sz/YP  
yY#h 1  
[<XYU,{R  
public synchronized Integer getValue(){ 6{)pF  
// ’i’ is immutable, so it is safe to return it instead of a copy. xNIrmqm5]  
return i; A+l(ew5Lw$  
} f/Y&)#g>k  
=`+D/ W\[Y  
&{j!!LL  
?M:>2wl  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: i]MemM-  
9^/Y7Wp/@  
a"@f< wU~  
0Md>-H;ZY  
  ?Boolean ()aCE^C  
   ?Byte U`6|K$@  
   ?Character e=&~6bs1U  
   ?Class ~xqiasE#K  
   ?Double ~v8X>XDL?T  
   ?Float  xL15uWk-  
   ?Integer ,>B11Z}PH  
   ?Long Z )c\B  
   ?Short Ck/44Wfej  
   ?String fTj@/"a  
   ?大部分的Exception的子类 7^i7U-A<A  
'HW l_M  
$NR[U+  
xb\EJ1M>  
 四、常见错误4# :自编代码来拷贝数组 ]T)N{"&N/  
XZ%[;[  
,&BNN]k  
>az~0PeEL  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: =][ )|n  
RI*n]HNgy+  
j sPavY  
i8?oe%9l  
public class Example{ gGP6"|tc4  
  private int[] copy; ChK-L6  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ ec ;  
  public void saveCopy (int[] data){ zTc;-,  
   copy = new int[data.length]; l>;hQh  
   for (int i = 0; i < copy.length; ++i) 4$iS@o|  
    copy = data; O+W<l:|$  
  } cvsH-uAp  
} -*7i:mg  
[RXLR#  
Fv]6 a n.  
6,5h4[eF*  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: o}Grb/LJ  
8y27O  
'xta/@Sq  
S TWH2_`  
void saveCopy (int[] data){ kl]V_ 7[  
  try{ vb^fx$V  
   copy = (int[])data.clone(); rN 9qH  
  }catch (CloneNotSupportedException e){ 9]v,3'QI  
   // Can’t get here. X$UK;O  
  } ?3~t%Q`  
} CT'4.  
g(pr.Dw6  
(#y2R F8j  
__b4dv  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: $1ovT8  
E n7~wKF  
?EC\ .{  
;~0q23{+;U  
static int[] cloneArray (int[] data){ 1 3 ]e< '  
  try{ LM eI[Ji  
   return(int[])data.clone(); ^mL X}E]  
  }catch(CloneNotSupportedException e){ rCF=m]1zxT  
   // Can’t get here. v7pu  
  } (kR NqfX  
} \0 ~?i6o  
Fj`k3~tUw  
n{N0S^h  
`qJJ{<1&U  
  这样的话,我们的saveCopy看起来就更简洁了: )5( jx  
C&yZ`[K  
C<=rnIf'  
%.d.h;^T  
void saveCopy (int[] data){ $9?:P}$v  
  copy = cloneArray ( data); CF>&mXg\  
} WOn<JCh]  
curYD~7  
x'0_lf</ #  
'!A}.wF0  
  五、常见错误5#:拷贝错误的数据 QcrhgR  
'ge$}L}4  
aB6/-T+ u  
f_)#  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: s=:)!M.i  
6hj[/O)E  
J.R]) &CB  
8XYxyOl  
import java.awt.Dimension; |yz[mP*;o  
/*** Example class. The height and width values should never * be FaCW +9B  
negative. */ 0 7Yak<+~  
public class Example{ w)|9iL8  
  static final public int TOTAL_VALUES = 10; pfZ[YC-  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; |= cc>]  
  public Example (){ } X'b3CS4  
cO]w*Hti  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ rmggP(  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ ?5CE<[  
   if (height < 0 || width < 0) @U!&XZ]h  
    throw new IllegalArgumentException(); %~:\f#6  
    if (d[index] == null) LCSvw  
     d[index] = new Dimension(); G%k&|  
     d[index].height = height; 1n<4yfJ  
     d[index].width = width; 8o+:|V~X  
  } hdWVvN  
  public synchronized Dimension[] getValues() K6-)l isf  
   throws CloneNotSupportedException{ 0 \ U*  
    return (Dimension[])d.clone(); a>l,H#w*vW  
  } Tv1oy%dK  
} gs!'*U)  
j_d}?jh  
p>eYi \'  
9{4oz<U  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: [_jw8`  
/RJ]MQ\*O  
3\4e{3$  
EC5 = 2w<  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ XY{N"S8  
  Dimension[] copy = (Dimension[])d.clone(); e|:\Ps`8  
  for (int i = 0; i < copy.length; ++i){ ]d[e  
   // NOTE: Dimension isn’t cloneable. TgjjwcO Y  
   if (d != null) D} B?~Lls  
    copy = new Dimension (d.height, d.width); ~ Rk.x +  
  } |=ph&9  
  return copy; @p~scE.#\  
} 6O,k! y>  
#w%-IhP  
V|@bITJ?7  
x-c5iahp'  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: L4B/ g)K  
Mi#i 3y(  
=8#.=J[/  
,mx\ -lWFy  
public void store (int[] data) throws CloneNotSupportedException{ ;Q,t65+Am  
  this.data = (int[])data.clone(); 0?oL zw&  
  // OK 9[JUJ,#X'0  
} ;=$;h6W0  
kJl^,q  
]VQd *~ -  
a T(]  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: r'yNc&~  
UUDHknm"  
ECi;o1hda  
7w2$?k',-  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ V-7l+C5  
  this.data = (int[][])data.clone(); // Not OK! ?o.d FKUe  
} N$e mS  
public void rightStore (int[][] data){ %\,9S`0  
  // OK! _BA; H+M  
  this.data = (int[][])data.clone(); LI@BB:)[  
  for (int i = 0; i < data.length; ++i){ ?7V~>i8[  
   if (data != null) 9#7W+9  
    this.data = (int[])data.clone(); hFm^Fy[R  
  } ~C^:SND7  
} G=[<KtWa  
-a@e28Y  
qD*y60~]zz  
.-iW T4Dn  
YFS6YA  
   六、常见错误6#:检查new 操作的结果是否为null riOaqV  
k L6s49  
/d}"s.3p  
+kd1q  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: q/zU'7%@  
*]HnFP  
ms5?^kS2O  
_p4]\LA  
Integer i = new Integer (400); <A=1]'1\r  
if (i == null) &*" *b\  
throw new NullPointerException(); LA_{[VWYp>  
Uc:NW   
`XH0S`B  
Z" ;q w  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 G3:!]}  
OFtf)cGE  
5b/ ~]v  
:,JjN&  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 3s?u05_  
tnnGM,"ol  
 七、常见错误7#:用== 替代.equals vTx>z\7q,  
SWx: -<  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: nl 'MWP  
2Q%7J3I  
1D#-,#?  
FfM^2`xP  
int x = 4; MZ$uWm`/  
int y = 5; .,z6a  
if (x == y) Wgh@XB  
  System.out.println ("Hi"); WtZI1`\qe  
// This ’if’ test won’t compile. 1N(1h D  
if (x.equals (y)) 8u~  
  System.out.println ("Hi"); :p}8#rb  
/a^ R$RHl'  
8 5ET$YV  
qJ`:$U  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 f%.Ngf9  
[HY r|T  
B2j1G JEO  
-c]AS[(  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 9x@|%4Zm"  
ko[w#j  
u*Xp%vNe  
e^\e;>Dh>  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 Gqd|F>  
(&eF E;c  
t}_ #N'`  
Godrz*"  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 =W3 K6w  
rWL;pM<  
MBg[hu%  
!5lV#w!vb  
  八、常见错误8#: 混淆原子操作和非原子操作 ?< b{  
J?3/L&seA  
)pHlWi|h  
GqRXNs!  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: FiiDmhu  
GKo&?Tj)  
o:Kw<z,$H  
-&Xv,:'?  
public class Example{ IyHbl_ P ^  
  private int value; // More code here... m4@NW*G{  
  public void set (int x){ =J]WVA,GqA  
   // NOTE: No synchronized keyword D BHy%i  
   this.value = x; 3U>-~-DS  
  } ??p%_{QY~b  
} ?yS1|CF%&y  
,J|,wNDU!K  
`Fn"QL-  
b`-|7<s  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: @5nFa~*K%  
@/<UhnI  
zw+aZDcV(  
>E+g.5 ,:W  
public void increment (){ W#<1504ip  
  // This is effectively two or three instructions: 7m-%  
  // 1) Read current setting of ’value’. _aPAn|.  
  // 2) Increment that setting. =lJ ?yuc  
  // 3) Write the new setting back. "wOfs$w%s  
  ++this.value; swF{}S"  
} t 6nRg  
P'U2hCif  
DD$> 3`  
pjFO0h_Y  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: vv ,4n&D  
;_(f(8BO   
+>q#eUS)  
mcez3gH  
public synchronized void increment (){  JaY"Wfc  
  ++this.value; geR+v+B,  
} &Pr\n&9A  
Zigv;}#  
[HQ)4xG  
*z0d~j*W;  
  九、常见错误9#:在catch 块中作清除工作 v3-' G gM  
E7A!,A&>  
m]2xOR_  
{=[>N>"  
  一段在catch块中作清除工作的代码如下所示: e NIzI]~  
z l r !   
k3#'g'>yh  
0ae8Xm3J@R  
OutputStream os = null; Q>%n&;:  
try{ &|>~7(  
  os = new OutputStream (); g5B TZZ  
  // Do something with os here. SQ>i:D;  
  os.close(); 4 ThFC  
}catch (Exception e){ ~w>h#{RB  
  if (os != null) 1Nt &+o  
  os.close(); K29/7A/  
} EG>?>K_D  
!?>V^#c  
}S/i3$F0~  
dgw.OXa  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: QadguV6|  
-G,}f\Cg  
{.:$F3T  
$6"(t=%{  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 /d3Jd .l!  
MoIh =rw  
:skR6J  
~sk p}g]  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 v=N?(6T  
GDxv2^4  
=j,WQ66r3  
F[jE#M=k  
  3.close()可能会抛出异常。 ,L/x\_28  
|u&cN-}C d  
_>- D*l  
(9'^T.J  
  上面代码的一个更优版本为: 7{|QkTgC  
So aqmY;+  
P3_.U8g$r  
<sH}X$/  
OutputStream os = null; !$Nj!  
try{ #V!a<w4_  
  os = new OutputStream (); KrE 'M  
  // Do something with os here. ntW@Fm:bw>  
}finally{ 9|+6@6VY!  
  if (os != null) mOE *[S)  
   os.close(); 3"y 6|e/5  
} .9jKD*U|  
z]G|)16  
s*izhjjX  
\/NF??k,jk  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 ukWn@q*  
@?3f`l 9  
LIZB!S@V\  
5f-b>=02  
  十、常见错误10#: 增加不必要的catch 块 ^dQ{vL@9b9  
@tH9$J*Y<  
=hPXLCeC  
0xB2  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 Qz~uD'Rs/  
isZ5s\  
3P cVE\GN  
}|P3(*S  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 .hl_zc#  
~r--dU  
W: ]FYC  
Ww7Ya]b.k  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: 3A#Tn7  
GShxPH{_j  
-JMn?]  
H71sxek3  
try{ Wc3z7xK1@  
  // Nifty code here HK@ij,px  
}catch(Exception e){ .Bm%  
  throw e; "j^i6RS  
}finally{ ( ay AP  
  // Cleanup code here fj_23{,/"g  
} {7NGfzwp;6  
wcGK *sWG-  
QZ a.c  
pO` KtagL  
  不必要的catch块被删除后,上面的代码就缩短为: P49\A^5S!  
@+u>rS|IB  
* DL7p8  
ScPVjqG2{  
try{ v,KKn\X  
  // Nifty code here 4-(kk0]`z  
}finally{ ~66xO9s  
  // Cleanup code here m#7(<#  
} >Fel) a  
u!_l/'\  
$]v}X},,  
^J'_CA  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 / ;]5X  
%ByPwu:f  
~4~`bT9  
yYG<tUG;  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 ]R__$fl`8  
>[}oH2oi  
@AsJnf$y  
jwZ,_CK  
  小结 0I&k_7_   
^t;z;.g  
ks '>?Dw  
7u):J  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 .Eao|;  
3*b5V<}'|  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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