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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 >e5zrgV  
nR5bs;gk"  
UPCQs",  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 D#sf i,O  
&B-[oqC?  
[h}K$q  
s*,cF6  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 3 %(Y$8U  
qc!xW ,I  
l%"`{   
&R]pw`mTH  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 Y4~vC[$ x'  
G_oX5:J*  
I"!'AI-  
`ypL]$cW  
  一、常见错误1# :多次拷贝字符串 ;R1B9-,  
2tr :xi@  
SR*Gqx  
qMgfMhQ7DU  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 :k!j"@r  
#MTj)P,  
]UUI~sFE  
<*@~n- R$  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: n!.2aq  
NXgRNca  
ko[TDh$T5  
' Js?N  
String s = new String ("Text here"); A o3HX  
L~MpY{!3  
:::>ro*R  
R`He^  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: 9y+[o  
ZO7bSxAN-  
('WY5Yps  
%_tk7x  
String temp = "Text here"; ]V<"(?,K  
String s = new String (temp); jfSg){  
e{t=>vry  
8IWw jyRr  
* -z4<LAa  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: =x4a~=HX  
A7'bNd6f9  
b?k4InXh  
_<u;4RO(s  
String s = "Text here"; :`zV [A:D  
$xcv>  
('BLU.7IX  
7)PJ:4IqS  
  二、常见错误2#: 没有克隆(clone)返回的对象 [a NhP;<  
?>)yKa#U  
6PJ'lA;*b  
?waebuj>  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: 6 6dTs,C  
(fd[P|G_]  
6H6Law!)  
w -dI<s  
import java.awt.Dimension; ~>w:;M=sV8  
/***Example class.The x and y values should never*be negative.*/ ".M:`BoW4  
public class Example{ j]4,6` b\  
  private Dimension d = new Dimension (0, 0); 'RQiLUF  
  public Example (){ } )]}$   
+{0=<2(EC  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ ~,G]glu8  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ 9@etg4#]  
   if (height < 0 || width < 0) h/9Sg*k  
    throw new IllegalArgumentException(); |YXG(;-BS  
    d.height = height; (k#t }B[  
     d.width = width; 5C65v:Q`N  
  } UhBz<>i;!  
6>lW5U^yA\  
  public synchronized Dimension getValues(){ :W%4*-FP  
   // Ooops! Breaks encapsulation :1NYpsd.i  
   return d; @aUZ#,(<  
  } X$PS(_M  
} th9 0O|;  
a^x  0 l  
yW:AVqE)t  
g!\QIv1D  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: ZgK@Fl*k  
XC2Q*Z  
cY^Y!.,  
b2W;|  
Example ex = new Example(); Qv74?B@  
Dimension d = ex.getValues(); 4(;20(q]  
d.height = -5; ]svw CPu C  
d.width = -10; + f6}p  
.$OjUlzr-H  
.GtINhz*  
 rA2qV  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 |B,dEx/uU  
%va[jJ  
Zq9>VqGe  
$*wu~  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 y.%i  
]KzJ u`O%G  
DU(X,hDBF  
[10$a(g\x  
  更好的方式是让getValues()返回拷贝: PaA6Z":  
W0+u)gDDz  
9B& }7kk  
-<_+-t  
public synchronized Dimension getValues(){ La@\q[U{@  
return new Dimension (d.x, d.y); (1OW6xtfG  
} \gjl^# ;  
uTxX`vH@!  
?#x'_2  
hLCsQYNDU  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 3@%BA(M  
"Fo  
Al)lWD}j2g  
^"PfDTyA  
  三、常见错误3#:不必要的克隆 ik8|9m4/  
^V_ku@DY  
f;&` 9s| 1  
?]759,Q3L  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: wA{*W>i  
?=TL2"L  
I=Gr^\x=  
D:+)uX}MOf  
/*** Example class.The value should never * be negative.*/ gF$1wV]e  
public class Example{ ]:[)KZ~  
  private Integer i = new Integer (0); SjFF=ib  
  public Example (){ } h2im sjf  
6tDg3`w>  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ 9;}L{yve  
  public synchronized void setValues (int x) throws IllegalArgumentException{ `M towXj  
   if (x < 0) g/fp45s  
    throw new IllegalArgumentException(); 9[6G8;<D&  
    i = new Integer (x); #JHy[!4  
  } 7*R{u*/e  
k8!hvJ)?  
  public synchronized Integer getValue(){ TsoCW]h  
   // We can’t clone Integers so we makea copy this way. <L[T'ZE+  
   return new Integer (i.intValue()); |_xZ/DT  
  } Q"x`+?!  
} -6.i\ B  
Nv}U/$$S  
4f_ZY5=  
t ?pIE cl  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 JNU"5sB  
LC~CPV'F  
n23%[#,r  
bW,BhUb,|  
  方法getValue()应该被写为: +H2m<  
4i ~eTb  
s8`}x_k=  
`8$:F4%P  
public synchronized Integer getValue(){ A52LH,  
// ’i’ is immutable, so it is safe to return it instead of a copy. }.fZy&_  
return i; dXf]G6  
} D;1 6}D  
&etL&s v  
UBUB/N Y  
(r#5O9|S  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: wsdB; 6%$  
LLXg  
DjevX7Q  
+R{A'Yl[(  
  ?Boolean : 9djMsd  
   ?Byte Pl>nd)i`  
   ?Character PU]7c2.y  
   ?Class qY*%p  
   ?Double 8M".o n  
   ?Float >uP{9kDm  
   ?Integer JN;TGtB^p  
   ?Long S?,KgMVM  
   ?Short L~?,6  
   ?String nsr _\F\  
   ?大部分的Exception的子类 ;:8jxkx6%  
'AAF/9  
 HzgQI  
rS,* s'G  
 四、常见错误4# :自编代码来拷贝数组 4X(1   
f//j{P[  
flm,r<*}  
nkr,  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: C[J`x>-K  
^G7n#  
ino7!T`  
y<'2BTf  
public class Example{ ^,ZvKA"}+/  
  private int[] copy; @'NaA SB  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ .LGkr@P  
  public void saveCopy (int[] data){ Hm8EYPr J  
   copy = new int[data.length]; RJ`/qXL  
   for (int i = 0; i < copy.length; ++i) ^}Dv$\;6  
    copy = data; tswG"1R  
  } p[k9C$@e}  
}  HPd+Bd  
>FE QtD~F  
T^=Ee?e  
kHo;9j-U  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: _6QLnr&@j  
RL]lt0O{  
=Y=^]ayO/  
A]~iuUHm  
void saveCopy (int[] data){ e) /u>I  
  try{ B#Oc8`1Y  
   copy = (int[])data.clone(); +=29y@c  
  }catch (CloneNotSupportedException e){ R(7X}*@X  
   // Can’t get here. lG[ )8!:+  
  } Um0<I)  
} [y&h_w.  
+_|cZlQ&  
][>M<J  
E6wST@ r  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: ~1wdAq`'a  
e&a[k  
5)SZd)  
U~zN*2-  
static int[] cloneArray (int[] data){ o"+ &^  
  try{ GWkJ/EX  
   return(int[])data.clone(); scPq\Qd?O  
  }catch(CloneNotSupportedException e){ EuimZW\V  
   // Can’t get here. <J_,9&\J  
  } kvO`]>#;$?  
} Wp9 2sm+  
@*`UOgP7  
Zn*CJNB  
?PxYS%D_L  
  这样的话,我们的saveCopy看起来就更简洁了: m LxwJ  
WUOPYYW<o  
%RD7=Z-z  
t%YX-@  
void saveCopy (int[] data){ pfn#~gC_=  
  copy = cloneArray ( data); Qj~W-^/ -  
} (9[C0eS  
G>{:D'#  
p$!+2=)gY  
s"Pk-Dv  
  五、常见错误5#:拷贝错误的数据 i\R\bv[9  
$q@RHcj  
) eGu4iEPM  
)b2E/G@X&  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: yW=hnV{  
`R=_t]ie  
Vi -!E  
AYQh=$)(  
import java.awt.Dimension; CH_Dat >  
/*** Example class. The height and width values should never * be h*X%:UbW  
negative. */ . eag84_  
public class Example{ =`.5b:e  
  static final public int TOTAL_VALUES = 10; `q{'_\gVt(  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; >D^7v(&  
  public Example (){ } _(s|Q  
i^V4N4ux]  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ k-PRV8WO  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ iO=uXN1g  
   if (height < 0 || width < 0) :x5o3xE  
    throw new IllegalArgumentException(); bFdg '_  
    if (d[index] == null) wNZS6JF.d  
     d[index] = new Dimension(); WF.$gBH"  
     d[index].height = height; B46H@]d#7K  
     d[index].width = width; ghd[G}  
  } q>lkLHS  
  public synchronized Dimension[] getValues() C]cT*B^  
   throws CloneNotSupportedException{ a ZCZ/  
    return (Dimension[])d.clone(); 5N</Z6f'o  
  } n)7$xYuH  
} +O:pZz  
+q?0A^C>  
X!HSS/'  
v@1Jh ns  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: ^|12~d_.T  
Y%cA2V\#m  
7Z:l;%]K  
P*=3$-`  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ Jt^JE{m9%  
  Dimension[] copy = (Dimension[])d.clone(); .xQ'^P_q  
  for (int i = 0; i < copy.length; ++i){ M@ZpgAfq  
   // NOTE: Dimension isn’t cloneable. <T~fh>a  
   if (d != null) RpXGgw  
    copy = new Dimension (d.height, d.width); &XTd[_VW!  
  } 8}b[Q/h!  
  return copy; ~=]@], {  
} k  5kX  
iYs?B0*JWK  
3\~fe/z'I  
3T^dgWXEG  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: >N"PLSY1  
MBrVh6z>  
F&j|Y>m  
p" W0$t.  
public void store (int[] data) throws CloneNotSupportedException{ z`{zqP:  
  this.data = (int[])data.clone(); l]=$<  
  // OK EF{'J8AQ  
} <g1hdF0  
7027@M?A?  
`5jB|r/  
~g|0uO}.  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: B{7/A[$%C  
5Jd {Ev  
hf5SpwxLiH  
}n8;A;axi  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ ~4gKA D  
  this.data = (int[][])data.clone(); // Not OK! zC;lfy{f=  
} e[o ;l  
public void rightStore (int[][] data){ ,+evP=(cX  
  // OK! p%_ :(  
  this.data = (int[][])data.clone(); F09AX'nj  
  for (int i = 0; i < data.length; ++i){ RLX^'g+P  
   if (data != null) Gf.o{  
    this.data = (int[])data.clone(); #u(,#(P'#  
  } AdW7 vn  
} X.5LB!I)  
p arG  
J~`%Nj5>  
RxG./GY  
@n'ss!h  
   六、常见错误6#:检查new 操作的结果是否为null YQsc(6  
Y|jesa {x  
`;GGuJb \  
dR{ V,H7N  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: 6MQ:C'8T&=  
QP0X8%+p  
CUcjJ|MZ  
mQuaO# I,  
Integer i = new Integer (400); ,1+_k ="Z  
if (i == null) }f*S 9V  
throw new NullPointerException(); bd;f@)X  
BVeNK=7m%  
kfECC&"  
>?FCv7qN  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 M&-/ &>n!  
a}hM}U!  
A2S9h,t  
~[6|VpGc:  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 TnAX;+u  
fdHxrH >*  
 七、常见错误7#:用== 替代.equals uS,$P34^oy  
T+!kRigN~P  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: i|'t!3I^m  
7?#32B Gr  
l]&)an  
C>ZeG Vq  
int x = 4; IBsn>*ja<  
int y = 5; mr.DP~O:9p  
if (x == y) Lcm!e  
  System.out.println ("Hi"); (DAJ(r~  
// This ’if’ test won’t compile. z6(Q 3@iO  
if (x.equals (y)) lNAHn<ht  
  System.out.println ("Hi"); s50ln&2  
G$<0_0GF  
w7u >|x!  
1A`";E&  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 m,O !M t  
G> >_G<x  
g7i6Yj1  
Df^S77&c!  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 N3) v,S-  
7 i/Cax  
Y?cw9uYB  
~yN,FpD  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 U)8]pUI+/P  
2O/_hv.  
|e >-v  
*Cw2h  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 ]9Hy "#Fz  
(=9&"UH  
<& iBR  
^ EOjq  
  八、常见错误8#: 混淆原子操作和非原子操作 ibyA~YUN/  
_!zY(9%  
AAcbY;  
RSC-+c6 1  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: oDa{HP\O]W  
6Y^o8R  
Q #gHD  
C+5nft6:  
public class Example{ 6* 7&X#gG  
  private int value; // More code here... @AOiZOH  
  public void set (int x){ E3!twR*Aw  
   // NOTE: No synchronized keyword xTT>3Fj  
   this.value = x; #ZA YP  
  } "T|\  
} c3##:"wr  
oWmla*nCKL  
Sls> OIc  
}JD(e}8$!  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: e=2D^ G#qE  
,"o \_{<z  
sXu]k#I^"  
@^O ww(I  
public void increment (){ AM>:At Y  
  // This is effectively two or three instructions: PJ{.jWwD  
  // 1) Read current setting of ’value’. Tx*m p+q  
  // 2) Increment that setting. (P?9Jct  
  // 3) Write the new setting back. z( wXs&z;  
  ++this.value; !r<7]nwV  
} |{ k B`  
scdT/|(U$  
cF6|IlhO  
#_d%hr~d  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: s>5 Z  
+-hmITJ v  
k;sUDmrO  
~J|0G6H  
public synchronized void increment (){ (k?OYz]c  
  ++this.value; LdOB[W  
} utr_fFu  
t`  Sh!e  
pfT7  
b7-a0zaN  
  九、常见错误9#:在catch 块中作清除工作 b{&@ Lm0Tn  
g=)@yZ3>v  
Z]tz<YSkG  
b|NEU-oy  
  一段在catch块中作清除工作的代码如下所示: yC[}gHv  
5GKz@as8  
(< h,R@:  
*b&|  
OutputStream os = null; f =Nm2(e  
try{ 7$'ja  
  os = new OutputStream (); bu]bfnYi9  
  // Do something with os here. 6vDgM fw  
  os.close(); N$ ?qAek  
}catch (Exception e){ mU!c;O  
  if (os != null) 99`xY$  
  os.close(); ;/v^@  
} w"~T5%p  
i[B%:q:&  
,D8 Tca\v  
j'Fni4;  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: ||hd(_W8  
Pu=YQ #F'  
uB\A8zC  
NunT1ved  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 |K]tJi4fz  
x`4">:IA  
139_\=5|U/  
C`fQ` RL\  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 },l3N K  
(UCWSA7oc  
J( 0c#}d  
dP82bk/e  
  3.close()可能会抛出异常。 #soWX_>  
d;`JDT  
>6c{CYuT  
!^y'G0  
  上面代码的一个更优版本为: SO|$X  
SJ<nAX  
I'JFt>]  
7a2 uNt,X  
OutputStream os = null; >y5~:L  
try{ Wa wOap  
  os = new OutputStream (); =m UtBD.;  
  // Do something with os here. d%iMjY`~[g  
}finally{ <%! EI@N  
  if (os != null) LBTf}T\  
   os.close(); gkDB8,C<j  
} uT_bA0jK  
{D$+~ lO  
Z<`QDBN"4  
opd^|xx0  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 y\S}U{*Z'  
vLq_l4l  
 eGjEO&$  
*~cq (PFQ  
  十、常见错误10#: 增加不必要的catch 块 q>t#5Z81  
#>}cuC@  
qeypa !  
H>% K}Fh  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 8 K'3iw>z  
cJMp`DQzc  
W2'u]1bs  
51xiX90D  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 #mFIZMTRd  
1GE[*$vuq  
^8OK.iC  
bZLY#g7L"  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: 0tK(:9S  
uWkW T.>$  
|cs]98FEf  
]De<'x}  
try{ G .NGS%v  
  // Nifty code here {{qu:(_g  
}catch(Exception e){ `p#tx.o  
  throw e; K|]/BjB/  
}finally{ .kcyw>T`I  
  // Cleanup code here <- L}N '  
} W^k95%zBM  
g%TOYZr!X  
a]]eQ(xQ  
]lqLC  
  不必要的catch块被删除后,上面的代码就缩短为: S"H djEF7\  
Lcb5^e?'Q  
Q`kV| pjg  
TophV}@B`  
try{ 2{|Z?3FJ^  
  // Nifty code here VkFMr8@|  
}finally{ $+w:W85B  
  // Cleanup code here 0Xb,ne 7  
} s5h}MXIXw  
y*=sboX  
pmRm&VgE.  
%:t! u&:q  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 KT[ZOtu  
gu|=uW K  
ijP `fM8  
0CO@@`~4  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 DG?g~{Y~b  
\#4mPk_"  
D6-R>"}  
S4\a"WYg  
  小结 gFqF&t  
F Y<Q|Ov  
p]0`rf!|  
!0dQfj^_  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 rGQ2 ve  
|sdG<+  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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