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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 6\Vu#r  
)6E*Qz  
9'q/&uH  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 iwJ-<v_:h  
e H  
T(UYlLe  
mzxvfXSF  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 `_U0>Bfg;  
m]d6@"Z.  
XOg(k(&T  
!otq X-  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 W4*BR_H&*  
~e<'t4  
0t/y~TrBY  
K4/P(*r`  
  一、常见错误1# :多次拷贝字符串 DG*o w^  
7(8  
%C6zXiO"  
J+ZdZa}Ob  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 $lAb6e$n  
Q(5:~**I  
xO<-<sRA  
`$Flgp0P  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: pZ~> l=-  
V 1nZ M  
qV8\/7'A0a  
7]xz8t  
String s = new String ("Text here"); qm8n7Z/  
C.)&FW2F_  
m2uML*&O5K  
&9dr+o-(~  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: 5rA!VES T  
wu!_BCIy  
*<1x:PR  
p:<gFZb  
String temp = "Text here"; JJ9e{~0 I  
String s = new String (temp); "8iiRzt#  
3b)T}g  
VgsCwJ9w  
2<o[@w  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: *!]Epb  
199hQxib:  
5;MK1l  
[{p?BTs  
String s = "Text here"; 0tm_}L$g=b  
4a.e ,gitf  
e4YfT r  
mGpkM?Y"  
  二、常见错误2#: 没有克隆(clone)返回的对象 0SCW2/o8  
h}`&]2|]  
Pv %vx U  
q8 xc70: R  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: yCkW2p]s,K  
%{~mk[d3  
aU.0dsq  
zNr_W[  
import java.awt.Dimension; 76_8e{zbr  
/***Example class.The x and y values should never*be negative.*/ }RN=9J  
public class Example{ MZMS ?}.2  
  private Dimension d = new Dimension (0, 0); N 1f~K.e\  
  public Example (){ } .H (}[eG_  
oF b mz*  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ 7{+Io  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ `b#nC[b6|v  
   if (height < 0 || width < 0) X:SzkkVl7  
    throw new IllegalArgumentException(); $Y 4ch ko  
    d.height = height; gc2|V6(  
     d.width = width; n?e@):  
  } o eJC  
Z!RRe]"y  
  public synchronized Dimension getValues(){ Kjn&  
   // Ooops! Breaks encapsulation \B>[je-d  
   return d; ? W2I1HEy  
  } FM"GK '  
} AY/-j$5+?  
Fe& n,  
9u7n/o&8v6  
8A8xY446)  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: V:G}=~+=  
JM+sHHs  
xH`j7qK.  
iZ.&q 6  
Example ex = new Example(); kf^-m/  
Dimension d = ex.getValues(); *@G(3 n  
d.height = -5; 0'%+X|  
d.width = -10; 4-d99|mv  
zN)|g  
g=oeS%>E  
76IALJ00V  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 yNqm]H3<MP  
!|Xl 8lV`  
:L [YmZ  
B=q)}aWc  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 Jp.3KA>  
."F'5eTT~  
>d27[%  
-@ UN]K  
  更好的方式是让getValues()返回拷贝: K.#,O+-Kg`  
`hK>bHj  
=N*%f%  
9&XV}I,~?|  
public synchronized Dimension getValues(){ h$aew63  
return new Dimension (d.x, d.y); K.V!@bPlw9  
} VeD+U~ d  
@wEKCn|}o  
_ r^90  
+YQ~t,/  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 FU]8.)`G  
3lLW'g&=  
XUQW;H  
y?Hj %,  
  三、常见错误3#:不必要的克隆 w8ZHk?:  
Y>78h2AU  
wcdW72   
OXIu>jF  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: yd0=h7s  
>ggk>s|  
;9p#xW6  
=q"w2b&  
/*** Example class.The value should never * be negative.*/ ]uStn   
public class Example{ U!a!|s>  
  private Integer i = new Integer (0); As6)_8w  
  public Example (){ } Yhc6P%{Z^  
"UhK]i*@l  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ Z0()pT  
  public synchronized void setValues (int x) throws IllegalArgumentException{ ;"d,~nLn  
   if (x < 0) `Ct'/h{  
    throw new IllegalArgumentException(); %?]{U($?  
    i = new Integer (x); "o^bN 9=  
  } nl)_`8=  
C;d|\[7Z  
  public synchronized Integer getValue(){ NRHr6!f>  
   // We can’t clone Integers so we makea copy this way. r&%gjqt  
   return new Integer (i.intValue()); BGlGpl  
  } Gs_*/E7,  
} 8m/FKO (r  
hapB! ~M?  
HsjELbH  
e'k;A{Oh  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 ueWR/  
%jbJ6c  
)){PBT}t]  
zqHpT^B?  
  方法getValue()应该被写为: Tsm)&$JI8  
pW*{Mx  
vi[#? ;pkF  
g{g`YvLu^  
public synchronized Integer getValue(){ :"OZc7 ~  
// ’i’ is immutable, so it is safe to return it instead of a copy. RsqRR`|X?  
return i; A6?qIy  
} Aj8l%'h[  
_" ?c9  
z9k*1:  
g:3d<CS  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: msA' 5>  
 D rF  
]+}ZfHp  
,h%D4EVx  
  ?Boolean '2Q.~6   
   ?Byte SWNU1x{,c\  
   ?Character 3o+KP[A  
   ?Class HZQDe&  
   ?Double kP!%|&w;  
   ?Float Tm%$J  
   ?Integer ;=5@h!@R  
   ?Long Y=P9:unG  
   ?Short Mv/IMO0rR  
   ?String @!z$Sp=  
   ?大部分的Exception的子类 8BYIxHHz  
YXczyZA`x  
cPA~eZbX  
J- t=1  
 四、常见错误4# :自编代码来拷贝数组 M(n<Iu4^_  
b34zhZ  
2x7(}+eD  
Ez06:]Jd  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: |_l<JQvf`E  
XAjd %Xv<  
B,~f "  
);Tx5Z}  
public class Example{ [n!$D(|"!V  
  private int[] copy; {c v;w  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ 6V'wQqJ  
  public void saveCopy (int[] data){ gIV3n#-{L  
   copy = new int[data.length]; x2 w8zT6M  
   for (int i = 0; i < copy.length; ++i) R'*<A3^  
    copy = data; ^-gfib|VGe  
  } _v1bTg"?  
} -rE eKt  
Zij"/gx\  
@rPI$ia1~  
AC 9{*K[  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: ggerh#  
7[ZkM+z!  
r/UYC"K3  
.yZK.[x4  
void saveCopy (int[] data){ l\K%  
  try{ Cr' ! "F  
   copy = (int[])data.clone(); UJ7'JBT=k  
  }catch (CloneNotSupportedException e){ jK3giT  
   // Can’t get here. `)rg|~#k  
  } |?\gEY-Se  
} qru2h #  
9k+N3vA  
v57N^DR{  
mZ`1JO9  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: P#AAOSlLV  
"V:   
Z 6 tE{/  
?RZq =5Um&  
static int[] cloneArray (int[] data){ 4st~3,lR$  
  try{ @)9REA(U  
   return(int[])data.clone(); Jb( DJ-&  
  }catch(CloneNotSupportedException e){ Ya~ "R#Uy  
   // Can’t get here. 99J+$A1  
  } I)[`ZVAXR  
} W\HLal  
W;^Rx.W  
"4 'kb  
G1kDM.L  
  这样的话,我们的saveCopy看起来就更简洁了: `-~`<#E[  
x}v1X`6b  
4uFIpS|rq  
3Z_t%J5QZ$  
void saveCopy (int[] data){ $8jaapNm@  
  copy = cloneArray ( data); V]r hr  
} 9 TqoLX  
+#0~:&!9  
^Y$QR]  
>NJjS8f5  
  五、常见错误5#:拷贝错误的数据 2K3MAd{  
EY So=  
]PeLcB  
ZH\0=l)  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: @/9>=#4c  
:_>\DJ'>  
KA`0g=  
\^Ep>Pq`]  
import java.awt.Dimension; 9X!ET!  
/*** Example class. The height and width values should never * be $2Kau 1  
negative. */  ~q*i;*  
public class Example{ OWqrD@  
  static final public int TOTAL_VALUES = 10; -UJ?L  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; Sbp  
  public Example (){ } yb69Q#V2  
k69kv9v@J  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ :qBGe1Sv(  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ xM% pvx.'L  
   if (height < 0 || width < 0) 9H>BWjS  
    throw new IllegalArgumentException(); +eU`H[iu  
    if (d[index] == null) 5f8"j$Az  
     d[index] = new Dimension(); +Dd"41  
     d[index].height = height; xtOx|FkYcl  
     d[index].width = width; I=U+GY:  
  } l(gJLjTH%  
  public synchronized Dimension[] getValues() VF\{ra;  
   throws CloneNotSupportedException{ l`DtiJ?$$0  
    return (Dimension[])d.clone(); 4 ^4d9?c  
  } S)+CTVVE  
} tL1P<1j_  
vuXS/ d  
i^R{Ul[  
= PV/`I_h  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: wcwQjHwd  
e]>/H8  
e$HQuA~Q;  
n|6?J_{<b>  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ Sobtz}A*  
  Dimension[] copy = (Dimension[])d.clone(); 2%5?F n=  
  for (int i = 0; i < copy.length; ++i){ 10?qjjb&  
   // NOTE: Dimension isn’t cloneable. !z?0 :Jg  
   if (d != null) mqdOu{kQ  
    copy = new Dimension (d.height, d.width); >jv\Qh  
  } $.wA?`1aSk  
  return copy; 21 ViHV  
} 7 %3<~'v[  
*_ PPrx5  
ZBF1rx?  
cI3y  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: #XcU{5Qm5  
cs t&0  
te)n{K",  
8`*`nQhWa  
public void store (int[] data) throws CloneNotSupportedException{ \2j|=S6  
  this.data = (int[])data.clone(); wra byRjK  
  // OK 6ga5^6W  
} *o!l/>4g  
BY$[g13  
<FQFv IKg  
-3` "E%9  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法:   La9r  
a&C.=  
4#_$@ r  
Hng!'  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ 7D   
  this.data = (int[][])data.clone(); // Not OK! U-eI\Lu  
} @ ICb Kg:  
public void rightStore (int[][] data){ 0Qp[\ia  
  // OK! Fom>'g*  
  this.data = (int[][])data.clone(); ]rnXNn;  
  for (int i = 0; i < data.length; ++i){ I(n }<)eF  
   if (data != null) J,(7.+`~#  
    this.data = (int[])data.clone(); 0aogBg_@K  
  } 3"Yif  
} 9KyZEH;pY  
Ib6(Bp9.L  
d/]|657u  
N 'i,>  
IM=+3W;ak  
   六、常见错误6#:检查new 操作的结果是否为null %l]Rh/VPn?  
\DS^i`o)rY  
@;;G88=  
3b@VY'P  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: :Jk33 N4y0  
7TpRCq#  
3{e'YD~hP  
iX%n0i  
Integer i = new Integer (400); > ws!5q  
if (i == null) IC/Q  
throw new NullPointerException(); j=9ze op %  
L,$3Yj  
=m9i)Q  
) |MJnx9  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 oNIFx5*Z  
(ND%}  
7}%H2$Do  
 HxIoA  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 bAiJn<  
s"coQ!e1.  
 七、常见错误7#:用== 替代.equals \(fq8AL?  
TF\sP8>V  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: 4mJFvDZV`  
|1Hc&  
0% +'  
:6D0j  
int x = 4; !y. $J<  
int y = 5; Jq)U</  
if (x == y) /H)Br~ l  
  System.out.println ("Hi"); {cR=N~_EO  
// This ’if’ test won’t compile. 63M=,0-Qt  
if (x.equals (y)) DsGI/c  
  System.out.println ("Hi"); ertBuU  
5un^yRMB-  
@5E,:)T*wR  
^N-'xy  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 j5^-.sEEw  
b#a@ rh  
:Q7mV%%  
X;VQEDMPU  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 ="'- &  
DP*@dFU"  
2h q>T&8  
!Lkm? (_  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 h e&V# #  
8+&JQ"UaB  
mU@xc N  
>DP:GcTG  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 R ]P;sk5  
>1ZJ{se  
($>XIb9f  
[s}/nu~U  
  八、常见错误8#: 混淆原子操作和非原子操作 4pPI'd&/7  
e_rzA  
!ni>\lZ  
K5`Rk" s  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: Jhy(x1%  
10O$'`  
p3yU:q#A  
9$RI H\*  
public class Example{ $iPP|Rw  
  private int value; // More code here... P^i.La,  
  public void set (int x){ *7RvHHf  
   // NOTE: No synchronized keyword 2d`c!  
   this.value = x; ]tY:,Mfs  
  } /S~m)$vu  
} Fm3t'^SqF  
.=<$S#x^Hb  
8\Hr5FqB(  
W)4QOS&  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: 2"&)W dm  
j@| `f((4  
Ik~1:D]f  
fdv`7u+}a  
public void increment (){ &leK}je [  
  // This is effectively two or three instructions: 1@_T  m  
  // 1) Read current setting of ’value’. ?WFh',`:  
  // 2) Increment that setting. *e/K:k  
  // 3) Write the new setting back. b$- e\XB!  
  ++this.value; xs!p|  
} lZWX7FO'  
VKW|kU7Cs$  
s9j7Psd  
Uyx&E?SlEq  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: n]N96oD  
x0Z5zV9  
*#&*`iJ(  
r;m`9,RW  
public synchronized void increment (){ |vILp/"9=W  
  ++this.value; %*W<vu>H  
} <Kt3PyF  
>M;u*Go`QO  
g^~Kze  
tju|UhP3  
  九、常见错误9#:在catch 块中作清除工作 &`!^Zq vG  
aGoE,5  
7r 0,> 3"  
0KvVw rWJ  
  一段在catch块中作清除工作的代码如下所示: ,1 UZv>}S  
Qa`hR  
^b-18 ~s  
tIuoD+AW  
OutputStream os = null; nII^mg~  
try{ sl|_=oXT  
  os = new OutputStream (); jirbUl  
  // Do something with os here. glUo7^ay7  
  os.close(); nH[+n `{o  
}catch (Exception e){  ux-CpI  
  if (os != null) * fc-gAj  
  os.close(); c&'JmKV>&  
} %f ju G  
z#Nl@NO&  
:`Az/U[  
.EP6oKA  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: `-UJ /{  
'Kbl3fUF  
jC, FG'P  
G|u3UhyB  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 BNucc']  
xWX*tJ4  
eon!CE0  
b,^*mx=  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 ;<wS+4,  
mpay^.(%  
K_BPZ5w  
^TFs;|..  
  3.close()可能会抛出异常。 d- E4~)Qy  
zO=%J)-=  
'vIx#k4D1  
`a]44es9q  
  上面代码的一个更优版本为: Nt-<W+,  
D'[Uc6  
pwX C  
Z)"61) )  
OutputStream os = null; {]}s#vvy  
try{ @QEqB_W  
  os = new OutputStream (); 0pgY1i7  
  // Do something with os here. e}{U7xQm1  
}finally{ "ZMkL)'7-  
  if (os != null) KeBQH8A1N  
   os.close(); 5g ;ac~g  
} ;/8{N0  
[=TCEU{"~  
SU%DW4 6  
UlovXb  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 G*}F5.>8(  
V5rp.~   
PX,rWkOce  
v."Dnl  
  十、常见错误10#: 增加不必要的catch 块 9.+/~$Ht  
,LYFEq_  
`,Vv["^PB  
-_^c6!i  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 F[`ZqW  
#Gf+=G  
i+vsp@d  
u<tk G B  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 ; y.E!  
\gO,hST   
Iw=Sq8  
}nx=e#[g%2  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: I$q>  
*OTS'W~t  
o'V%EQ  
Q9?t[ir  
try{ m7|RD]q&  
  // Nifty code here ((3}LQ  
}catch(Exception e){ ^4$ 'KIq  
  throw e; cPF<D$B  
}finally{ ;[0&G6g  
  // Cleanup code here C2F0tr|  
} #gbH^a'  
2y GOzc  
i%{X9!*%TX  
y=sGe!^  
  不必要的catch块被删除后,上面的代码就缩短为: f@V3\Z/6E  
a}nbo4jK  
Y:QD   
O>0VTW  
try{ `)>7)={  
  // Nifty code here : mGAt[Cc  
}finally{ 7^e +  
  // Cleanup code here UVu DQ  
} )mcEQ-!b  
^>$P)=O:v  
]F*3"y?)2  
^HA %q8| n  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 X]*QUV]i  
VM=+afY5M  
oR#:Nt X@  
'\DSTr:N  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 HeN~c<NuB  
v90T{1+M|4  
j2n,f7hl.  
Ukphd$3J=  
  小结 qN| fEO>  
VHUW]8We  
30cd| S?  
&XLD S=j  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 ?w&SW{ I  
/X8 <C=}  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

您目前还是游客,请 登录注册
温馨提示:欢迎交流讨论,请勿纯表情、纯引用!
认证码:
验证问题:
3+5=?,请输入中文答案:八 正确答案:八