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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 _<Dt z  
!]b@RUU  
`a4 $lyZ  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 %_f;G+fK\p  
?0vNEz[  
At9X]t  
=3'(A14C=  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 aG27%(@  
V2 `> ]/|  
JF9Hfs/jS  
=_-C%<4  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。  s>[{}7ca  
_l&ucA  
IN!02`H  
b9("DZW;  
  一、常见错误1# :多次拷贝字符串 %e=!nRc  
g(m_yXIx  
u-><}OVf~  
Q7a(P  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 Pes =aw  
Tov&68A~e  
! D1zXXq  
c> ~:dcy  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: ;z>p8N  
J$[Q?8 ka  
;Bs^iL  
5~}!@yzc  
String s = new String ("Text here"); -Ktwo_ V*  
}76.6=~  
p;->hn~D'5  
Y!n'" *J>  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: 5*j:K&R-.K  
7),*3c')  
CeW7Ym  
K51fC4'{  
String temp = "Text here"; s[V `e2O  
String s = new String (temp); UrK"u{G  
JDhwN<0R  
gWu"91Y0>  
[.3sE  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: ;=p;v .l  
^5x4q  
Q8Te'1Ln!  
:,YLx9i>  
String s = "Text here"; 'j#a%j@{  
 -$R5  
^[TOZXL`:  
vKkf2 7  
  二、常见错误2#: 没有克隆(clone)返回的对象 SALCuo"L  
J/7 u7_  
S7#0*2#[o  
e7|d=W  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: HA\A$>  
<PQRd  
Tw}z7U"  
AeQC:  
import java.awt.Dimension; 4 Q&mC"  
/***Example class.The x and y values should never*be negative.*/ hDb HSZ  
public class Example{ f#c BQ~  
  private Dimension d = new Dimension (0, 0); O 3}P07  
  public Example (){ } p[gAZ9  
5j1 IH,yW  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ d#Wn[h$"  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ :|fl?{E  
   if (height < 0 || width < 0) d-i&k(M  
    throw new IllegalArgumentException(); gyg|Tno  
    d.height = height; {5.?'vMp  
     d.width = width; SmvwhX  
  } G`WzJS*}v  
Qv=Bq{N  
  public synchronized Dimension getValues(){ ^EUQ449<p  
   // Ooops! Breaks encapsulation WMA*.$Zi  
   return d; BDnBBbBrz  
  } a6 * Y%?  
} h!~|6nj  
`F@f?*s:  
*IVD/9/  
E.Pje@d  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: GMt)}Hz  
81cv:|"  
Q!Msy<v  
ap{{(y&R  
Example ex = new Example(); ^$6bs64FSm  
Dimension d = ex.getValues(); W>VAbm  
d.height = -5; 9EjjkJ%)q  
d.width = -10;  !zF4 G,W  
= c/3^e  
&<#/&Pq/i  
|)*m[_1  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 dcM+ylB  
  -kV|  
JJ*0M(GG  
cRjL3  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 ~ rQ4n9G  
~4}*Dhsh  
'aLPTVM^  
-aTg>Q|g&  
  更好的方式是让getValues()返回拷贝: 8UjCX[v  
wV]sGHuF}  
|~Htj4K/  
(0O`A~M3  
public synchronized Dimension getValues(){ aEV|>K=6Y'  
return new Dimension (d.x, d.y); q\-xg*'  
} _D!M nTK  
=]2 b8  
I67k M{V  
U1OLI]P  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 N8u_=b{X  
#J'Z5)i|  
:qt82tbn  
rin >r0o  
  三、常见错误3#:不必要的克隆 @#G6z`,  
mkKRC;  
Q-H =wJ4R  
gs^UR6 D,  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: 9RbGa Y&  
;7B2~zL  
c"oQ/x  
P\Pc/[ Z7  
/*** Example class.The value should never * be negative.*/ a3DoLq"/  
public class Example{ 38zR\@'j]4  
  private Integer i = new Integer (0); 7%~VOB  
  public Example (){ } WVBE>TB  
)w3XN A_V  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ jn5=N[hd  
  public synchronized void setValues (int x) throws IllegalArgumentException{ /cn/[O9  
   if (x < 0) zEA{%)W  
    throw new IllegalArgumentException(); 4#'^\5  
    i = new Integer (x); )dzjz%B)  
  } s%0[DO3NV  
p~k`Z^ xY$  
  public synchronized Integer getValue(){ o} bj!h]N  
   // We can’t clone Integers so we makea copy this way. ^*4(JR   
   return new Integer (i.intValue()); yE!7`c.[u  
  } z}Mb4{d1  
} $bM#\2'  
yF [@W<  
%te'J G<  
{H"=PYR  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 cZ7F1H~  
AQ_#uxI'oa  
NjCLL`?f  
SFm.<^6  
  方法getValue()应该被写为: F#3$p$;B$  
lD+y, ";  
;d'O.i=  
E<fwl1<88  
public synchronized Integer getValue(){ )H=[NB6J8  
// ’i’ is immutable, so it is safe to return it instead of a copy. 3GuMiht5  
return i; O#89M%  
} SgQmYaa&  
]#Z$jq{,  
"}y3@ M^  
cJp1 <R  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: UmHJ/DI@  
tMs| UC  
c#4ZDjvm6  
]jT[dX|?  
  ?Boolean PrYWha=c-  
   ?Byte  p% YvP  
   ?Character )jQe K  
   ?Class D} <o<Dk  
   ?Double @ #O|  
   ?Float n4 6PQm%p  
   ?Integer }od7YL  
   ?Long 8G^B%h]  
   ?Short 1k70>RQ&69  
   ?String 5a/ A_..+I  
   ?大部分的Exception的子类 aN);P>  
ThiPT|5u  
VL,?91qwe  
GlkAJe]  
 四、常见错误4# :自编代码来拷贝数组 07WIa@Q  
:?of./Df|  
Ss#{K;  
otnY{r *  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: d=u%"36y  
LW8{a&  
wQ/@+$>  
fw~%^*  
public class Example{ #Ew eG^!#  
  private int[] copy; vkauX :M  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ \?]HqPibx  
  public void saveCopy (int[] data){ #z54/T  
   copy = new int[data.length]; 5'L}LT8p@  
   for (int i = 0; i < copy.length; ++i) ^V;lZtZ  
    copy = data; w"Y55EURB  
  } `0gK;D8t  
} n'(n4qH2#s  
J+*Y)k  
uN^qfJ'@ >  
s*U&[7P  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: gJfL$S'w  
c!FjHlAnP  
;;J98G|1  
K`Zb;R X  
void saveCopy (int[] data){ 4Tw1gas.  
  try{ ?d`+vHK]>  
   copy = (int[])data.clone(); c15^<6]g  
  }catch (CloneNotSupportedException e){ >" 8j{ s  
   // Can’t get here. ee?M o`  
  } yX Q;LQ;  
} %l P   
"TNUw&ih  
u:kY4T+Z  
G~L#v AY  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: 9&FV =}MO  
<^Nj~+G'  
%QZ!Tb  
l\y*wr`  
static int[] cloneArray (int[] data){ oYM3$.{E  
  try{ Xr;noV-X  
   return(int[])data.clone();  b- /x  
  }catch(CloneNotSupportedException e){ FPFYH?;$  
   // Can’t get here. r1?FH2Ns  
  } lR3^&d72?  
} 0'oT {iN  
RN3D:b+  
2^lT!X@  
+W4g:bB1  
  这样的话,我们的saveCopy看起来就更简洁了: U2?gODh'  
UAO#$o(  
nx]b\A  
^$ bhmJYT  
void saveCopy (int[] data){ HwK "qq-  
  copy = cloneArray ( data); mR@Xt#  
}  e) (|  
~jTn jx  
pa73`Ca]  
c'qM$KN9G  
  五、常见错误5#:拷贝错误的数据 /RmCMT  
I]$d,N!.  
H >{K]7D/y  
>u2#<k]1&  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: 6p{x2>2y[  
( %!R  
O+J;Hp;\_  
E e&$9 )t  
import java.awt.Dimension; C^tC} n1D(  
/*** Example class. The height and width values should never * be DyZ90]N  
negative. */ t1^96@m^  
public class Example{ i% lB U 1  
  static final public int TOTAL_VALUES = 10; \ChcJth@o<  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; Tq8r SZi  
  public Example (){ } Q.AM  
FCPRg^=<!~  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ E9hWn0 e  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ l$KcS&{w9  
   if (height < 0 || width < 0) \&90$>h  
    throw new IllegalArgumentException(); NBYE#Uih  
    if (d[index] == null) uSjMqfK  
     d[index] = new Dimension(); L]}|{< 3\  
     d[index].height = height; `yXy T^  
     d[index].width = width; <,C})H?  
  } q4N$.hpb  
  public synchronized Dimension[] getValues() )O Cr6UR  
   throws CloneNotSupportedException{ -;*Z!|e9  
    return (Dimension[])d.clone(); )Cl&"bX  
  } IWgC6)n@n  
} s q KkTG3  
8=9sIK2  
opC11c/  
wP9C\W;  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: 8hg(6 XUG  
uzsN#'7=  
!c7Od )]  
CHB{P\WF  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ enj2xye%Y  
  Dimension[] copy = (Dimension[])d.clone(); 0*/~9n-Vl  
  for (int i = 0; i < copy.length; ++i){ [PT_y3'%  
   // NOTE: Dimension isn’t cloneable. o_n.,=/cZ  
   if (d != null) E=d[pI,e  
    copy = new Dimension (d.height, d.width); C*!_. <b  
  } @C2<AmY9q*  
  return copy; M ~IiJ9{  
} y=Hl~ev`9  
;xc  
&.,OvVAo  
PhS"tOGtX  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: |7-tUHMo[  
S}/CzQ  
ES>3Cf  
')+EW" e  
public void store (int[] data) throws CloneNotSupportedException{ c#DTL/8"DO  
  this.data = (int[])data.clone(); ^9nM)[/C?  
  // OK h(sD]N  
} U#}.r<  
fomkwN  
r/e&}!  
Q7\Ax0  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: L!Ro`6|7;  
^:0?R/A  
mR^D55k  
/d4xHt5a  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ L8fr uwb  
  this.data = (int[][])data.clone(); // Not OK! 2gGJ:,RC$  
} uP$K{ )  
public void rightStore (int[][] data){ rW^&8E[  
  // OK! 5c#L6 dA)  
  this.data = (int[][])data.clone(); 8yo9$~u;  
  for (int i = 0; i < data.length; ++i){ =aCv Xa&,  
   if (data != null)  4Zq5  
    this.data = (int[])data.clone(); ICi- iX  
  } x>@UqUJV  
} j405G4BVW  
Q pX@;j  
DM&"oa50  
F8/@/B  
pkoHi'}}$  
   六、常见错误6#:检查new 操作的结果是否为null S92Dvw?  
TuU.yvkU  
(GV6%l#I  
#k6;~  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: - zkB`~u_  
U:>O6"  
!qR(Rn  
rT#QA=YB  
Integer i = new Integer (400); m0P5a%D  
if (i == null) |'.SOm9)*  
throw new NullPointerException(); uO6_lOT9n  
Z(Vrmz2.  
c"~TH.,d  
2{& " 3dq  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 8_lD*bEt   
~EN@$N^h  
l|K8+5L  
0jXIx2y  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 ^uj+d"a)  
Jx}5`{\  
 七、常见错误7#:用== 替代.equals 4k#6)e  
k\lj<v<vD  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: Y /+ D4^ L  
TwH(47|?Nt  
Q>rQ/V  
$5A XE;~{  
int x = 4; _:g V7>S?  
int y = 5; UYPBKf]A9  
if (x == y) (3-G<E  
  System.out.println ("Hi"); =nsY[ s<  
// This ’if’ test won’t compile. w+}dm^X  
if (x.equals (y)) )#Id 2b~  
  System.out.println ("Hi"); h<j04fj  
z}&<D YD  
<Z&gAqj 2  
LD NpEX~  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 =19]a  
B|Omz:c  
9\xw}ph  
&M2x`  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 -L[K1;Xv"  
KGJB.<Be  
D|S)/o6  
o ).pF">jh  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 JdAjKN  
we@bq,\w  
jzV#%O{`  
#Y:/^Q$_qS  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 LU]~d< i99  
ZTun{Dw{  
/x-t -}  
qN)cB?+  
  八、常见错误8#: 混淆原子操作和非原子操作 i^> RjR  
)dV.A IQ+  
Mqpo S  
4>|5B:  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: ~\^8 ^  
e p^0Cd/  
nMnc&8r  
3o?Lz7L  
public class Example{ 51&T`i  
  private int value; // More code here... LY>JE6zTt  
  public void set (int x){ zEGwQp<  
   // NOTE: No synchronized keyword nGf@zJDb  
   this.value = x; Yv;aQF"a  
  }  ^ "f  
} N.<hZ\].=  
&yN<@.  
@Q^;qMy  
w5;EnI  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: ooAZ,l=8  
M{H&5 9v  
gu/Yc`S[  
k (R4-"@  
public void increment (){ `3OGCy  
  // This is effectively two or three instructions: BE2{qO{  
  // 1) Read current setting of ’value’. 3k/Mig T  
  // 2) Increment that setting. HfF$>Z'kM  
  // 3) Write the new setting back. 3)ip@29F  
  ++this.value; D%A-& =  
} K?>&Mr  
NWvxbv  
r:IU +3  
N7_Co;#(zK  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: _H,RcpyJ  
71%u|k8|  
Ef!F;De)A  
$CmTsnR1#y  
public synchronized void increment (){ wnX6XyUH  
  ++this.value; A3n"zxU  
} r$7rYxFR  
_=] FJhO  
 #59zv=  
tu'MYY  
  九、常见错误9#:在catch 块中作清除工作 wB^a1=C  
E;sltl  
8n)Q^z+ K  
D6t]E)FH  
  一段在catch块中作清除工作的代码如下所示: 0#JBz\  
-O5m@rwt<  
@o ED tN  
0TpA3K  
OutputStream os = null; $W]bw#NH  
try{ z -D pLV  
  os = new OutputStream (); DkIF vsLK  
  // Do something with os here. xpM~* Gpm  
  os.close(); UU/|s>F  
}catch (Exception e){ J[l K  
  if (os != null) hs4r5[  
  os.close(); <Va>5R_d<  
} .kIf1-(<U  
%vXQ Sz  
5IK@<#wE  
V'.|IuN  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: ~'3% Qr  
YLGLr @:q  
l&B'.6XKs  
yH^*Fp8V  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 2e"}5b5  
GN0'-z6Uy  
[w f12P  
;!?K.,N:N  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 VsEAo  
bl_WN|SQ  
QaR.8/xV  
i&)C,  
  3.close()可能会抛出异常。 RCXSz  
dRm'$ G9  
B}+9U  
-FV'%X$i  
  上面代码的一个更优版本为: X:&p9_O@  
mcr#Ze  
 <z2mNq  
|wQZ~Ux:  
OutputStream os = null; y$o=\:  
try{ n5*7~K "C  
  os = new OutputStream (); I?Fa  
  // Do something with os here. =pyZ^/}P  
}finally{ y4We}/-<  
  if (os != null) _x 6E_i-(  
   os.close(); NqE7[wH  
} cu!bg+,zl  
myOX:K*  
FNCLGAiZ  
)+4}Ix/q  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 ,9wenr  
iCRw}[[  
zy6(S_j  
{JCz^0DV  
  十、常见错误10#: 增加不必要的catch 块 fhIj+/{_O  
%%cSvPcz  
MI'l4<>u  
Tv,.  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 ^@lg5d3F  
8pMZ~W;  
" IkF/  
<`j[;>O  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 Po11EZa$a  
\*!%YTZ~  
^}/ E~Sg7\  
o/ g+Z  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: 6Y_O^f  
k.xv+^b9Q  
=>}.W:=  
GHC?Tp   
try{ uj9tr`Zh  
  // Nifty code here q\'P1~  
}catch(Exception e){ Y:!/4GF  
  throw e; ?V)C9@bp  
}finally{ g;#KBxE  
  // Cleanup code here u~- fK'/!|  
} ?I2k6%a  
4 ^~zN"6]  
i=X*  
"ex~ LB  
  不必要的catch块被删除后,上面的代码就缩短为: JdUz!=I  
_E1]cbIo  
akvwApn5  
9p\Hx#^  
try{ ' MS!ss=r  
  // Nifty code here tk)>CK11  
}finally{ &.  =}g]  
  // Cleanup code here j`|^s}8t  
} T7lj39pJq  
0qL V(L  
h%1~v$W`  
p17|ld`  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 y@kcXlY  
)`s;~_ZZ  
),;D;LI{S  
(Q@+v<   
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 (o*e<y,}W  
qJ X+[PJ  
| z#m  
yxG:\y b  
  小结 *dG}R#9Nv  
T@Ss&eGT2  
zJfK4o  
o%Uu.P  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 x5fgF;  
dXhCyr%"6  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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