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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 aj>6q=R  
sFh mp  
3aL8GMiu  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 >)E{Hs  
8_yhV{  
W dM?{; #  
v(5zSo  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 ^! ?wh  
;[pY>VJ(  
b#XY.+ *0  
7OF6;@<  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 v?\Z4Z|f  
NJ 6* 7Cd  
6x?3%0Km  
g<ZB9;FX %  
  一、常见错误1# :多次拷贝字符串 5,H,OZ}  
HB+{vuN*L  
WS17DsWW  
Y 6B7qp  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 $^[^ ]Q  
J0{;"  
){~]-VK  
%d3KE|&u  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: )zU bMzF  
IEbk_-h[  
E'_3U5U  
?<mxv"  
String s = new String ("Text here"); }q-*Ls~  
=8Bq2.nlR  
Sz z:$!t  
<$H-/~Y  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: X,+M?  
G)|s(C!  
X:3W9`s )*  
s2`:NS  
String temp = "Text here"; 9d5|rk8VS  
String s = new String (temp); ;gE]*Y.Z.p  
ak_&\'P  
x^0MEsR  
rV *`0hA1  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: 'WF Ey>1#  
_VvXE572  
0m`{m'B4n  
=Fu~ 0Wc  
String s = "Text here"; m+Um^:\jX  
'aLTiF+  
eaNMcC1  
5;:P^[cH9  
  二、常见错误2#: 没有克隆(clone)返回的对象 .=I:cniw\r  
}{3XbvC  
BRSOE U\=  
g`NJ `  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: Ms * `w5n  
!:zWhu,  
2|n)ZP2cp  
p`oSI}ZwB  
import java.awt.Dimension; r]6X  
/***Example class.The x and y values should never*be negative.*/ %d%$jF`  
public class Example{ Ug2^cgL  
  private Dimension d = new Dimension (0, 0); ?G|*=-8  
  public Example (){ } qFV }Y0w  
`XmT)C  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ PPj_NV  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ &O&;v|!9  
   if (height < 0 || width < 0) G; onJ>  
    throw new IllegalArgumentException(); G\\0N^v  
    d.height = height; /E%r@Rui3$  
     d.width = width; Uu}a! V  
  } N\f={O8E  
:4HZ >!i  
  public synchronized Dimension getValues(){ KMU2Po qD  
   // Ooops! Breaks encapsulation ;XUiV$  
   return d; ZJZKCdT@  
  } 06r-@iY.]  
} y,YK Mc  
i,3[0*ge  
il403Ae0  
IN{ 1itE  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: -JMlk:~  
O /S:S  
czp .q  
K1*oYHB  
Example ex = new Example(); v \xuq`  
Dimension d = ex.getValues(); x!@3.$  
d.height = -5; X{-@3tG<r  
d.width = -10; cVR#\OM  
S*0P[R  
";>>{lYA.  
.#BWu(EYV  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 i wFI lJ@  
8i?Hh?Mf}  
S|_}0  
]CL9N  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 Q,AM<\S  
jO5R0^w  
)^D:VY9 2  
^ y1P~4w?  
  更好的方式是让getValues()返回拷贝: +CQ$-3  
7?[{/`k~?  
VK~ OL  
"&@v[O)!xu  
public synchronized Dimension getValues(){ 0r|mg::'  
return new Dimension (d.x, d.y); >^U$2P  
} DqQ+8 w  
:t'*fHi~  
4ne95_i  
l&2}/A  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 ,'FdUq)i  
Z2.S:y.  
q ad`muAd  
qh]ILE87(  
  三、常见错误3#:不必要的克隆 uFXu9f+  
Gl@-RLo  
/-mo8]J#2~  
E+tV7xa~  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: `g~T #U\>d  
S,'y L7s  
=Y-ZI  
faqh }4  
/*** Example class.The value should never * be negative.*/ (:TZ~"VY  
public class Example{ QnJ(C]cW  
  private Integer i = new Integer (0); piy_9nk  
  public Example (){ } ;FI"N@z  
kCuIEv@  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ #xlT,:_:)  
  public synchronized void setValues (int x) throws IllegalArgumentException{ BY&+fK ae  
   if (x < 0) xGU~FU  
    throw new IllegalArgumentException(); w4"4(SR.  
    i = new Integer (x); /HiRbwQK#  
  } 9pPohR*#V  
GK>.R<[  
  public synchronized Integer getValue(){ iW\Q>~0#_  
   // We can’t clone Integers so we makea copy this way. kz UP   
   return new Integer (i.intValue()); REaU=-m-  
  } u\uYq  
} E#T6rd P  
n$}) }kj  
tu%!j}3s  
$ M8ZF(W  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 8rXQK|A  
@h91: hb  
4XCy>;4u  
F^xhhz&e  
  方法getValue()应该被写为: ;<?mMi@<E  
)j^~=Sio.  
~$@~X*K~  
<)J83D0$E  
public synchronized Integer getValue(){ b-Q%c xJ  
// ’i’ is immutable, so it is safe to return it instead of a copy. 2X@| H  
return i; Q^_*&},V  
} th 9I]g^=t  
C@$!'^ 61  
~dpU D F  
7w_cKR1;  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: lJR  
T`?{Is['(  
a7_&;  
ZtFOIb*  
  ?Boolean 6')pM&`t  
   ?Byte ;@&mR <5j  
   ?Character TS~>9h\;  
   ?Class <%~`!n,t0  
   ?Double (8$; 4q[!  
   ?Float 7"iUyZ(  
   ?Integer Oapv`Z\i~  
   ?Long C@7<0w  
   ?Short 9|}u"jJB%E  
   ?String eOdB<He36  
   ?大部分的Exception的子类 [RqL0EP  
Z^'i16  
HF\|mL  
K< ;I*cAX  
 四、常见错误4# :自编代码来拷贝数组 B_u1FWc  
X`n)]~  
v"po}K  
Ew9\Y R}  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: R[l9f8  
.>.B  
]3%( '8/  
`wzb}"gLsM  
public class Example{ x'c%w:  
  private int[] copy; Y<"BhE  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ ;B,6v P#  
  public void saveCopy (int[] data){ n*Q~<`T  
   copy = new int[data.length]; Q=+*OQV29  
   for (int i = 0; i < copy.length; ++i) ROv(O;.Ty  
    copy = data; +li<y`aw0  
  } vs`"BQYf  
} t\/i9CBn  
3b#eB  
i 1{Lx)  
vfn _Nq;  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: _3_kvs  
L T.u<ThR}  
LrL ZlJf  
p;P"mp\'  
void saveCopy (int[] data){ ,'KS:`m!  
  try{ AD** 4E  
   copy = (int[])data.clone(); [nx OGa2  
  }catch (CloneNotSupportedException e){ Xv~v=.HNhk  
   // Can’t get here. ks}J ke>  
  } d5hYOhO[  
} 6BnP"R.  
[#}0)  
G1vg2'A  
N3Yf3rK  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: [X"F}ph  
fH#*r|~  
49gm=XPm  
3.c0PRZ  
static int[] cloneArray (int[] data){ 8~~*/oCoJt  
  try{ 9Ez>srH(  
   return(int[])data.clone(); e)#O-y  
  }catch(CloneNotSupportedException e){ =.sg$VX  
   // Can’t get here. 2%|0c\y|z=  
  } mHiV};$  
} 1hz:AUH  
H;eGBVi  
,k,RXgQ  
sghQ!ux  
  这样的话,我们的saveCopy看起来就更简洁了: 0pC}+ +  
9}=]oX!+V  
;F/yS2p  
5}pn5iI  
void saveCopy (int[] data){ ]I+"";oQGB  
  copy = cloneArray ( data); S-4C >gM  
} T&ECGF;Y/  
x3`b5^  
 wh A  
EGY'a*]cU  
  五、常见错误5#:拷贝错误的数据 *i=+["A  
FK^JCs^  
X q"_^  
kzK4i!}  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: &$,%6X"  
3p%B  
qId-v =L  
nQ$4W  
import java.awt.Dimension; Ji#"PE/Pt  
/*** Example class. The height and width values should never * be {D6E@a  
negative. */ kwcH$w<I  
public class Example{ "\n,vNk  
  static final public int TOTAL_VALUES = 10; 0c$0<2D%  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; 0Bo7EV  
  public Example (){ } ?tf/#5t}  
5q.d$K |  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ >BDK?YMx  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ FLqF!N\G  
   if (height < 0 || width < 0) x=/`W^t2  
    throw new IllegalArgumentException(); &V$qIvN$  
    if (d[index] == null) o/;kzi  
     d[index] = new Dimension(); w`N|e0G@  
     d[index].height = height; BotGPk><c  
     d[index].width = width; ~=!d>f~U  
  } "M GX(SQ  
  public synchronized Dimension[] getValues() 2i~tzo  
   throws CloneNotSupportedException{ =)2sehU/  
    return (Dimension[])d.clone(); \e=Iw"yd  
  } tiTJ.uz6  
} zm& D #)  
"<#-#j  
WRq:xDRn0  
7jj.maK  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: h6yXW! 8  
`.Oj^H6  
n%SR5+N"  
gH0' Ok'  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ 7lC );  
  Dimension[] copy = (Dimension[])d.clone(); j[^(<R8  
  for (int i = 0; i < copy.length; ++i){ M;96 Wm  
   // NOTE: Dimension isn’t cloneable. "&_$%#HUv  
   if (d != null) F7FUoew<  
    copy = new Dimension (d.height, d.width); ]YO &_#  
  } ]ZkR~?  
  return copy; <~%e{F:[#  
} ,C=Lu9  
e(5 :XHe  
:jJ;&t^^  
#[Z1W8e  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: (P+TOu-y\  
!4"!PrZDB  
vxey $Ir  
j56#KNAha  
public void store (int[] data) throws CloneNotSupportedException{ :c*_W /  
  this.data = (int[])data.clone(); _F2 R x@Y  
  // OK U)f;*{U  
} d(=*@epjR  
MRI`h.  
#><P28m  
e[Tu.$f-  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: lj U|9|v  
w,6zbI/  
5i<E AKL  
p#]D-?CM)  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ E`"<t:RzF  
  this.data = (int[][])data.clone(); // Not OK! c}QWa"\2n  
} lBYc(cr  
public void rightStore (int[][] data){ feSj3,<!  
  // OK! \V1geSoE  
  this.data = (int[][])data.clone(); 4 8}\  
  for (int i = 0; i < data.length; ++i){ $N}nO:`t  
   if (data != null) ZFJ qI  
    this.data = (int[])data.clone(); o'Uaz*-po  
  } _3;vir%)  
} Epl\(  
K5h2 ~  
| 4slG   
LNA5!E  
_gLj(<^9  
   六、常见错误6#:检查new 操作的结果是否为null U= Gw(  
 MeP,8,n'  
".Z1CBM(  
2!}:h5   
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: /"f4aF[  
z-J?x-<  
#835 $vOe  
3 7F&s  
Integer i = new Integer (400); -MCDX^ >P  
if (i == null) dr54 D  
throw new NullPointerException(); oB$P6   
cLZaQsS%  
~!PaBS3A  
eB]R<a60  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 =k{ n! e  
Xf"B\%,(`  
THOXs; k0  
^L,Uz:[J  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 :c_>(~  
Z{MR#.I  
 七、常见错误7#:用== 替代.equals <%SG <|t  
w[/_o,R  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: Im<i.a <`  
RqONVytx  
iB1+4wa  
[s} n v]  
int x = 4; .$rt>u,8<  
int y = 5; \i2S'AblYq  
if (x == y) =!~6RwwwY  
  System.out.println ("Hi"); odm!}stus  
// This ’if’ test won’t compile. ri]"a?Rm  
if (x.equals (y)) ac2G;}B|  
  System.out.println ("Hi"); Rg3cqe#O/  
mF6 U{=  
5, j&-{ 0W  
tn\PxT  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 KysJ3G.k\  
)J"*[[e  
>$g+Gx\v4  
|)4aIa  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 TA~FP#.  
.*x |TPv{  
(Cc!Iw'0M  
`1hM3N.nO  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 WlF}R\N!  
T\ cJn>kCn  
-!ARVf *  
Q&@~<!t  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 K,$Ro@!  
<* vWcCS1  
3[a&|!Yw  
[8h~:.d`  
  八、常见错误8#: 混淆原子操作和非原子操作 M?nYplC  
,~TV/l<  
3lw8%QD>  
F4#^jat{  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: n{@^ne4 m  
_P:}]5-|  
.O1Kwu  
kgQyG[u  
public class Example{ Ln4zy*v{  
  private int value; // More code here... v?)u1-V0  
  public void set (int x){ b14WIgjsl  
   // NOTE: No synchronized keyword W,53|9b@  
   this.value = x; Wb;x eG  
  } < 9 vS  
} 5TB==Fj ?  
;LhNz()b  
Vlka+$4!  
4kr! Af  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: *.2[bQL@v  
y?}R,5k  
/ Ml d.  
5{.g~3"  
public void increment (){ iDdmr32E  
  // This is effectively two or three instructions: =a]B#uUn  
  // 1) Read current setting of ’value’. ~t,-y*=  
  // 2) Increment that setting. g3h:oQCS  
  // 3) Write the new setting back. ]CnqPLqL  
  ++this.value; -:P`Rln  
} E979qKl  
4Up3x+bg  
Aq5@k\[  
%ylpn7I\6  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: m`Dn R`+  
Nm;V9*5  
>7Y6NAwY  
l(fStpP  
public synchronized void increment (){ aP}%&{iC*  
  ++this.value; W4ygJL7 6  
} b~L8m4L  
ss4<s 5:y  
flr&+=1?D  
&# w~S~  
  九、常见错误9#:在catch 块中作清除工作 '-?t^@  
q@6Je(H  
yrgb6)]nm@  
HEMq4v4  
  一段在catch块中作清除工作的代码如下所示: .15^c+j  
giNyD4uO  
i4p2]Nr t  
M9J^;3Lrh  
OutputStream os = null; >.}ewz&9o  
try{ AY~~a)V  
  os = new OutputStream (); z!0 }Kj  
  // Do something with os here.  (r!d4  
  os.close(); NU#rv%p  
}catch (Exception e){ ;<~lzfs  
  if (os != null) B;6N.X(K  
  os.close(); @?gN &Z)I  
} Q&;d7A.@  
i(pevu  
|#rP~Nj)  
<zdo%~ba  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: P?Fm<s:  
s(3iGuT  
/EXub U73  
L3 VyW8Y  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 P-*=e8z{  
Ou'<9m!9  
9>1 $Jv3  
`tjH#W`  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 xSal=a;k  
:87HXz6]jS  
rQ!X  
p#T^o]+  
  3.close()可能会抛出异常。 "v9i;Ba>+  
YJ[Jo3M@j0  
c~=yD:$  
0s%rd>3  
  上面代码的一个更优版本为: 5( }Qg9%  
A!\-e*+W=  
GSh~j-C'  
4-dV%DgC  
OutputStream os = null; {k#RWDespy  
try{ 4\?GA`@  
  os = new OutputStream (); C $r]]MSj  
  // Do something with os here. G'\x9%  
}finally{ cYaf QyU  
  if (os != null) 61}hB>TT:  
   os.close(); (wtw1E5X  
} ^9zFAY.|  
h+!   
1}$GVb%i  
wzka4J{  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 m@W\Pic,j.  
lbg^ 2|o~~  
V.8pxD5 s  
mn;Wqb/  
  十、常见错误10#: 增加不必要的catch 块 &\_cU?0d  
?7:?OX  
8pQ:B/3=  
i H^Gv*  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 HR> X@g<c  
eK]$8l|LI  
IUJRP  
fsxZQ=-PW  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 bR*/d-v^  
jRv j:H9  
nYv`{0S+m  
Oy `2ccQ#  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: (fYrb# ]!y  
a=!I(50  
n~wNee  
L9FijF7  
try{ R>YDn|cWI  
  // Nifty code here ?[ts<Ltp  
}catch(Exception e){ 1~x=bphS  
  throw e; JnT1-=t.  
}finally{ 52L* :|b  
  // Cleanup code here (6WSQqp  
} S/XkxGZ2  
Gw;[maM!%`  
Q6r!=yOEY  
yye( ^  
  不必要的catch块被删除后,上面的代码就缩短为: W,[b:[~v  
B9-Nb 4  
>cEB ,@~  
D}| 30s?u1  
try{ [9O,C-Mk  
  // Nifty code here ezOZHY>|#  
}finally{ W97%12J3  
  // Cleanup code here J:c]z9&!  
} ]q2g[D o5  
)/:&i<Q:  
oiS>:de%tc  
H3?HQ>&O7  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 u7Xr!d+wR  
#78P_{#!  
s|1BqoE  
k$hNibpkt  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 ;{Sgv^A  
(M5{y` Kk  
!Hk$  t  
LcA~a<_  
  小结 }#rdMh  
4G%!t`? q  
.skR4f,h  
.kGlUb?^Q  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 8-wW?YTG  
y8{PAH8S  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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