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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 F!do~Z  
$8FUfJ1@  
n:X y6H  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 = / 8cp  
3a|\dav%  
m kexc~l  
oU/5 a>9~  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 3o qHGA:}  
{b{s<@?  
54/=G(F   
(w{j6).3Dj  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 %3 rP `A  
-HuA \0J  
x"~JR\yzKJ  
wS*E(IAl  
  一、常见错误1# :多次拷贝字符串 Y ay?=Y{  
*h|U,T7ew  
A=4OWV?  
/ j^  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 0`hdMLONR  
n*$ g]G$  
Je{ykL?N  
:pUtSs7p}  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: Yw9GN2AG  
ry!!9Z>9n  
W4N{S.#!  
=#\:}@J5I  
String s = new String ("Text here"); If.r5z9  
Q20 %"&Xp]  
he4(hX^  
Y0>y8U V  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: Z}QB.$&  
% `3jL7|  
iB{V^ksU  
fIF8%J ^3  
String temp = "Text here"; 7 3m1  
String s = new String (temp); f<H2-(m  
yjAL\U7`T  
HV.t6@\};  
O84i;S+-p  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: #F#%`Rv1  
g 'gdgfvn  
#S(Hd?34,  
v1[29t<I!  
String s = "Text here"; =fbWz  
:r[`.`  
OY d !v`<  
 `]X>V,  
  二、常见错误2#: 没有克隆(clone)返回的对象 +0~YP*I`/  
]! dTG  
PdCEUh\>y  
9my^ Y9B  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: q7!{?\T%  
9UkBwS`  
E3i4=!Y  
6-I'>\U~  
import java.awt.Dimension; !?XC1xe~R  
/***Example class.The x and y values should never*be negative.*/  eIlva?  
public class Example{ <N)oS-m>  
  private Dimension d = new Dimension (0, 0); >bxS3FCX  
  public Example (){ } `g,..Ns-r  
Ngwb Q7)  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ WM{=CD  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ xmX 4qtAL  
   if (height < 0 || width < 0) /B3iC#?  
    throw new IllegalArgumentException(); G"6 !{4g  
    d.height = height; O}P`P'Y|'  
     d.width = width; OPi0~s  
  } Rv=YFo[B  
74u&%Rj  
  public synchronized Dimension getValues(){ <[phnU^ 8  
   // Ooops! Breaks encapsulation yuVs YV@"  
   return d;  <Uur^uB  
  } y(&Ac[foS}  
} 6mE\OS-I  
y2v^-q3  
ZoeD:xnh[  
TV:9bn?r)  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: Mhu*[a=;x  
XuTD\g3)  
!W\+#ez  
2T1q?L?]  
Example ex = new Example(); (mOtU8e  
Dimension d = ex.getValues(); ~?dI*BZ)]  
d.height = -5; v^iAD2X/F  
d.width = -10; XG?8s &  
Fs{*XKv&lH  
omFz@  
)1z@  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 pw#-_  
@L`jk+Y0vF  
K'xV;r7Nt  
S @Y39  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 9$Y=orpWxr  
fOHxtHM  
~>G^=0LT  
pdMc}=K  
  更好的方式是让getValues()返回拷贝: @d_M@\r=j  
vIvIfE  
Y@v>FlqI{  
YQ} o?Q$z  
public synchronized Dimension getValues(){ . me;.,$#  
return new Dimension (d.x, d.y); t7pFW^&  
} C^){.UGmJ  
/}$+uBgJm  
jCY %|  
x38 QD;MT  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 gIfh3D=yX  
uO**E-`  
DH=hH&[e(d  
FwK] $4*  
  三、常见错误3#:不必要的克隆 [ )F<V!  
,+VGSd  
7^Uv7< pw  
SJLis"8  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: > !JS:5|  
3%6? g*  
2eogY#  
[Pp'Ye~K@c  
/*** Example class.The value should never * be negative.*/ k+ /6$pI  
public class Example{ K}y f>'O  
  private Integer i = new Integer (0); xo)P?-  
  public Example (){ } [UR-I0 s!/  
6Zo}(^Ovz  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ /1 dT+>  
  public synchronized void setValues (int x) throws IllegalArgumentException{ pCDmXB  
   if (x < 0) W)/#0*7  
    throw new IllegalArgumentException(); 5G#n"}T  
    i = new Integer (x); }vuARZ>  
  } K"6vXv4QO  
iscz}E,Y  
  public synchronized Integer getValue(){ `V1]k_h  
   // We can’t clone Integers so we makea copy this way. #mT"gs  
   return new Integer (i.intValue()); R_KH"`q  
  } $qiya[&G4  
} 9sP0D  
VTM/hJmwJ  
c L]1f  
'"^'MXa  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 zA 3_Lx!  
kM 6 Qp  
NbobliC=  
|)&%A%m  
  方法getValue()应该被写为: GyIV Hby  
#c J@uqR  
(Z*!#}z`  
.`lCWeHN  
public synchronized Integer getValue(){ 6863xOv{T  
// ’i’ is immutable, so it is safe to return it instead of a copy. 1oS/`)  
return i; h8P)%p  
} M}a6Vu9  
3]>|  i  
0sqFF[i  
>z03{=sAN  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: ]]mJ']l  
qM`}{ /i  
x:;kSh  
b 6p|q_e  
  ?Boolean 0[`^\Mv4y  
   ?Byte Y73C5.dNcE  
   ?Character uH]OEz\H'  
   ?Class _w{Qtj~s|  
   ?Double !VJoM,b8  
   ?Float pRqx`5 }  
   ?Integer ixFi{_  
   ?Long .8R@2c`}Cs  
   ?Short m*pJBZxd  
   ?String w(/S?d  
   ?大部分的Exception的子类 6<]lW  
2iOV/=+  
YVU7wW,1  
3Ul*QN{6  
 四、常见错误4# :自编代码来拷贝数组 S!UaH>Rh  
3<!7>]A  
n]9$:aLZ  
Ey2^?  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: )UR7i8]!0  
QY/w  
E.TAbD&5(  
,2q-D&)\Z  
public class Example{  &HW9Jn  
  private int[] copy; O?2DQY?jT  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ tc! #wd+u  
  public void saveCopy (int[] data){ uYN`:b8  
   copy = new int[data.length]; WLT"ji0w2  
   for (int i = 0; i < copy.length; ++i) *VcJ= b 2Y  
    copy = data; *p U x8yB  
  } ~a:  
} vQCy\Gi   
Pal=F0-Q\  
&pRREu:[4L  
%Zi} MPx  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: $I=~S[p  
nKY6[|!#  
]/Pn EU[  
fex@,I&  
void saveCopy (int[] data){ f8~_E  
  try{ Tbq;h ?D  
   copy = (int[])data.clone(); 3u=g6W2 F  
  }catch (CloneNotSupportedException e){ WcAkCH!L  
   // Can’t get here. *pq\MiD/  
  } !a`&O-ye  
} a9gLg &  
CrLrw T  
3S{ />1Y  
";F'~}bDA  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: i@yC-))bY  
;+%rw2Z,B  
;TYBx24vD'  
t0S 1QC+  
static int[] cloneArray (int[] data){ Cy e.gsCT  
  try{ z_HdISy0  
   return(int[])data.clone(); 3w=J'(RU  
  }catch(CloneNotSupportedException e){ Vk suu@cch  
   // Can’t get here. 5+vaE 2v  
  } L,\Iasv  
} aUp g u"  
KoT\pY^7\  
g#bRT*,L  
^W ^OfY  
  这样的话,我们的saveCopy看起来就更简洁了: @dK Tx#gZ  
7I}uZ/N  
'DR!9De  
^[[P*NX3  
void saveCopy (int[] data){ ax`o>_)  
  copy = cloneArray ( data); wMn i  
} Tk}]Gev  
4 KiY6)  
(=0.inZ  
h{Y",7] !  
  五、常见错误5#:拷贝错误的数据 N7"W{"3D  
h`q1  
s;e\ pt  
3`g^  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: b}`T Ln  
[JiH\+XLPs  
f|5co>Hk  
7.Op<  
import java.awt.Dimension; <E~'.p,  
/*** Example class. The height and width values should never * be X'srL j.  
negative. */ dV_G1'  
public class Example{ ]^E?;1$f?  
  static final public int TOTAL_VALUES = 10; la!~\wpa  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; :TbgFQ86~  
  public Example (){ } }vuO$j  
CJY$G}rk  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ FrS]|=LJhX  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ Ui~>SN>s  
   if (height < 0 || width < 0) @"A4$`Xi3  
    throw new IllegalArgumentException(); oR'm2d^  
    if (d[index] == null) b6bHTH0  
     d[index] = new Dimension(); (QEG4&9  
     d[index].height = height; +7Gwg  
     d[index].width = width; @ Y+oiB~Y  
  } -w2/w@&  
  public synchronized Dimension[] getValues() J1k>07}|  
   throws CloneNotSupportedException{ K- v#.e4  
    return (Dimension[])d.clone(); D*jM1w_`  
  } t.<i:#rj>l  
} S jqpec8  
9[4xFE?|  
Wr 4,YQM  
XFl 6M~ c  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: >MZ/|`[M  
c 9Mz]1@f  
7Q 3k 7  
Txu/{ M,  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ BGSw~6  
  Dimension[] copy = (Dimension[])d.clone(); BPrt'Nc  
  for (int i = 0; i < copy.length; ++i){ { 6il`>=C  
   // NOTE: Dimension isn’t cloneable. *4'"2"  
   if (d != null) {7[Ox<Ho  
    copy = new Dimension (d.height, d.width); Jy)/%p~  
  } O.? JmE  
  return copy; rI\FI0zIp_  
} V~GDPJ+  
/~1+i'7V.,  
MgZ/(X E  
dysS9a,  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: %9"H  
[Xkx_B  
_a, s )  
\bXa&Lq  
public void store (int[] data) throws CloneNotSupportedException{ =;L|gtH"  
  this.data = (int[])data.clone(); vx =&QavL  
  // OK #!=tDc &  
} VbYdZCC  
)%TmAaj9d  
}f ?y* H  
mH(:?_KrS-  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: zLQx%Yg!  
}MySaL>  
w0. u\  
+{]j]OP  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ g)-te+?6  
  this.data = (int[][])data.clone(); // Not OK! 5P bW[  
} PCA4k.,T  
public void rightStore (int[][] data){ *~`(RV  
  // OK! h[ ZN+M  
  this.data = (int[][])data.clone(); i8p6Xht  
  for (int i = 0; i < data.length; ++i){ jXJyc'm7  
   if (data != null) 6BlXLQ,8q  
    this.data = (int[])data.clone(); JF]JOI6.e  
  } sO Y:e/_F  
} A/(a`"mK|'  
_c07}aQ ],  
(FV >m  
(7Qo  
hH.G#-JO  
   六、常见错误6#:检查new 操作的结果是否为null ~*7]r`6\@  
GgU/ !@  
g(g& TO  
[g,}gyeS(  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: \V:^h [ad  
z?zL97H  
>_} I.\ X  
}H2 R3icE  
Integer i = new Integer (400); qs6aB0ln  
if (i == null) okXl8&mi  
throw new NullPointerException(); 9WHddDA  
buC{ r,  
7)m9"InDI  
b>k y  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 M|-)GvR$J  
ICCc./l|  
M5B# TAybC  
MD]>g>  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 a*;b^Ze`v  
?2a$*(  
 七、常见错误7#:用== 替代.equals /reX{Y  
u2I Cl  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: BUFv|z+H  
=a!=2VN9y  
& kIFcd@  
:&Nbw  
int x = 4; p_ =z#  
int y = 5; G3]4A&h9v~  
if (x == y) E7hhew  
  System.out.println ("Hi"); rNM;ZPF#  
// This ’if’ test won’t compile. ?%86/N>  
if (x.equals (y)) w!CNRtM:~  
  System.out.println ("Hi"); :N@^?q{b  
z#N@ 0R  
3T 9j@N77  
^8tEach  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 C~[,z.FvO  
lr?;*f^3  
SuznN L=/$  
Cw%{G'O   
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 fM}#ON>Z  
+p^u^a  
v=k$A  
$M#>9QHhc  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 b -y  
!wNO8;(  
5xde;  
l0] EX>"E  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 4 :=]<sc,  
DlT{`  
2:R+tn(F  
*I'yH8Fcn  
  八、常见错误8#: 混淆原子操作和非原子操作 |%wX*zaf  
%\DX#.  
GfG|&VNlz  
'S~5"6r  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: ~ 1pr~  
S'14hk<  
Qd6FH2Pl  
*VeRVaBl  
public class Example{ 5;S.H#YOpO  
  private int value; // More code here... bcR_E5x$  
  public void set (int x){ F?cK- .  
   // NOTE: No synchronized keyword iLz@5Zj8  
   this.value = x; 23?rEhKe  
  } :]c3|J  
} h~26WLf.  
:EH=_"  
/bEAK-  
"j-CZ\]U|  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: r/sNrB1U"y  
U&xUfBDt  
H-%v3d>3  
_|]x2xb)  
public void increment (){ &{RDM~  
  // This is effectively two or three instructions: G j1_!.T  
  // 1) Read current setting of ’value’. ca}2TT&t  
  // 2) Increment that setting. -+5>|N#  
  // 3) Write the new setting back. Tr|JYLwF  
  ++this.value; FqifriLN  
} i?gSC<a  
KgG4*<  
8_tQa^.n\  
':}\4j&{E  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: 2Hdu:"j  
]d`VT)~vje  
fatf*}eln  
>MK98(F  
public synchronized void increment (){ {U1m.30n  
  ++this.value; *J{+1Ev~$p  
} l]cFqL p  
to\N i~a&  
CJ%I51F`X  
 9a kH  
  九、常见错误9#:在catch 块中作清除工作 {^'HL   
KF/-wZ"1s  
'!$%> ||S  
-F92-jBM4  
  一段在catch块中作清除工作的代码如下所示: 66 Tpi![  
7 ?t6UPf  
^J d r>@  
v@Ox:wl>  
OutputStream os = null; V$~9]*Wn  
try{ 3~ \[7I/  
  os = new OutputStream (); d\Zng!Z'  
  // Do something with os here. vI]N^j2%  
  os.close(); _~pbqa,  
}catch (Exception e){ (ik\|y% A  
  if (os != null) >j`qh:^  
  os.close(); s <Fl p  
} Kg$ Mx  
`W-Fssu  
N<-Gk6`C/  
T)}) pt!V  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: `lPfb[b  
ipILG4  
5-G@L?~Vw  
D6^6}1WI  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 H|D.6^  
+"6`q;p3)  
l(q ,<[O  
ty`DJO=Omj  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 CP{cAzHO  
@I*{f  
|CzSU1ma  
]_f<kW\1*  
  3.close()可能会抛出异常。 wf $s*|z  
:aQt;C6Z>  
m6djeOl  
*4Izy14e  
  上面代码的一个更优版本为: C $JmzrE  
"nWw;-V}}  
ERt{H3eCcJ  
#,.Hr#3nI  
OutputStream os = null; N?>vd*  
try{ `@ FYkH  
  os = new OutputStream (); h} EPnC}  
  // Do something with os here. rbCAnwA2  
}finally{ 7yba04D)  
  if (os != null) Lxk[;j+  
   os.close(); rD>f|kA?L  
} B]$GSEB  
<|\Lm20 G]  
+]50DxflA  
Yuc> fFA  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 c=+!>Z&i$G  
S@Hf &hJ  
|W\(kb+  
`#gie$B{  
  十、常见错误10#: 增加不必要的catch 块 <o= 8 FO  
veRm2 LSP  
h-D }'R  
; Hd7*`$  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 1r7y]FyH$  
[sb[Z:  
M xG W(p  
#u + v_  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 _,d~}_$`i  
@<Yy{ ~L|  
,{q;;b9  
(b6NX~G-:  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: +KEWP\r  
\_U$"/$4VH  
- R6)ROGl  
z"4~P3>{g  
try{ BX^tR1  
  // Nifty code here ss e.*75U  
}catch(Exception e){ $a %MOKr  
  throw e; M|[oaanY'  
}finally{ t.'!`5G  
  // Cleanup code here ))i}7 chc  
} BRYHX.}h\A  
TBrPf-Xr  
Fr$5RAyg  
2wgg7[tGi  
  不必要的catch块被删除后,上面的代码就缩短为: pU7lnS[  
1>&]R=  
O,A{3DAe0  
~3S~\0&|  
try{ -B\HI*u  
  // Nifty code here eNu7~3k}  
}finally{ Jdp3nzM^^@  
  // Cleanup code here Z*2Vpnqh\  
} qcGK2Qx  
>6pf$0  
[ps*uva  
zZC9\V}R  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 63~ E#Dt4  
"v4B5:bmqW  
7( 2{'r  
uc"P3,M  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 1~QPG\cdIX  
8Dm%@*B^b  
[=]4-q6UN  
y Ej^=pw  
  小结 .%C|+#&d  
)Iq<+IJ  
w(TJ*::T  
x7x\Y(@  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 Q'mM3pq4r  
om-omo&,X=  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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