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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 DQnWLC"u  
6^u(PzlA|~  
=@  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 T^G<)IX`c  
N\&;R$[9:  
,^C;1ph  
xhS/X3<th  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 ENjD~S  
2=+ ,jX{  
EIm\!'R]  
XnOl*#P  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 M3`A&*\;  
kn|l3+  
AE _~DZ:%c  
dig76D_[e  
  一、常见错误1# :多次拷贝字符串 y@JYkp>I  
XjU;oh4:.  
>L4$DKO  
/MtacR  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 7?] p\`  
ob #XKL  
tpK4 gjf  
#ySx$WT;  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: !,"G/}'^;  
axOy~%%c  
OG`O i^2  
0VPa;{i/  
String s = new String ("Text here"); zy;w07-)  
f'U]Ik;Jy  
E1_4\ S*z  
'YZs6rcJ  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: [G/X  
3Gv i!h7  
;d40:q<  
ro@BmRMW  
String temp = "Text here"; c Zr4  
String s = new String (temp);  Z.JTq~`I  
%L.+r!.  
iKY&gnu"  
_AHVMsz@  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: X_l,fu^C#$  
)v0vdAh'b  
jp`N%O]6  
w[-Bsf  
String s = "Text here"; ;Vt u8f  
D IN PAyY  
[K- s\  
6'zy"UkH  
  二、常见错误2#: 没有克隆(clone)返回的对象 >m!.l{*j>N  
q4= RE  
< .!3yy  
m Y0C7i  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: v2V1&-  
eGil`:JY"  
.YRSd  
(6{ VMQ  
import java.awt.Dimension; jFfki.H  
/***Example class.The x and y values should never*be negative.*/ wQc  w#  
public class Example{ y[rLk  
  private Dimension d = new Dimension (0, 0); 8>9+w/DL  
  public Example (){ } u'p J 9>sC  
X;NTz75  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ %Z4=3?5B"9  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ V^i3:'  
   if (height < 0 || width < 0) #v-!GK_<  
    throw new IllegalArgumentException(); ./'n2$^3  
    d.height = height; !TF VBK  
     d.width = width; IpxjP\  
  } 9iE66N>z  
:83" t-O8[  
  public synchronized Dimension getValues(){ r "R\  
   // Ooops! Breaks encapsulation E.9F~&DPJ<  
   return d; 8^lXM-G-  
  } x3 S  
}  Eqc$*=  
U<b!$"P9  
2}twt  
JSU\Hh!  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: }UrtDXhA  
<4g^c&  
S SXSgp  
/v[- KjTj7  
Example ex = new Example(); :w+Rs+R  
Dimension d = ex.getValues(); |=POV]K  
d.height = -5; x3Uv&  
d.width = -10; :-)[B^0  
H=jnCGk  
]!N5jbA@  
7-DC"`Y8e  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 c z|IBsa*  
jY kx]J%S  
1yu!:8=ee  
%0 4n,&mg  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 v|GvN|_|  
K^bn4Nr  
\w3wh*  
,n*.Yq  
  更好的方式是让getValues()返回拷贝: 5kF5`5+Vj  
t>xV]W<  
iYf4 /1IG,  
Gu= Rf`o  
public synchronized Dimension getValues(){ <_![~n$H  
return new Dimension (d.x, d.y); N5\<w>  
} Li2)~4p><  
c.fj[U|j  
"{k3~epYaN  
O,cx9N  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 ($wYaw z  
=EKJ!{  
DQ)SMqOotw  
MD7[}cB  
  三、常见错误3#:不必要的克隆 1 .M?Hp9i  
llzl-2` /  
#lO;G k{  
7XNfH@  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: "hfwj`U  
I9 E@2[=!  
&a`-NRU#  
II91Ia  
/*** Example class.The value should never * be negative.*/ AS7!FD6b  
public class Example{ eZcm3=WV|  
  private Integer i = new Integer (0); 89paR[  
  public Example (){ } 4v>V7T.  
uMI2Wnnc:/  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ j!s&yHE1  
  public synchronized void setValues (int x) throws IllegalArgumentException{ (GQy"IuFh  
   if (x < 0) ?vVkZsU  
    throw new IllegalArgumentException(); ,"'agg:St  
    i = new Integer (x); <R2  
  } Y'-Lt5SCS  
O v-I2  
  public synchronized Integer getValue(){ 4M _83WL  
   // We can’t clone Integers so we makea copy this way. $3L7R  
   return new Integer (i.intValue()); lwU&jo*@  
  } 7,1idY%cy  
} [Ue>KG62=  
4Qd g t*  
^tah4QmUA  
zE[c$KPP  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 N(9'U0z  
6-3l6q  
_VlN Z/V  
\o^+'4hq<5  
  方法getValue()应该被写为: % ;<FfS  
?o4&cCFOE  
'/j`j>'!^  
1$^{Uma  
public synchronized Integer getValue(){ 8p FSm>  
// ’i’ is immutable, so it is safe to return it instead of a copy. )"1D-Bc\Q  
return i; <ygO?m{  
} BjH(E'K[b  
 en   
$OT:J  
>eC^]#c  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: bfJDF(=h  
/EC m  
_ReQQti[  
zm e:U![  
  ?Boolean 0h7\zoZ5  
   ?Byte ESO(~X+  
   ?Character IQM!dC  
   ?Class #U1soZ7  
   ?Double MwuH.# Ez  
   ?Float \R<yja  
   ?Integer j.z#fU  
   ?Long /90@ 85%r  
   ?Short  &]euN~y  
   ?String 5}m2D='  
   ?大部分的Exception的子类 8]Pf:_e,+  
'1w<<?vX?  
u&qdrKx  
xH=&={  
 四、常见错误4# :自编代码来拷贝数组 B4.hJZ5  
L+,{*Uj[;  
WMg#pLc#  
[)*fN|Hy  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: {>z.y1  
PXkPC%j  
f5G17: Q  
`jV0;sPd;  
public class Example{ qg>i8V  
  private int[] copy; lj[Bd >  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ 3oSQe"  
  public void saveCopy (int[] data){ +|}~6`  
   copy = new int[data.length]; &pCKz[Yf+  
   for (int i = 0; i < copy.length; ++i) ^WeT3b q  
    copy = data; Kg.E~  
  } JK1b 68n  
} snyx$Qx(  
\F> *d!^C  
D^A_0@  
%|;^[^7+}t  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: WaH TzIa[  
|m=@;B|  
6G( k{S  
 "u%$`*  
void saveCopy (int[] data){ 7 724,+2N  
  try{ |BXq8Erh  
   copy = (int[])data.clone(); 0{j>u`  
  }catch (CloneNotSupportedException e){ b*bR<|dTj  
   // Can’t get here. -du+iOe?  
  } J|ILG  
} eSvu:euv  
eZUK<&0x5  
HloP NE&}  
N%T-Q9k  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: 'aCnj8B  
E J 9A 4B  
%o?fE4o'  
v!x=fjr<  
static int[] cloneArray (int[] data){ o$Jk2 7  
  try{ t'z] <7  
   return(int[])data.clone(); %TLAn[LW(  
  }catch(CloneNotSupportedException e){ t >8t|t+  
   // Can’t get here. bk8IGhO|m!  
  } D.HAp+lx  
} =^{^KHzIl3  
_ p?q/-[4  
{ }>"f]3  
sx/g5 ?zh  
  这样的话,我们的saveCopy看起来就更简洁了: 72PDqK#  
*fjarZu  
xd>2TW l#  
'8}\! i&  
void saveCopy (int[] data){ cd:O@)i  
  copy = cloneArray ( data); M HgS5b2  
} >`6^1j(3  
 1 ft. ZJ  
5Wn6a$^  
v+\E%H  
  五、常见错误5#:拷贝错误的数据 7$^V_{ej  
?"L>jr(  
s&c^Wr  
n%ld*EgY  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: AKS(WNGEp  
BG'gk#J+f  
%``FIv15w  
`E}2|9  
import java.awt.Dimension; ']qC,;2  
/*** Example class. The height and width values should never * be 2)U3/TNe  
negative. */ jL 2f74?1  
public class Example{ 5uu{f&?u)  
  static final public int TOTAL_VALUES = 10; +8~S28"Wg3  
  private Dimension[] d = new Dimension[TOTAL_VALUES];  R z[-  
  public Example (){ } ~M <4HC  
7C&`i}/t  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ !Dz:6r  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ ;aD_^XY  
   if (height < 0 || width < 0) iA%3cpIc(Z  
    throw new IllegalArgumentException(); -,Q<*)q{  
    if (d[index] == null) 3Hq0\Y"Y  
     d[index] = new Dimension(); n:7=z0 s  
     d[index].height = height; 3lKIEPf6r  
     d[index].width = width; >f_D|;EV  
  } ma-|L3 #  
  public synchronized Dimension[] getValues() ,@<-h* m  
   throws CloneNotSupportedException{ M>0~Ek%3  
    return (Dimension[])d.clone(); xE+Go  
  } smWA~Aq  
} Ir]b. 6B  
Y\j &84  
6_9w1 ,W E  
\ 0:ITz  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: AjZT- Q0L  
IPJs$PtKok  
0V1kZ.  
J H$  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ 5m_@s?P[  
  Dimension[] copy = (Dimension[])d.clone(); oE5+   
  for (int i = 0; i < copy.length; ++i){ #?aR,@n  
   // NOTE: Dimension isn’t cloneable. }p "HD R>  
   if (d != null) qT}&XK`Q^  
    copy = new Dimension (d.height, d.width); 2*Gl|@~N  
  } (spX3n%p  
  return copy; jP+4'O!s[  
} ;&[0 h)  
KnbP@!+c  
gg6&Fzp  
vnIxI a  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: J :,  
"i#!  
<nIU]}q  
n)pBK>+  
public void store (int[] data) throws CloneNotSupportedException{ \f._I+gJ  
  this.data = (int[])data.clone(); Wmp\J3  
  // OK 1AhL-Lj  
} EQ7cK63  
OD*DHC2rN]  
W}(dhgf  
 dedi6Brl  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: K_ RrSI&>  
6C)OO"Bc  
76c}Rk^  
h#;yA"j1&  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ }P^n /  
  this.data = (int[][])data.clone(); // Not OK! ukri7 n*  
} @89mj{  
public void rightStore (int[][] data){ &\1Dy}:  
  // OK! ay4|N!ExO  
  this.data = (int[][])data.clone(); 5nEvnnx0  
  for (int i = 0; i < data.length; ++i){ QAX+oy  
   if (data != null) 1)k))w9  
    this.data = (int[])data.clone(); G|H\(3hHLZ  
  } g |2D(J  
} #&DJ3(T  
 6W3}6p  
.%D] z{''  
FSH6C2  
`L:wx5?  
   六、常见错误6#:检查new 操作的结果是否为null f!1K GP  
S$V'_  
a3p|>M6E  
js2?t~E]  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: 8lbNw_U  
|/rBR!kPq  
_gU [FUBtJ  
Ih"f98lV  
Integer i = new Integer (400); bZa?h.IF  
if (i == null) ]jM D'vg^b  
throw new NullPointerException(); R|tjvp-[}  
;m;wSp  
'd/A+W  
;r8,Wx@f1C  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 .o fYFK  
Z^#7&Pv0  
.i`+}@iA  
MLL2V`vBT  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 hWuq  
k%c ?$n"  
 七、常见错误7#:用== 替代.equals sp AYb<  
c*LnLK/m  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: [?;oiEe.|  
eeuAo&L&  
+>/ Q+nh  
]_#[o S  
int x = 4; W>s<&Vb  
int y = 5; EEF}Wf$f  
if (x == y) W*VQ"CW{^]  
  System.out.println ("Hi"); >N44&W  
// This ’if’ test won’t compile. ? BBDk  
if (x.equals (y)) M*@MkN*u&  
  System.out.println ("Hi"); e?F r/n  
WqwD"WX+w  
5MiWM2"X\  
LgB}!OLQ  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 q-p4k`]  
>Utn[']~  
6eQrupa  
T*'5-WV|3t  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 =g?r.;OO  
Hs2L$TX  
XbG=H-|  
H2|w  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 69rVW~Z  
$8X?|fV)  
vChkSY([  
#16)7  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 vE{QN<6T  
%lEPFp  
YIjBKh  
x4fLe5xv  
  八、常见错误8#: 混淆原子操作和非原子操作 |1rBK.8  
c a$D|3  
R?^FO:nM%!  
uy7)9w  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: V@T G"YF  
2{ }5WH  
:Im_=S[0  
c1b@3  
public class Example{ qC IZW  
  private int value; // More code here... OB5(4TY  
  public void set (int x){ $eI[3{}X  
   // NOTE: No synchronized keyword FVL0K(V(  
   this.value = x; C\E Z8  
  } \:^$ZBQr<n  
} + ^4"  
<XGOcekG  
i_f"?X;D  
>>K) 4HYID  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: yBq4~b~[  
P0UMMn\-#  
<K|_M)/9  
| u36-  
public void increment (){ mrk Q20D  
  // This is effectively two or three instructions: (r:WG!I,  
  // 1) Read current setting of ’value’. [Fj h  
  // 2) Increment that setting. SlsMMD  
  // 3) Write the new setting back. k&@JF@_TI  
  ++this.value; l&5| =  
} q0SvZw]f1  
7| IW\  
=yfr{5}R  
7zpwP  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: &# `d8}3D  
<S TwylL  
NS<lmWx+  
V/J[~mN9  
public synchronized void increment (){ \fh.D/@  
  ++this.value; ]TqcV8Q~  
} h.=YAcR0D  
9sJbz=o]r  
;/ >~|@  
G2rxr  
  九、常见错误9#:在catch 块中作清除工作 SO8Ej)m  
Po93&qE  
$;"@;Lj%,  
o]PSyVg  
  一段在catch块中作清除工作的代码如下所示: Nf1) 5  
A~O 'l&KB  
5|Vb)QBv%  
$kkdB,y  
OutputStream os = null; F1gDeLmJ  
try{ Oj~k1+*  
  os = new OutputStream (); {n #  
  // Do something with os here. $F;$-2  
  os.close(); d ID] {  
}catch (Exception e){ sRt|G  
  if (os != null) P4Wd=Xoz6  
  os.close(); (47jop0RDQ  
} jAN(r>zVL  
Ff%m.A8d,4  
l.fNkLC#  
l<GRM1^kU  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: ~d){7OG  
) Q~Q .  
5N`g  
Br1JZHgA  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 F_\\n#bv  
tgc&DT; E  
&A=d7ASN=  
9`-ofwr'|  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 ]^ZC^z;H  
2|w(d  
=@w};e#D  
A3!NEFBK  
  3.close()可能会抛出异常。 iTqv=  
Ba!`x<wa  
2ggW4`"c  
/.7x[Yc  
  上面代码的一个更优版本为: pl|< g9  
$?ke "  
6L'cD1pu  
:8yrtbf$  
OutputStream os = null; K xh)'aal  
try{ ,&z_ 2m  
  os = new OutputStream (); ,7 >_Lp_v  
  // Do something with os here. q2&&n6PYW  
}finally{ ~'v^__8  
  if (os != null) r(J7&vR}h  
   os.close(); ' G) Wy|*  
} I{B8'n{cN  
klv^310  
Scxf5x-  
Y2<Z"D`  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 LEHlfB#z`@  
|I85]'K9a  
}yS"C fM  
rbQA6_U 5A  
  十、常见错误10#: 增加不必要的catch 块 5wP(/?sRy  
kX5v!pm[  
wz>j>e6k`  
-}PD0Pzg;=  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 [ivJ&'vB  
JFR,QUT  
TS-m^Y'R  
mY dU`j  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 G4=%<+  
HPtaW:J  
!i#;P9K  
V@e0VV3yx%  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: /rKrnxw  
#^xiv/ sV  
~wh8)rm  
Ca?pK_Y  
try{ AO>K 6{  
  // Nifty code here _EjS(.e/=  
}catch(Exception e){ /`:5#O  
  throw e; O:p~L`o>>  
}finally{ s:wLEj+  
  // Cleanup code here cg$7`/U  
} #HM0s~^w&  
[u,B8DX  
DV{Qbe#In  
B7N?"'$i  
  不必要的catch块被删除后,上面的代码就缩短为: EDL<J1%  
J cvK]x  
9QWS[E4  
;t[<!  
try{ +#'exgGU^[  
  // Nifty code here a+r0@eFLc  
}finally{ v<3i~a  
  // Cleanup code here &[23DrI8  
} lq1pgM?Kf  
V..m2nQj  
7}TjOWC  
EQu M|4$ix  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 Z78&IbR  
!{r Gt`y  
i#(T?=VPcy  
(fY(-  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 LT:KZ|U9  
  7&l  
!oM 1  
V&)-u(s_S/  
  小结 *hFT,1WE=+  
vF1] L]z:?  
!mq+Oz~  
7 tit>dJ  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 HQv#\Xi1  
eX;"kO  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

您目前还是游客,请 登录注册
欢迎提供真实交流,考虑发帖者的感受
认证码:
验证问题:
3+5=?,请输入中文答案:八 正确答案:八