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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 T?Z&\g0yp  
?+Vi !eS  
If&y 5C  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 x2HISxg  
PMbq5  
T <k;^iqR  
D-i, C~W  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 6'uCwAQU  
aYc<C$:NC"  
b-<@3N.9]  
%`]!atH  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 Y+g(aak+.  
WLVkrTvX  
8a8D0}'  
<RC%<  
  一、常见错误1# :多次拷贝字符串 rhaq!s38:  
P&[&Dj  
#E\6:UnT  
%8Y+Df;ax  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 5{DwD{Q  
-U_,RMw~  
X6w+L?A  
- 3PLP$P  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: ([rSYKpi  
sy4Nm0m  
ld({1jpX,  
!v%>W< 3Q  
String s = new String ("Text here"); G8?Do+[  
8 ?y|  
h|Qb:zEP,  
O<@L~S]  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: "szJ[ _B  
*h).V&::O  
c3#eL  
QKVOc,Fp7i  
String temp = "Text here"; [wQJVYv  
String s = new String (temp); Z1$U[Tsd  
CZ$B2i6  
/yx)_x{  
:mLXB75gH  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: ywyg(8>zE  
Mty[)+se  
; 5[W*,7s  
^liW*F"UY  
String s = "Text here"; L+@X]O W8  
3q@JhB  
(ToD u@p  
]WcN6|b+  
  二、常见错误2#: 没有克隆(clone)返回的对象 w0H#M)c  
.EjR<UU  
)^6Os2  
{;u+?uY  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: L5|g \Y`  
fsnZHL}=n  
HmU6:8V *Z  
`pDTjJ  
import java.awt.Dimension; +`V<& Y-5l  
/***Example class.The x and y values should never*be negative.*/ '+g[n  
public class Example{ v*As:;D_  
  private Dimension d = new Dimension (0, 0); ?0JNaf  
  public Example (){ } W*(- * \1[  
9OY ao  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ DYgB_Iak  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ _T8S4s8q  
   if (height < 0 || width < 0) 9^Web~yi#  
    throw new IllegalArgumentException(); MI:%Eq  
    d.height = height; d`5AQfL&  
     d.width = width; YvP62c \  
  } 9~a5R]x2  
P-8QXDdr  
  public synchronized Dimension getValues(){ &u6n5-!v  
   // Ooops! Breaks encapsulation =i;T?*@  
   return d; OpIeo+^X*  
  } oj'YDQ^uj  
} O?A%  
31e O2|7  
^~bd AO81  
4UzXTsjM7  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: E:A!tu$B  
N{@~(>ee^  
B/n~ $  
e0Gs|c+6  
Example ex = new Example(); S}]B|Q  
Dimension d = ex.getValues(); OZ"76|H1`  
d.height = -5; !g=b=YK  
d.width = -10; s&$e}yxVO  
Zv-1*hhHf  
0E (G1o'  
&0%B3  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 ORWi+H|  
]A#:Uc5  
MOp "kA  
W_3BL]^=  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 M_r[wYt!  
)<_qTd0`  
2*Pk1 vrI  
!u  .n  
  更好的方式是让getValues()返回拷贝: # kNp);  
8?: 2<  
+|5 O b  
D+~*nc~ g  
public synchronized Dimension getValues(){ e5 zi"~  
return new Dimension (d.x, d.y); )vVf- zU  
} +KNd%AJ  
qZ@d:u  
mieyL9*n7  
hJir_=  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 ssoE,6kS  
oK4xRv8Hd  
];b+f@  
V3d$C&<(  
  三、常见错误3#:不必要的克隆 3=} P l,  
{{gt>"D,  
T-/3 A%v  
|R!ozlL{}  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: k9:|CEP  
#)[.Xz:U  
y*US^HJOZ  
<63TN`B  
/*** Example class.The value should never * be negative.*/ aD_7^8>  
public class Example{ a1%}Ee  
  private Integer i = new Integer (0); wrXn|aV  
  public Example (){ } } _^ vvu  
3#>%_@<  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ }4h0 {H  
  public synchronized void setValues (int x) throws IllegalArgumentException{ :2C <;o  
   if (x < 0) >Q[ Z{  
    throw new IllegalArgumentException(); |k%1mE(+=s  
    i = new Integer (x); 5 ddfdIp  
  } S'NLj(  
]IeLKcn  
  public synchronized Integer getValue(){ :)tsz;  
   // We can’t clone Integers so we makea copy this way. V d]7v  
   return new Integer (i.intValue()); |GsMLY:0  
  } Wv;,@xTZ  
} ?.lo[X<,*  
_Rk vg-  
dn Sb}J  
f\.y z[  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 cx&\oP  
Y5;afU='  
}&2,!;"">3  
`<| <1,  
  方法getValue()应该被写为: U|\ .)h=  
6KXW]a `  
c14d0x{  
u GqeT#dP  
public synchronized Integer getValue(){ <hTHY E=  
// ’i’ is immutable, so it is safe to return it instead of a copy. #M+_Lk3  
return i; F[!%,-*  
} |JHNFs  
,Oy$q~.  
EBz4k)@m  
Z2H bAI8  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: U,61 3G  
nKnrh]hX  
eMmNQRmH  
#d/T7c#  
  ?Boolean hlze]d?z  
   ?Byte bqp^\yu-E  
   ?Character $8AW  
   ?Class $|3zsi2  
   ?Double 84WcaH  
   ?Float 6-)WXJ@V  
   ?Integer T JZ~Rpq  
   ?Long rXE0jTf:a  
   ?Short !IO\g"y~|%  
   ?String N mxh zjJ  
   ?大部分的Exception的子类 lcjOBu  
-qHG*v,  
j6XHH&ZEb  
m.1-[2{8~  
 四、常见错误4# :自编代码来拷贝数组 X#ud5h  
v>Kh5H5e~  
g;6/P2w  
o^* :  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: pL`Q+}c}  
#=33TvprR2  
 G +41D  
O"\_%=X9  
public class Example{ bGK*1FlH  
  private int[] copy; Bmcc SC;o4  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ m6$&yKQ-=h  
  public void saveCopy (int[] data){ "e8EA!Ipte  
   copy = new int[data.length]; &ni#(   
   for (int i = 0; i < copy.length; ++i) Nq8 3 6HL  
    copy = data; u~Po5W/i  
  } gW--[  
} >wt.)c?5  
kD%MFT4  
y%61xA`#  
bu_@A^ys  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: d,(q 3  
U1E@pDH  
\/;c^!(<  
fR'!p: ~  
void saveCopy (int[] data){ bn8maYUZ  
  try{ |)Dm.)/0)  
   copy = (int[])data.clone(); z 5(5\j]  
  }catch (CloneNotSupportedException e){ "c]9Q%  
   // Can’t get here. JehanF[  
  } ]Sa#g&}T>  
} 8]`s&d@GY  
GIcq|Pe  
z uW4gJ  
HR8YPU5  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: X';qcn_^  
V6HZvuXV!  
Mog [,{w  
C,W_0= !e  
static int[] cloneArray (int[] data){ A:GqR;;"x>  
  try{ .PVYYhrt  
   return(int[])data.clone(); Y9<[n)>+  
  }catch(CloneNotSupportedException e){ :/szA?:W  
   // Can’t get here. rg k1.0U0  
  } 3'`&D/n  
} Y$n+\K  
f77W{T4  
L/-SWid)  
F1-"yX1B  
  这样的话,我们的saveCopy看起来就更简洁了: 7z1@XO<D  
7=}tJ  
r0lI&25w  
<Z3C&BM  
void saveCopy (int[] data){ ~K3Lbd| r  
  copy = cloneArray ( data); stUv!   
} % H"  
C=q&S6/+  
h'=)dFw7  
2#lpIj  
  五、常见错误5#:拷贝错误的数据 g_P98_2f.k  
(_@5V_U  
?&eS}skL  
-bOtF%  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: Cy6!?Mik  
w`f66*@Q1  
#iv4L  
SH=S>  
import java.awt.Dimension; I5l%X{u"N  
/*** Example class. The height and width values should never * be 9=]HOUn  
negative. */ [qRww]g;P|  
public class Example{ =0Y0o_  
  static final public int TOTAL_VALUES = 10; UR _Ty59  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; sfw* _}y  
  public Example (){ } x,10o   
&`n:AR`  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ p19(>|$J  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ .$x}~Sw  
   if (height < 0 || width < 0) ojf6@p_  
    throw new IllegalArgumentException(); <5pNFj}0;X  
    if (d[index] == null) ?zutU w/m  
     d[index] = new Dimension(); *v K~t|z  
     d[index].height = height; a BMV6'  
     d[index].width = width; x/M$_E<G  
  } e4Y+u8gT  
  public synchronized Dimension[] getValues()  XA;PWl5!  
   throws CloneNotSupportedException{ R--s u:  
    return (Dimension[])d.clone(); 2 SD Z  
  } &R4?]I  
} (n?f016*%d  
_zM?"16I}  
db_?da;!`  
R0*P,~L;|  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: {-me;ayk  
@^YXE,  
'R+^+urq^  
VpHwc!APq  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ e\[q3J  
  Dimension[] copy = (Dimension[])d.clone(); b' M"To@  
  for (int i = 0; i < copy.length; ++i){ lrKT?siB  
   // NOTE: Dimension isn’t cloneable. ,pTZ/#vP#  
   if (d != null) W? 4:sLC#3  
    copy = new Dimension (d.height, d.width); Y#V(CIDe  
  } x+6z9{O  
  return copy; urx?p^c  
} J9 NuqV3  
P}gtJ;  
ZZ^A&%E(a  
`^8mGR>OpI  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: oz{X"jfu  
Ar/P%$Zfq  
W[)HFh(#  
hkb\ GcOj  
public void store (int[] data) throws CloneNotSupportedException{ kDm uj>D  
  this.data = (int[])data.clone(); @aUNyyVP  
  // OK >@bU8}rT  
} +<xQF  
@"fv[=Xb  
!=.y[Db=  
eza"<uBr  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: |-\anby<  
Y)]VlV!`  
C/N;4  
[O_5`X9|  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ wAi7jCY%OY  
  this.data = (int[][])data.clone(); // Not OK! ^znUf4N1  
} jmq^98jB  
public void rightStore (int[][] data){ &glh >9:G  
  // OK! Pz2Q]}(w  
  this.data = (int[][])data.clone(); ~gZ1*8 s`  
  for (int i = 0; i < data.length; ++i){ [olSgq!3  
   if (data != null) jsgDJ}  
    this.data = (int[])data.clone(); R#~l[S8u^  
  } *.wj3' wV  
} :EHk]Hkz  
DpmAB.  
oO?+2pTQV  
]=-=D9ZS3  
@(6i 1Iwu9  
   六、常见错误6#:检查new 操作的结果是否为null a6z0p%sIZ  
pwHe&7e#  
+ XBF,<P  
A ?V-Sz#  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: v ))`U,Gm  
{RI^zNgs[  
)_! a:  
S#p_Y^A  
Integer i = new Integer (400); z0ufLxq  
if (i == null) Il@K8?H@  
throw new NullPointerException(); >ZPu$=[W  
[Nm?qY  
4x+[?fw  
Q/Z>w+zh#  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 Zi}h\R a  
&${| o@  
o?M;f\Fy  
TeZu*c  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 h2mHbe43  
\oxf_4X  
 七、常见错误7#:用== 替代.equals ShV_8F z  
5 8;OTDR!  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: CfrO1iF  
& }j;SK5  
*< fJgc"3  
p(GI02|n  
int x = 4; CL5^>. }  
int y = 5; "-Ny f  
if (x == y) v4rO 0y=C  
  System.out.println ("Hi"); GGHeC/4  
// This ’if’ test won’t compile. Iy*Q{H3[  
if (x.equals (y)) i}>EGmv m  
  System.out.println ("Hi"); NqKeQezX  
8|i<4>  
c%b|+4 }x  
7],y(:[=v  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 P;gd!Yl<-  
{*hGe_^  
{y@8E>y5$  
=$#5Ge]b  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 OC,yLQ  
4n(w{W>  
.%W.uF^  
45%D^~2~F  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 M"K$.m@t  
d<=!*#q;o  
/03 Wst  
P>~Usuf4  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 @Bkg<  
RlvvO  
T&S=/cRBK}  
G1zP^ogk  
  八、常见错误8#: 混淆原子操作和非原子操作 e9:pS WA-n  
Q8l vwip  
gxI/MD~!>  
c(8>oeKyD  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: k:j?8o3  
`]19}GK~xo  
M!gu`@@}F  
438r]f?0|{  
public class Example{ n?q+:P  
  private int value; // More code here... {s6#h#U  
  public void set (int x){ 7bGt'gvv  
   // NOTE: No synchronized keyword %O[1yZh \  
   this.value = x; FoYs<aER  
  } /s(PFN8#Y  
} n2c(x\DA&  
9"B;o  
U~7{q >  
lQ [JA[  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: =:R${F  
dYwEVu6q  
9~K>c  
U/v)6:j)4R  
public void increment (){ "J}B lB  
  // This is effectively two or three instructions: u0[O /G  
  // 1) Read current setting of ’value’. j[$+DCO#|m  
  // 2) Increment that setting. b=WkRj  
  // 3) Write the new setting back. kwS[,Qy\  
  ++this.value; [CV0sYEA  
} l96 AJB'  
\b)P4aL  
=:xJZy$  
_m#TL60m  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: 'rfs rZ?  
BTA2['  
<X1[j9Qtv0  
[5b[ztN%  
public synchronized void increment (){ 0U.Ld:  
  ++this.value; l`j@QP  
} ^ 6t"A  
Cf<TDjU`|  
xw1,Wbu]  
"4*QA0As  
  九、常见错误9#:在catch 块中作清除工作 cZWW[i  
4l/~::y  
.Z17X_  
+@@( C9  
  一段在catch块中作清除工作的代码如下所示: 5':j=KQE_  
h=NXU9n%'  
4dSAGLpp  
6,R<8a;Wn  
OutputStream os = null; >Ij# +=  
try{ l,b_' m@  
  os = new OutputStream (); qX[C%  
  // Do something with os here. +$^ [ r  
  os.close(); [R~@#I P!  
}catch (Exception e){ M&/e*Ta5  
  if (os != null) hNp.%XnnZ  
  os.close(); IeIv k55  
} oye/tEMG  
d;r,?/C  
Z\)P|#L$  
7:.!R^5H  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: ;:)u rI?  
6H|T )  
WCI'Kh   
PCKxo;bD  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 fjQIuM  
kY~yA2*G  
,fm{ krE  
bXHtw} n  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 Z>'hNj)ju  
6HVGqx  
z-S8s2.Fd  
I?<ibLpX  
  3.close()可能会抛出异常。 kf)s3I/`(  
<|a9r: [  
23zR0z(L  
-]Oi/i,{  
  上面代码的一个更优版本为: wS:`c J  
F2=#\U$  
J\I`#  
8O*O 5   
OutputStream os = null; 6 )Qe*S  
try{ dSzq}w4xY  
  os = new OutputStream (); k0DX|O8mXV  
  // Do something with os here. OadGwa\:s  
}finally{ QVR-`d/  
  if (os != null) 9Bu=8P?  
   os.close(); UWBR5  
} ) .H nK  
K5d>{c  
xkz`is77Y@  
q +c~Bd  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 Fw"x4w  
dC">AW  
'KNUPi|  
?vP }#N!=d  
  十、常见错误10#: 增加不必要的catch 块 e(-Vp7vXG  
4f,%@s)zn  
}e,*'mCC*  
9kU|?JE  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 js=w!q0)9  
*>Zq79TG  
XZPq4(,9}  
(K> 4^E8  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 d!q)FRzi  
wQ9fPOm  
}9&~+Q2  
9t0NO-a  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: n11eJEtm  
9uY$@7qH  
> bSQ}kXe  
zeHs5P8}r  
try{ ]P(_ d'}  
  // Nifty code here sMb+4{W&6  
}catch(Exception e){ 7ZpU -':  
  throw e; -M?s<R[&  
}finally{ ("@ih]zYf  
  // Cleanup code here pS)/yMlVj  
} j&=!F3[  
J.npv1F  
sMqAuhw$.  
+P 9h%/Yk  
  不必要的catch块被删除后,上面的代码就缩短为: XiUae{j`  
>c8EgSZJ  
>1d`G%KfG  
,7|2K&C5  
try{ r;&rc:?A  
  // Nifty code here :mz6*0qW  
}finally{ kA<58 ,!  
  // Cleanup code here Y- c_ 2 )  
} C+c;UzbD  
^Zw1X6C5~  
&]HY:  
62%=%XD  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 #s^~'2^%4  
pD%Pg5p`  
v`pIovn  
"EC,#$e%ev  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 rQPV@J]:  
L(eLxw e%  
4o*wLCo7^  
Ny)!uqul*  
  小结 {%3sj"suB  
f\gN+4)  
`G^MTDp?L+  
6N'v`p8  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 N!:&Xz  
|\/Y<_)JD  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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