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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 0BxO75m}o  
-qid.  
'hU&$lgMF  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 al#yc  
*( D_g!a  
-PEpy3dMY  
9)l[$X  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 >qcir~ &  
D?X97jNm  
?B@iBOcu[  
KZ/}Iy>As  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 T3'dfe U  
A3Ltk 2<  
q;>'jHh  
g>VkQos5"  
  一、常见错误1# :多次拷贝字符串 `P : -a7_  
crZ\:LeJ  
;I5HMc_a"  
Dc #iM0  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 ZVK;m1?'  
l#]Z?zW.  
;v8,r#4  
;}^Pfm8  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: /+02 BP  
|`:Uww+3  
Q8z>0ci3o  
mQo]k  
String s = new String ("Text here"); "xnek8F  
a&PoUwG  
0o`0Td  
TtkB  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: G ^r^" j  
LB2 2doW  
VpTp*[8O  
]J_Dn\  
String temp = "Text here"; i1|-  
String s = new String (temp); ffuV$#  
h2h$UZIv  
V 1#/ +~  
r95$B6  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: -I\_v*nA  
D/@:wY  
IE'OK  
X Uh)z  
String s = "Text here"; RFQa9Rxk  
HZfcLDrO  
J^0co1Y0  
d-xKm2sH  
  二、常见错误2#: 没有克隆(clone)返回的对象 {9'"!fH  
`|v0@-'$  
N \A)P  
5vg@zH\z  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: 2JtGS-t  
ed>_=i  
<J?i+b  
G8akMd]2  
import java.awt.Dimension; $\m=-5 0-  
/***Example class.The x and y values should never*be negative.*/ Ha4?I$'$  
public class Example{ Hdj0! bUx  
  private Dimension d = new Dimension (0, 0); Hsx`P  
  public Example (){ } Z*s/%4On  
_3hCu/BV  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ kTs)u\r.  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ iK=H9j  
   if (height < 0 || width < 0) !=k\Rr@qx  
    throw new IllegalArgumentException(); F;`of  
    d.height = height; qXP)R/~OZ  
     d.width = width; &k : |  
  } ?G.9D`95  
wQ(ME7 t  
  public synchronized Dimension getValues(){ t-_N|iW' 5  
   // Ooops! Breaks encapsulation *!l q1h  
   return d; <NT/+>:2  
  } J"FKd3~:E  
} NoZz3*j=  
.eq-i>  
v8-F;>H  
_qJ[~'m<^C  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: ^'v6 ,*:4  
YgdoQBQ  
,| xG2G6  
URJ"  
Example ex = new Example(); NjsP"  
Dimension d = ex.getValues(); ^vsOlA(4  
d.height = -5; P,D >gxl  
d.width = -10; *w> /vu  
5\EHu8  
Y6^lKw  
(WN'wp  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 >2>xr"  
-v>BeVF  
cGOE$nL  
<Hm:#<\  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 ?CL1^N%  
Jg;Hg[  
i!YZF$|  
sOU_j4M{  
  更好的方式是让getValues()返回拷贝: R0*DfJS:Z  
@YWfq$23  
otX#}} +  
MH{vFA4:,  
public synchronized Dimension getValues(){ mj5A*%"W  
return new Dimension (d.x, d.y);  6~$ <  
} I%{^i d@  
l_^>spF  
Z0`?  
S,Zjol%p  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 ;@v7AF6Hq  
*M- .Vor?R  
] p+t>'s  
>Z<ym|(T*  
  三、常见错误3#:不必要的克隆 |mY<TWoX  
br$!}7#=L  
^Fb"Is#S,  
cr,o<  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: E3NYUHfZ  
(IJf2  
f&^Ea-c  
Y k~ i.p  
/*** Example class.The value should never * be negative.*/ |[k6X=5  
public class Example{ X]  Tb4  
  private Integer i = new Integer (0); _mXq]r0  
  public Example (){ } =CRaMjN  
B;W=61d  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ e/@udau  
  public synchronized void setValues (int x) throws IllegalArgumentException{ Yn1U@!  
   if (x < 0) \EB]J\ x<  
    throw new IllegalArgumentException(); h`3;^T  
    i = new Integer (x); )-9|3`  
  } uVOpg]8d  
ZpI_/  
  public synchronized Integer getValue(){  _%i|*  
   // We can’t clone Integers so we makea copy this way. ] ^  
   return new Integer (i.intValue()); D8[&}D4  
  } ?ADk`ts~,}  
} y<3v/ ,Y  
G/<{:R"  
uz'beE  
|W:kzTT-T  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 ua7I K~8l  
S$muV9z2=  
mpr["C"l  
o#0NIn"GS/  
  方法getValue()应该被写为: 5\QNGRu"  
:peBQ{bj  
&[RC4^;\V  
RA.@(DN&  
public synchronized Integer getValue(){ vkbB~gr@*  
// ’i’ is immutable, so it is safe to return it instead of a copy. qc*+;Wi+5  
return i; xW"J@OiKL  
} nW|[poQK  
m\@Q/_ v  
+H ="5uO<  
V!FzVl=G  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: O@E&lP6  
i1aS2gFi_  
bIWcL$}4Q  
7Dm^49H  
  ?Boolean $8_*LR$  
   ?Byte hc0VS3 k)  
   ?Character $I1p"6  
   ?Class \?qXscq  
   ?Double _}JygOew  
   ?Float rR C3^X`u  
   ?Integer .iew5.eB+  
   ?Long zq1&MXR)l  
   ?Short 7zQD.+&L  
   ?String %@pTEhpF  
   ?大部分的Exception的子类 g08=D$P  
eTrGFe!8w  
J>Zd75;U  
y)(SS8JR  
 四、常见错误4# :自编代码来拷贝数组 A9tQb:  
\N"K^kR4  
rZpc"<U  
pF"z)E|^  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: by8d18:it  
xYwbbFGrG  
)]qFI"B7  
c1:op@t  
public class Example{ G9V zVx#T#  
  private int[] copy; CqrmdWN  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ cRU.   
  public void saveCopy (int[] data){ ]/d2*#  
   copy = new int[data.length]; A]=?fyPh{'  
   for (int i = 0; i < copy.length; ++i) |ZRl.C/e  
    copy = data; {v]>sn;P1  
  } >O\-\L  
} ( !Ml2  
P<2yCovn`  
xsAF<:S\  
09x\i/nb  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: 5l)p5Bb48c  
ih~c(&n0  
(G$m}ng  
4r5,kOFWb  
void saveCopy (int[] data){ typ*.j[q  
  try{ %o{vD&7\  
   copy = (int[])data.clone(); < W&~tVv  
  }catch (CloneNotSupportedException e){ 2 ] 4R`[#  
   // Can’t get here. Po^2+s(fY  
  } zlFl{t  
} Bq:@ [pCQ  
.!9]I'9M  
53(m9YLk  
0s<o5`v  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: RKBjrSZg8  
8E0Rg/DnT  
KE5f`h  
da[l[b;  
static int[] cloneArray (int[] data){ sDbALAp +  
  try{ r0S7e3xb  
   return(int[])data.clone(); @H{$,\\  
  }catch(CloneNotSupportedException e){ 0!(Ii@m=N  
   // Can’t get here. =20Q! wcu  
  } +9h6{&yr1  
} i [j`'.fj  
$ B$=,^)3  
XU SfOf(  
;#Mq=Fr-SG  
  这样的话,我们的saveCopy看起来就更简洁了: q5OW1%  
PK+][.6H  
.3HC*E.e  
PfuYT_p4s  
void saveCopy (int[] data){ 9qqEr~  
  copy = cloneArray ( data); jpBE| Nm  
} 9Z+@i:_}  
%F}`;>C3  
,:L}S03k  
SH`"o  
  五、常见错误5#:拷贝错误的数据 <&+l;z  
Y[x ^59  
:Z< 5iLq  
xaeY^"L  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: {b#c0>.8-  
8^4X/n  
jN*A"m  
(U7%Z<  
import java.awt.Dimension; H%%#^rb^  
/*** Example class. The height and width values should never * be }"cb^3  
negative. */ 2%@j<yS  
public class Example{ 1":{$A?OB  
  static final public int TOTAL_VALUES = 10; aa".d[*1  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; mIr{Wocx  
  public Example (){ } 2r* o  
^ePSI|EW  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ WVo%'DtF`  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ Rw. Uz&  
   if (height < 0 || width < 0) L)w& f  
    throw new IllegalArgumentException(); 2"i<--Y  
    if (d[index] == null) \!YPht  
     d[index] = new Dimension(); nFB;!r  
     d[index].height = height; 2nEj X\BY  
     d[index].width = width; FlkAo]  
  } |r /}r,t}  
  public synchronized Dimension[] getValues() dmF<J>[  
   throws CloneNotSupportedException{ O~t5qnu/}  
    return (Dimension[])d.clone(); 0{B5C[PTG  
  } L50`,,WF  
} B2,! 0Re  
b(XhwkGVq  
 vb70~k  
,*%8*]<=  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: ]X-ZRmB`  
<`N\FM^vo  
@:c 1+  
h+Z|s  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ -6H)GK14b  
  Dimension[] copy = (Dimension[])d.clone(); JdV!m`XpXy  
  for (int i = 0; i < copy.length; ++i){ <T7y85  
   // NOTE: Dimension isn’t cloneable. N.isvDk%  
   if (d != null) #2WBYScW0  
    copy = new Dimension (d.height, d.width); Vy5Q+gw  
  } ~w$8*2D  
  return copy; 4XKg3l1  
} <~Y4JMr"  
wvc?2~`  
r^\^*FD |  
^ #Wf  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: Hu'c )|~f  
h]zx7zt-  
?]7ITF  
i3Ffk+ |b  
public void store (int[] data) throws CloneNotSupportedException{ [&zP$i&  
  this.data = (int[])data.clone(); i "-#1vy=  
  // OK +ATN2 o  
} .:lzT"QXI  
wZOO#&X#r  
10 p+e_@  
5-C6;7%:  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: 7'&Xg_  
%d?%^) u,  
{?j|]j  
w1GCjD*y  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ qrdA?V V  
  this.data = (int[][])data.clone(); // Not OK! yDyq. -Q  
} V*)6!N[5  
public void rightStore (int[][] data){ {$s:N&5  
  // OK! r] ]Ke_s!  
  this.data = (int[][])data.clone(); ~q1s4^J  
  for (int i = 0; i < data.length; ++i){ r7IhmdA  
   if (data != null) '17=1\Ss6;  
    this.data = (int[])data.clone(); ~pF'Qw" z|  
  } o+tY[UX  
} &bL1G(}  
"@f`O  
DL~LSh  
*wSl~J|ZM%  
#Y{"`5>  
   六、常见错误6#:检查new 操作的结果是否为null &FK=w]P  
HML6<U-eS  
=W4cWG?+  
d[S!e`,iD  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: ,:v}gS?Uq  
)Z^( +  
^< E,aCy  
dIf Jr}ih  
Integer i = new Integer (400); t /47lYN)  
if (i == null) ioviJ7N% O  
throw new NullPointerException(); A2vOI8  
d>aZpJ[.  
v\HGL56T  
a v`eA`)S  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 *3k~%RM%?  
4,aBNuxWd  
=djzE`)0  
#h&?wE>  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 S9L3/P]  
LEhi/>T  
 七、常见错误7#:用== 替代.equals T&S< 0  
.oe,# 1Qh{  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: +g.WO5A  
 c\x?k<=  
YJ"gm]Pm  
d)0%|yX6  
int x = 4; \{&55>  
int y = 5; i 9b^\&&  
if (x == y) ]ny(l#Hu:  
  System.out.println ("Hi");  t]vz+VQ  
// This ’if’ test won’t compile. L8$7^muad  
if (x.equals (y)) sVC5<?OW!p  
  System.out.println ("Hi"); @ J"1 !`  
.:;i*  
* r%  
LD6fi  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 U .rH,`  
bX9}G#+U  
KcrF=cA  
o/[NUQSI  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 *U]f6Q<X  
' Wi*[  
xp39TiXJ*  
I%(+tJ  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 3oIoQj+D  
B02~/9*Y"  
)V>FU=  
r|#4+'  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 .O,gl$y}  
hrW.TwK  
&3^40s/+  
V}J W@  
  八、常见错误8#: 混淆原子操作和非原子操作 \l[5U3{  
yy>4`_  
1X{A}9nA  
Z$pR_dazU  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: C qxP@  
LCdc7  
*(HH71Y  
c]n4vhUa5  
public class Example{ 8+!$k!=X  
  private int value; // More code here... ,~3sba  
  public void set (int x){ xCQ<G{;C  
   // NOTE: No synchronized keyword _&:o"""Wf  
   this.value = x; JhD8.@} b~  
  } 56v<!L5%  
} HL)1{[|`  
EU\1EBT^  
F{}z[0  
sn *s7v:  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: :l 7\7IT  
0? l  
Fq{nc]L6  
g\^(>Ouc  
public void increment (){ xE9s=}  
  // This is effectively two or three instructions: w|M?t{  
  // 1) Read current setting of ’value’. S=my;M-  
  // 2) Increment that setting. z1L.  
  // 3) Write the new setting back. 0J_x*k6  
  ++this.value; VVf~ULZ-  
} g$:2c7uL  
\q,w)BE  
`S.;&%B\  
%bv<OMD  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: OrH&dY  
B8P%4@T  
JD'/m hN0  
!k[ zUti  
public synchronized void increment (){ ~svu0[Vx  
  ++this.value; aN7u j  
} QF^An B  
q=EQDHmh  
/bw-*  
S-L6KA{  
  九、常见错误9#:在catch 块中作清除工作 ))MP]j9 T  
BY 1~\M  
bLUn0)c  
hMDyE.X-  
  一段在catch块中作清除工作的代码如下所示: !<~Ig/  
k4`v(au^  
9 np<r82  
W]R5\ G*  
OutputStream os = null; gG $o8c-  
try{ R vY`9D  
  os = new OutputStream (); /wK7l-S  
  // Do something with os here. hqE#BnQxP,  
  os.close(); ;J`X0Vl$  
}catch (Exception e){ GnLh qm"\  
  if (os != null) JlH|=nIaj6  
  os.close(); XM)|v |  
} ,CvU#ab8$  
5Q^~Z},  
Q647a}  
ck^Z,AKL+  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: 6Z'zB&hM}  
p;'vOb  
nU`;MW/^w  
qVY\5`f@  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 w68qyG|wM  
Tq?W @DM*  
q`\lvdl  
8cd,SQ}y  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 BpK P]V  
7>4t{aRf_8  
](W #Tj5-  
Xau.4&\d  
  3.close()可能会抛出异常。 *]EcjK%  
TLkkB09fvk  
f8n'9HOw>  
zb3ir|  
  上面代码的一个更优版本为: B 51LZP  
& v`kyc  
v(0vP}[Q7E  
pLIBNo?  
OutputStream os = null; eygyVhJ  
try{ ES+&e/G"ds  
  os = new OutputStream (); @.gCeMlOf  
  // Do something with os here. .)o5o7H  
}finally{ 'IgtBd|K>  
  if (os != null) a@X'oV`(2b  
   os.close(); Kzmgy14o  
} |n~v_V2.0  
TX 87\W.  
Wqqo8Y~fq  
%W c-.E R  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 EXzY4D ^  
j^k{~]+_^]  
EYQ!ELuF  
mEqV&M1;7l  
  十、常见错误10#: 增加不必要的catch 块 dxd}:L~z  
0|U<T#t8?  
Oe=,-\&_  
A/.cNen  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 j9,X.?Xvx  
6v1j*'  
FX'W%_f,  
Nn^el' S'  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 PF+`3  
a#{a{>  
;J _d%  
J) (pGS@  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: B[*i}k%i  
Fl O%O D  
?oF@q :W  
4x3`dvfp/  
try{ Z`f _e?  
  // Nifty code here HsXFglQ  
}catch(Exception e){ ''(T3;^ +  
  throw e; 0 Hq$h  
}finally{ 9 (&!>z  
  // Cleanup code here HgMDw/D(  
} VP"L _Um  
7j]@3D9[:p  
{k)MC)%  
cEN^H  
  不必要的catch块被删除后,上面的代码就缩短为: Z]6D0b  
oDRNM^gz  
z C``G<TB  
?LW1D+  
try{ Z# :Ww  
  // Nifty code here }skXh_Vu4  
}finally{ HS2)vd@)  
  // Cleanup code here |GsLcUv6  
} 'J\%JAR@  
@B[V'|  
59)PJ0E  
g,1\Gj%y  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 _7;#0B  
sI.Ezuw  
Q'rG' |  
)h/fr|  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 >sP;B5S  
3}vlj:L  
j jwY{jV  
fu|I(^NV  
  小结 e]5QqM7  
e5AiIVlv  
I7}[%(~Sf/  
&2g1Oy~  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 D]0#A|n F  
7_|zMk.J*  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

您目前还是游客,请 登录注册
如果您在写长篇帖子又不马上发表,建议存为草稿
认证码:
验证问题:
3+5=?,请输入中文答案:八 正确答案:八