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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 #m$H'O[WG\  
$hKgTf?  
!?l 23(d  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 ;euWpE;E\#  
a@8knJ|  
3_h%g$04 s  
PA,j;{,(b  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 qWanr7n]@  
?5(L.XFm  
9txZ6/  
Ys<wWfW  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 QlXy9-oJ"  
Rp@u.C <  
htF&VeIte  
y:,{U*49  
  一、常见错误1# :多次拷贝字符串  R(zsn;  
wz, \zh  
\]9;c6(  
#5H@/o8!s=  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 EXBfzK)a  
Iewq?s\Fo  
wZC'BLD  
'@fk(~|  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: &>s(f-\8  
>)N#n`  
}2\"(_  
plf<O5'  
String s = new String ("Text here"); JHQ8o5bEQp  
@?1%*/  
mD=?C  
t&&OhHK  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: *,R e&N8  
t%TZu>(1O  
^#=L?e  
c^bA]l^a  
String temp = "Text here"; }!d}febk_  
String s = new String (temp); "(xS  
.H>Rqikj  
djSN{>S  
Olno9_'  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: 4/h2_  
Gt1Up~\s  
Gg!))I+  
jNyC%$  
String s = "Text here"; y&CUT:M6  
9.@(&  
9:Y:Vx  
jqLyX  
  二、常见错误2#: 没有克隆(clone)返回的对象 cr/|dc'  
H 0h  
^s?wnEo;j  
O[`Ob6Q{F  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: l~=iUZW<  
:rj78_e9  
:J~j*_hZ  
bo*q{@Ue  
import java.awt.Dimension; B@=Yj_s  
/***Example class.The x and y values should never*be negative.*/ O<E0L&4-&  
public class Example{ yp4G"\hN9  
  private Dimension d = new Dimension (0, 0); $YC~02{  
  public Example (){ } $e_ps~{7$  
~H$XSNPi  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ p']AXJ`Z  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ ]S:@=9JB'  
   if (height < 0 || width < 0) [_0g^(`  
    throw new IllegalArgumentException(); j~{2fd<>  
    d.height = height; i f"v4PHq  
     d.width = width; N0piL6Js  
  } Stc\P]%d  
K@i*Nl  
  public synchronized Dimension getValues(){ esHiWHAC  
   // Ooops! Breaks encapsulation xL BG}C  
   return d; q)~qd$yMS  
  } `u}x:f !  
}  #.><A8J  
9?:S:Sq  
nx4aGS"F:  
\fhT#/0N  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: toWmm(7v  
ZX0c_Mk=  
xHG oCFB  
3dbf!   
Example ex = new Example(); HDEG/k/~m  
Dimension d = ex.getValues(); ;1dz?'%V  
d.height = -5; \PFx# :-c  
d.width = -10; |W <:rT  
/Ow?nWSt  
KRtu@;?  
93J)9T  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 }*'ha=`J  
bxN;"{>Xz  
6+5Catsn  
V!P3CNK  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 V9 VP"kD  
AAW7@\q.  
6:,^CI|@ t  
j+9 S  
  更好的方式是让getValues()返回拷贝: R]Oy4U,f  
(*ng$z Z$  
V\"5<>+O  
[!le 9aNg  
public synchronized Dimension getValues(){ 5\S7Va;W  
return new Dimension (d.x, d.y); sV<4^n7  
} w b[(_@eZ  
X W)A~wPBs  
=5`@:!t7  
 ~Hs{(7   
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 dO[4}FZ$  
]KeNC)R  
_p&$X  
w#T,g9  
  三、常见错误3#:不必要的克隆  62jA  
wDO5Zew!  
8:% R |b  
/6zpVkV  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: t {"iIz_S  
m3!M L>nLt  
GU3/s&9  
{9".o,  
/*** Example class.The value should never * be negative.*/ F 29AjW86  
public class Example{ 1%"` =$q%  
  private Integer i = new Integer (0); ^rwSbM$  
  public Example (){ } ]e.+u  
fO^6q1a  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ u`@f ~QP0  
  public synchronized void setValues (int x) throws IllegalArgumentException{ 8~E)gV+v  
   if (x < 0) ;#9| l=  
    throw new IllegalArgumentException(); MPbPq3an  
    i = new Integer (x); l*Ei7 |Z  
  } <&:&qn gg  
8>q% 1]X  
  public synchronized Integer getValue(){ =on!&M  
   // We can’t clone Integers so we makea copy this way. GiXde}bm  
   return new Integer (i.intValue()); 3+3m`%G  
  } Y}uQ`f  
} 4P!DrOB  
s RQh~5kM  
ok[=1gA#h  
M7R&J'SAY  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 t3$gwO$  
JF%=Bc$C  
io7U[#  
C-u/{CP  
  方法getValue()应该被写为: kA!(}wRL  
K<6x4ha  
':D&c  
2nkj;x{H$  
public synchronized Integer getValue(){ EAw#$Aq=  
// ’i’ is immutable, so it is safe to return it instead of a copy. *t{c}Y&@  
return i; a~F@3Pd  
} ;J-Ogt@d7  
v8bl-9DQ  
xsDa!  
*g4Cy 8$  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: ]A$^ l,  
^YJA\d@  
WWW#s gM%  
3C=clB9<  
  ?Boolean DhNo +"!z  
   ?Byte Sn2Ds)Pfx3  
   ?Character ll\^9 4]Q  
   ?Class k(z<Bm  
   ?Double xg,]M/J  
   ?Float A}bHfn|  
   ?Integer eD{ @0&   
   ?Long 8='21@wrN  
   ?Short 8UT%:DlxQ  
   ?String #A9_A%_.h  
   ?大部分的Exception的子类 <hZ}34?]i2  
M |?p3%  
?w37vsN  
V/}>>4  
 四、常见错误4# :自编代码来拷贝数组 qzt2j\v  
0j!ke1C&C  
8V|jL?a~  
&rztC]jF  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: R P:F<`DB|  
]Wd`GI  
e=o{Zo?H=  
mERrcYY{  
public class Example{ x56 F  
  private int[] copy; e9@fQ  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ xSDE6]  
  public void saveCopy (int[] data){ x*&&?nV Iz  
   copy = new int[data.length]; #VdI{IbW  
   for (int i = 0; i < copy.length; ++i) E)Qh]:<2v  
    copy = data; `x$}~rP&)!  
  } 'CX.qxF1;p  
}  n22hVw  
+yb$[E*  
f'6qJk%J  
)xvx6?Ah|  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: R^yZG{?t  
_d[2_b1  
6+ $d  
KtU GI.X  
void saveCopy (int[] data){ 40Qzo%eL  
  try{ OKZam ik~  
   copy = (int[])data.clone(); 5<O61Lgx  
  }catch (CloneNotSupportedException e){ HM@}!6/s  
   // Can’t get here. L);||]B  
  } VyoE5o  
} >[XOMKgQ](  
co^P7+j  
Krr?`n  
$}^\=p}X  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: N=Uc=I7C  
@ojg`!,  
I,<>%Z|'  
\'??  
static int[] cloneArray (int[] data){ Jn<e"  
  try{ qBBYckS.  
   return(int[])data.clone(); I#S~  
  }catch(CloneNotSupportedException e){ n-y^ 7'v  
   // Can’t get here. iijd $Tv  
  } -?aw^du  
} yF/< :  
-.b Io  
HTUYvU*-  
p&OJa$N$[  
  这样的话,我们的saveCopy看起来就更简洁了: V+=*2?1  
=tS[&6/  
TDl!qp @  
xMSNrOc  
void saveCopy (int[] data){ yL ;o{ G  
  copy = cloneArray ( data); hINnb7 o  
} Q.9Ph ~  
]@/^_f>D  
?Rt 1CDu  
x0u?*5-t  
  五、常见错误5#:拷贝错误的数据 7~kpRa@\P  
5mna7 BCEb  
^p"4)6p-W  
KkdG.c'  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: uP%axys  
hsHVX[<5`  
D%jD 8p  
}RA3$%3  
import java.awt.Dimension; foFg((tS  
/*** Example class. The height and width values should never * be "rjv5*z^&  
negative. */ "#-Nqq  
public class Example{ mmrW`~-  
  static final public int TOTAL_VALUES = 10; lPRdwg-  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; h;EwkbDQg>  
  public Example (){ } .#=j <&  
;.nP%jD  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ <+? Y   
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ P\22op_te-  
   if (height < 0 || width < 0) CJMaltPp&  
    throw new IllegalArgumentException(); t+=12{9;f  
    if (d[index] == null) Ad]<e?oN=  
     d[index] = new Dimension(); $[M} K  
     d[index].height = height; jiA5oX^g  
     d[index].width = width; 4Vu'r?  
  } 3 x"@**(Q  
  public synchronized Dimension[] getValues() fa!3/X+  
   throws CloneNotSupportedException{ lFp!XZ!  
    return (Dimension[])d.clone(); 1u"R=D9p,=  
  } ).0V%}>  
} *? K4!q'  
/S7+B ]  
1<LC8?wt  
%_B:EMPd  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: , @%C8Z  
vp\PYg;x  
! Q|J']|  
JqI6k6~Q^  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ c }<*~w;  
  Dimension[] copy = (Dimension[])d.clone(); ~vW)1XnK  
  for (int i = 0; i < copy.length; ++i){ \LIy:$`8  
   // NOTE: Dimension isn’t cloneable. ~In{lQ[QX  
   if (d != null) ; g Z%U  
    copy = new Dimension (d.height, d.width); fKL'/?LD]  
  } M&uzOK+  
  return copy; GXOFk7>  
} YPF&U4CN  
Bii6Z@kS  
sg3h i"Im  
w1wXTt  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: k~0#'I9  
_MM   
`4VO&lRm  
BN+V,W  
public void store (int[] data) throws CloneNotSupportedException{ R&6n?g6@/V  
  this.data = (int[])data.clone(); ]s*5[ =uc2  
  // OK 3C277nx  
} vP3K7En  
r_4T tP&UW  
-'80>[}q/  
7<h.KZPc  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: ixOEdQ  
Y3-]+y%l  
' 2>l  
84iJ[Fq{  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ S 3R|8?|  
  this.data = (int[][])data.clone(); // Not OK! 0Vf)Rw1%I  
} B }6Kd  
public void rightStore (int[][] data){ Q/9a,85  
  // OK! ^g9}f  
  this.data = (int[][])data.clone(); /VRUz++K  
  for (int i = 0; i < data.length; ++i){ ;LHDh_.pX  
   if (data != null) pU M&"V  
    this.data = (int[])data.clone(); $ I#7dJ"*  
  } `Jn,IDq  
} %/P=m-K  
'b8R#R\P  
KuA>"X  
M[A-1]'  
Oc7 >S.1  
   六、常见错误6#:检查new 操作的结果是否为null jyNb(Z  
?#?e(mpo  
JYPxd~T/-  
$np=eT)  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: -r!42`S  
7nm}fT z7  
]x1p!TSU  
^rL ,&rk  
Integer i = new Integer (400); v#zPH5xo  
if (i == null) d{W}p~UbH  
throw new NullPointerException(); TW>?h=.z  
.\$Wy$ d  
d&hD[v  
L*P_vCC  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 }qG#N  
,aI,2U91  
d;{y`4p)s  
(/'h4KS@  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 KZ]r8  
}xqXd%uz  
 七、常见错误7#:用== 替代.equals $)Wb#B  
@\ }sb]  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: TfL4_IAG.  
G=1m] >I8  
-)X{n?i  
VO9XkA7  
int x = 4; *MI)]S  
int y = 5; vEF=e  
if (x == y) SWT:frki`  
  System.out.println ("Hi"); r]9e^  
// This ’if’ test won’t compile. TaOOq}8c#  
if (x.equals (y)) )Lb72;!?  
  System.out.println ("Hi"); 8\DME  
@.k5MOn  
^+M><jE9  
|-n ('gQ[  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 `0?^[;[u[  
9<v}LeX  
!Vb,zQ  
or7pJy%4"  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 va^0JfQ  
A';n6ne%i  
' X}7]y  
@LcT-3u  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 i *B:El1  
WKxm9y V  
` VwN!B:  
Ae6("Oid  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 ?ZaD=nh$mK  
_-/x;C  
r sLc&2F  
W<Z$YWr  
  八、常见错误8#: 混淆原子操作和非原子操作 @HvScg*Y  
d5:tSO  
K@6`-|I  
dnwdFsf  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: O4E(R?wd  
l~['[Ub0)  
~5ubh2{  
?gN9kd)  
public class Example{ R4SxFp  
  private int value; // More code here... _jmkl B  
  public void set (int x){ v J-LPTB  
   // NOTE: No synchronized keyword S*g`d;8gV  
   this.value = x; UQ~4c,  
  } AFm,CINa  
} XIRR Al(,  
H*rx{F?  
pqeL%="p;  
H<Hrwy~  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: Pcdf$a"`  
LE K/mCL  
0 I @$ 0Gg  
]26mB  
public void increment (){ <m0{'xw  
  // This is effectively two or three instructions: Oqmg;\pm  
  // 1) Read current setting of ’value’. 61Bhm:O5W  
  // 2) Increment that setting. d&u 7]<yDA  
  // 3) Write the new setting back. ZBJ3VK  
  ++this.value; -w~(3(  
} Q&PB]D{  
MRs,l'  
2e.N"eLNt  
IA2GUnUhu  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: b=1%pX_  
z,x" a  
1ef'7a7e8  
 w;+ br  
public synchronized void increment (){ AW/wI6[T  
  ++this.value; /$:U$JVb?l  
} .T$D^?G!D  
13a(FG  
[4XC #OgA  
vbp-`M(  
  九、常见错误9#:在catch 块中作清除工作 ;v_V+t <$  
O:^'x*}  
j#VIHCzlr  
wbi3lH:;  
  一段在catch块中作清除工作的代码如下所示: qo_]ZKL44  
e\9g->DUs  
_!!}'fMC  
 M6Pw /S!  
OutputStream os = null; ]'k[u  
try{ ?'sXgo.}  
  os = new OutputStream (); ru{f]|  
  // Do something with os here. mM5|K@0|  
  os.close(); nJT4w|Yx  
}catch (Exception e){ ^i'y6J  
  if (os != null) K%gP5>y*9>  
  os.close(); rY,PSK/j  
} 7Ms90oE/c  
2]2H++  
#*2Rp8n  
EgNH8i  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: `c(\i$1JY)  
8Z#21X>  
AIh*1>2Xn  
_faJB@a_  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 \zu }\{  
<'&F;5F3V  
=Ndli>x}1  
+.@c{5J<  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 XdsJwn F  
ooE{V*Ie  
O k7zpq  
y94kX:q  
  3.close()可能会抛出异常。 %>y;zqZIU  
QaQ'OrP  
(Z-l/)Q  
} 0M{A+  
  上面代码的一个更优版本为: 4x,hj  
%l7fR}  
PLdn#S}.  
RUGv8"j  
OutputStream os = null; 9?EVQ  
try{ 7>n"}8i  
  os = new OutputStream (); J :S'uxM  
  // Do something with os here. u 9]1X1wV  
}finally{  &?+WXL>  
  if (os != null) 7pet Hi  
   os.close(); 4o5i ."l  
} } ` T8A  
<o0~H  
)acV-+{  
[X/(D9J  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 Sj-[%D*  
IU!Ht>  
M"U OgS  
vM4<d>  
  十、常见错误10#: 增加不必要的catch 块 64U6C*w+  
>85zQ 1aL  
?QpNjsF  
HY)ESU !  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 mqFq_UX/ T  
;&f1vi4  
^o d<JD4  
K]fpGo  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 SDBt @=Nl  
zn)yFnB!TH  
`;F2n2@  
Fr5 Xp  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: 3z[ $4L'.  
@`|)Ia<  
&5Y_>{,  
Hwu4:^OL|  
try{ @-"R$HOT  
  // Nifty code here 9y~"|t  
}catch(Exception e){ s@!$='|  
  throw e; <KQ(c`KW7  
}finally{ U7H9/<&o  
  // Cleanup code here Qn=$8!Qqa  
} ndi+xaQtG  
K)[8 H~Lm  
NK/4OAt%  
WY>Knp=  
  不必要的catch块被删除后,上面的代码就缩短为: M"wue*&  
Q~Ea8UT. #  
nvyB/  
8;n_TMb  
try{ 6E^~n  
  // Nifty code here &88oB6$D^q  
}finally{ ? +`x e{k  
  // Cleanup code here \dkOK`)b  
} D7Zm2Kj  
Z8&' f,  
CAgaEJhX3  
0=![fjm  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 8MZ$T3IM  
(lWq[0^N  
PW)aLycPK  
4~|<` vqN  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 {THqz$KN  
cm@;*  
Vb)zZ^va+  
: F9|&q-W,  
  小结 6 bO;&  
!'W-6f  
jv&+<j`r  
~&g a1r2v?  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 urZ8j?}c  
)2.)3w1_4  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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