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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 [I*BEJ;W'  
`(j}2X'[  
9UD~$_<\  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 SKx&t-  
B>dXyo  
CO25  
Pb05>J3N  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 fD8A+aA  
`mU'{  
[C@0&[[  
oM`[&m.,  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 -5 -X[`cF  
S`yY<1[O  
: b^\O  
]YF[W`2h  
  一、常见错误1# :多次拷贝字符串 aBX^Wd  
Z-(Vfp4  
l`s_Id#  
tOn_S@/r  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 n !ty\E  
1-.UkdZ}  
X|Gsf= 1S  
e<_p\LiOS  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: vh8{*9+  
Eeem y*U  
mz\d>0F U.  
_KSYt32N  
String s = new String ("Text here"); Jj:4l~b,w  
&r \pQ};  
VH3 j  
fL[(;KcAa  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: n GE3O#fv  
5Qb%g )jZ  
H]a;<V9[  
&M$s@FUY  
String temp = "Text here"; O9>& E;`5  
String s = new String (temp); (;^VdiJ  
1n7tmRl  
q5il9*)d (  
x%kS:!  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: g0-hN%=6  
g0@i[&A@{  
Q>y2C8rnJ/  
9;3f`DK@2k  
String s = "Text here"; +'qzk>B  
:( A5 ,$  
=k]RzeI  
<5*cc8  
  二、常见错误2#: 没有克隆(clone)返回的对象 q=8I0E&q  
CrHH Ob  
SdF*"]t  
3| 5Af  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点:  2lw0'  
Lx&2)  
mO$]f4}  
)ymd#?wq  
import java.awt.Dimension; JCNZtWF  
/***Example class.The x and y values should never*be negative.*/ Q5'DV!0aSv  
public class Example{ 6AgevyVG  
  private Dimension d = new Dimension (0, 0); Qn> 0s  
  public Example (){ } (I~-mzu\  
BR5r K  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ )cc:Z7p  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ :4|W;Lkd!  
   if (height < 0 || width < 0) [4,=%ez  
    throw new IllegalArgumentException(); y~_wr}.CS  
    d.height = height; o$I% 1  
     d.width = width; &-#!]T-P:E  
  } <_&H<]t%rI  
> t *+FcD  
  public synchronized Dimension getValues(){ kDuN3  
   // Ooops! Breaks encapsulation il=y m  
   return d; |}paa  
  } A$G>D3  
} IDbqhZp(  
Y*iYr2?;  
\gferWm  
TqK`X#Zq  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: =\Td~>  
=s"_! 7  
%<%ef+*  
xcfEL_'o  
Example ex = new Example(); l0Wp%T  
Dimension d = ex.getValues(); h%MjVuLn  
d.height = -5; " SkTVqm  
d.width = -10; c%Y%c2([  
Ij>IL!  
X $LX;Lv  
Y85M$]e,  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 U CzIOxp}  
S0C 7'H%?#  
Y9fktg.  
#N\kMJl$l  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 \nM$qr'`B  
 6jFc'  
CqQ>"Y  
o9+ "6V|.  
  更好的方式是让getValues()返回拷贝: 4bD^Kc 4\  
x_lCagRGC4  
4R-Y9:^t  
]Ga}+^  
public synchronized Dimension getValues(){ 8/X#thG  
return new Dimension (d.x, d.y); w=>~pYASH  
} 4PUSFZK?  
fMRBGcg7Dc  
7!Z\B-_,  
-MZ LkSU  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 :lQl;Q -e  
Hm@+(j(N96  
Vrl)[st!;I  
fNOsB^Y  
  三、常见错误3#:不必要的克隆 =E@wi?  
kW>Q9Nc=V  
](yw2c;m e  
~[bS+ ]d!  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: i{zg{$U  
UD6D![e  
'3B`4W,  
1SoKnfz{6  
/*** Example class.The value should never * be negative.*/ L<bZVocOb_  
public class Example{ 46c7f*1l  
  private Integer i = new Integer (0); ,@"Z!?e  
  public Example (){ } =qH9<,p`H  
EMME?OW$  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ ^LgaMmz  
  public synchronized void setValues (int x) throws IllegalArgumentException{ &RQQVki3  
   if (x < 0) =~Oi:+L  
    throw new IllegalArgumentException(); rug^_d=B  
    i = new Integer (x); K 8CjZpzq  
  } `WvNN>R  
(u`[I4z`  
  public synchronized Integer getValue(){ %/!n]g-  
   // We can’t clone Integers so we makea copy this way. hXr`S4aJ  
   return new Integer (i.intValue()); e6n1/TtqM  
  } !l!^`c  
} (.Tkv Uj`  
i1RU5IRy|j  
tX)l$oRPr  
*oLAO/)n  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 sdP% Y<eAT  
MkJ}dncg*  
gIv :<EJ9  
[v$_BS#u^3  
  方法getValue()应该被写为: J~7E8  
v%c r   
b'Cy!dr  
No8~~  
public synchronized Integer getValue(){ <1#v}epD#  
// ’i’ is immutable, so it is safe to return it instead of a copy. V*P3C5 l  
return i; c$aTl9e  
} (3YqM7cqt  
OH~X~n-Z  
ud xLHs  
&Npv~Iy  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: yIC.Jm D*  
#q.Q tDz  
gbNPD*7g9  
n]I_ LlbY  
  ?Boolean ct='Z E  
   ?Byte j3 d=O!  
   ?Character .-[uQtyWW  
   ?Class n\k6UD  
   ?Double q]Gym 7o  
   ?Float o"D`_ER  
   ?Integer DArEIt6Q  
   ?Long [OJ@{{U%  
   ?Short Ep-{Ew{T_=  
   ?String v w$VR PW  
   ?大部分的Exception的子类 .&d]7@!qy  
|@pJ]  
Gs$<r~Tg  
mlCw(i,  
 四、常见错误4# :自编代码来拷贝数组 5P_%Vp`B2  
cF{5[?wS  
zRtaO'G(  
t6p}LNm(V  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: pQr `$:ga  
xi=Z<G  
JzH\_,,  
0KqGJ :Ru  
public class Example{ +f/G2qY!t  
  private int[] copy; D&_Ir>"\  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ !FOPFPn  
  public void saveCopy (int[] data){ VQE8hQ37  
   copy = new int[data.length]; z:f[<`,GT  
   for (int i = 0; i < copy.length; ++i) tK)E*!  
    copy = data; *k'D%}N:  
  } <%klrQya  
} vU Bk oC2Q  
|__\Vn  
%Y8#I3jVJ  
q,-bw2   
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: xEtzqP<]  
3DRbCKNL  
UlQZw*ce  
]$/TsN  
void saveCopy (int[] data){ (!kOM% 3{  
  try{ KB+,}7  
   copy = (int[])data.clone(); S)Cd1`Gf  
  }catch (CloneNotSupportedException e){ g+BW~e)  
   // Can’t get here. i/QE)"B"q  
  } c/.U<  
} N}x \Ll  
}8cL+JJU  
m@o/W  
TNBFb_F  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: xvP<~N-  
yiyyw,iy  
WP&P#ju&  
\y?Vou/  
static int[] cloneArray (int[] data){ /NFv?~</k  
  try{ W 0^.Dx  
   return(int[])data.clone(); A `\2]t$z  
  }catch(CloneNotSupportedException e){ nokk! v/  
   // Can’t get here. td-2[Sy  
  } $h1`-=\7  
} LY}%|w  
vgRjd1k.\y  
&L}e&5  
0-#SvTf>;:  
  这样的话,我们的saveCopy看起来就更简洁了: [eL?O;@BD  
0eq="|n^|  
O~yPe.  
+=#sa m*i  
void saveCopy (int[] data){ KJc fbZ~  
  copy = cloneArray ( data); [*zB vj}G  
} HFYN(nz}[  
qPsf`nI7  
u '-4hU  
TR3_!0  
  五、常见错误5#:拷贝错误的数据 hX4&B  
^n#6CW*n  
`Q?rQ3A}  
S'T&`"Mr  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: Cv{>|g#  
0g% `L_e_  
tqyR~  
Zh.5\&bm  
import java.awt.Dimension; '5zolp%St  
/*** Example class. The height and width values should never * be IB#L5yN r  
negative. */ `hYj0:*)S$  
public class Example{ T7vilfO5G  
  static final public int TOTAL_VALUES = 10; u50 o1^<X  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; " O1\]"j  
  public Example (){ } 27q 9zi!Q  
R}lS@w1  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ B-`d7c5  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ o= VzVg  
   if (height < 0 || width < 0) (xw)pR  
    throw new IllegalArgumentException(); e"HA.t[A  
    if (d[index] == null) j4H]HGHv  
     d[index] = new Dimension(); ]kUF>Wp  
     d[index].height = height; BL1$ ~0  
     d[index].width = width; EhDKh\OY5  
  } .}gGtH,b3  
  public synchronized Dimension[] getValues() ihjs%5Jo%  
   throws CloneNotSupportedException{ B|E4(,]^  
    return (Dimension[])d.clone(); v-u53Fy  
  } 7+wy`xi  
} /IS_-h7>XS  
^g/    
4'JuK{/ A7  
&P 'cf|KI  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: (VeX[*}I  
b 'p0T1K(  
4PG]L`J{  
\fG?j@Qx  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ Htd-E^/  
  Dimension[] copy = (Dimension[])d.clone(); KhK:%1po  
  for (int i = 0; i < copy.length; ++i){ Gkci_A*  
   // NOTE: Dimension isn’t cloneable. @-y.Y}k#$~  
   if (d != null) UMsJg7~  
    copy = new Dimension (d.height, d.width); *aF#on{  
  } Dizc#!IGU  
  return copy; >t_5( K4  
} 5e tbJk  
#(6^1S%  
uCGJe1!Ai>  
=\mAvVe  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: ]hY'A>4Uq  
?;NC(Z,  
9UlR fl  
AwrW!)n }  
public void store (int[] data) throws CloneNotSupportedException{ 4^h_n1 A  
  this.data = (int[])data.clone(); 4%#Y)z o.e  
  // OK n[$bk_S  
} |HhqWja  
J`/t;xk  
>*/\Pg6^  
Q;A1&UA2  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: =+24jHs  
+[386  
7,0^|P  
G&qO{" Js  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ tKtKW5n~  
  this.data = (int[][])data.clone(); // Not OK! F*" "n  
} wyF' B  
public void rightStore (int[][] data){ +u+|9@  
  // OK!  l* C>  
  this.data = (int[][])data.clone(); ^Pqj*k+F  
  for (int i = 0; i < data.length; ++i){ XV)<Oavs  
   if (data != null) jI})\5<R  
    this.data = (int[])data.clone(); <Uj~S  
  } epw*Px  
} 8 nCw1   
^5j+O.zgN  
zJC!MeN  
F91uuSSL  
f|U;4{ k  
   六、常见错误6#:检查new 操作的结果是否为null s|*0cK!K^  
)IN!CmpN  
&/XRiK1"0  
GQ=Zp3[  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: OCR`1  
'T G43^  
}G8gk"st  
z4 GcS/3K  
Integer i = new Integer (400); )UBU|uYR\  
if (i == null) %eK=5Er jx  
throw new NullPointerException(); Sg#$ B#g  
x"/DCcZ  
k:1p:&*m  
aMa ICM  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 C(N' +VV_  
/ =]h@m-`  
SP}!v5.  
(>~:1  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 `" BFvF#  
H&$L1CrdL  
 七、常见错误7#:用== 替代.equals qUNK Dt  
}le}Vuy\s  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: T!![7Rs  
c~1+5&  
0PfjD  
B49: R >  
int x = 4; 6-"@j@l5<  
int y = 5; Vr/UY79  
if (x == y) (2 nSZRB  
  System.out.println ("Hi"); EI+RF{IKh  
// This ’if’ test won’t compile. Dn x` !  
if (x.equals (y)) ?w^MnK0U)  
  System.out.println ("Hi"); c? Z M<Y"  
A kMP)\Q  
}57s  
ZLP)i;Az  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 ,"6Bw|s  
& OO0v*@{  
g=G>4Ua3  
.D X  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 m5c=h  
OKW}8qM  
z@za9U`6i  
nZtMF%j'  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 &A/k{(.XP  
4F[4H\>'  
7'IcgTWDZy  
=()Vrk|uK  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 D*T*of G  
Ms4~P6;%  
r6WSX;K  
Z;v5L/;  
  八、常见错误8#: 混淆原子操作和非原子操作 'dXGd.V7u  
K_SURTys  
3@}rO~  
zD"n7;  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: rXh*nC  
r`dQ<U,  
U# +$N3%  
-uk}Fou  
public class Example{ u; ]4 ydp  
  private int value; // More code here... gWK[%.Jnw  
  public void set (int x){ qV$\E=%fhM  
   // NOTE: No synchronized keyword [SKN}:D  
   this.value = x; 0Dt-!Q7  
  } Ji#eA[  
} ax)>rP,V  
Q9G\T:^ury  
?)-#\z=6G  
\&8 61A;  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: yg@8&;bP`  
_O,k0O   
Q[n*ce7L0  
c1Rn1M,2k  
public void increment (){ ^-^ii 3G`  
  // This is effectively two or three instructions: 634OH*6  
  // 1) Read current setting of ’value’. te[#FF3{  
  // 2) Increment that setting. m;4qs#qCg?  
  // 3) Write the new setting back. n^lr7(!6  
  ++this.value; luWr.<1  
} ,=IGqw  
7g7[a/Bts  
GQH15_  
.&i_~?1[N  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: @sdHB ./  
+0l-zd\  
Q\W?qB_  
{*PbD;/f  
public synchronized void increment (){ WGwIc7  
  ++this.value; % lK/2-  
} f1$'av  
<9dfbI)  
YB}m1 g`  
4{lrtNd~K  
  九、常见错误9#:在catch 块中作清除工作 ^TZ`1:oL#  
;Yve m  
+HT?> k  
H$ZLtPv5  
  一段在catch块中作清除工作的代码如下所示: 91#rP|88;  
;5 p;i 8m  
wJc`^gj  
Y"  Ut  
OutputStream os = null; oQiRjDLx  
try{ }&LVD$Bz  
  os = new OutputStream (); R>D[I.  
  // Do something with os here. R wTzS;  
  os.close(); <kCOg8<y :  
}catch (Exception e){ @P )2ZGG  
  if (os != null) Di"Tv<RlQ  
  os.close(); koa-sy)#L  
} 8l l}"  
q o6~)Aws  
&_$0lI DQ  
r_hs_n!6  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: >ZwDcuJ~Lz  
*djVOC  
) ^`V{iD  
G]n_RP$G  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 Bra}HjHO  
-#Ys67,4N  
JJHO E{%  
9Ca }+  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 b_vKP  
xj[v$HP  
Y SB~04  
?,`g h}>  
  3.close()可能会抛出异常。 ]++,7Z\AU  
,m Nd#  
d{Cg3v`Rd  
Oz4vV_a&'  
  上面代码的一个更优版本为: 0j :u.x  
6rMXv0)  
TWM^5 L:U  
W#@6e')d  
OutputStream os = null; j#jwK(:]  
try{ 7?;ZE:  
  os = new OutputStream (); P0/Ctke;  
  // Do something with os here. xOx=Z\ c  
}finally{ /Un\P   
  if (os != null) - -\eYVh[  
   os.close(); qjsEyro$-  
} " ?Ux\)*  
ti^=aB   
H0f]Swh0a  
tM|/OJ7  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 t)5.m}  
if?X^j0  
e>m+@4*sn  
t$3B#=  
  十、常见错误10#: 增加不必要的catch 块 wBJ|%mc3TA  
t*J?#r  
!>#gm7  
ceuEsQ}  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 ..R JHa6B  
q`3HHq  
eH V#Mey[  
PpLiH9}  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 =$y;0]7Lwi  
H)h$@14xu  
I7\T :Q[  
qe5;Pq !G  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: cw,|,uXq 6  
xn>N/+,  
M.\XG}RR  
Y!`  pF  
try{ jwg*\HO,s  
  // Nifty code here pD!j#suMA  
}catch(Exception e){ <=Saf.  
  throw e; 'jXJ!GFw  
}finally{ f _Hh"Vh  
  // Cleanup code here h1)ny1;  
} D~FIv  
IML.6<,(Z  
CkRilS<  
nIZsKbnw  
  不必要的catch块被删除后,上面的代码就缩短为: E[i#8_  
I/%L,XyRI  
29l bOi  
RG=i74a  
try{ ->*~e~T  
  // Nifty code here ]T{v~]7:{  
}finally{ JAM]neKiX  
  // Cleanup code here dOK]Su  
} KF1Zy;  
} lXor~_i  
DS9-i2  
2r!- zEV  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 qnb/zr)p  
hE E1i  
Z^BZH/I?  
PC\p>6xT  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 CED[\ n  
A9KPU:  
Kf6 D)B 26  
YCVT0d  
  小结 <(_Tanx9Q  
{6O} E9  
P @J)S ?  
| N}*  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 ;Ea8>  
dq%C~j{v  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

您目前还是游客,请 登录注册
如果您提交过一次失败了,可以用”恢复数据”来恢复帖子内容
认证码:
验证问题:
10+5=?,请输入中文答案:十五