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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 V[;^{,;  
A4tb>O M  
7~qyz]KkE  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 ii5dTimRJ  
l9Av@|  
01 <Ti"  
h( DmSW  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 8x`E UJ  
5F78)q u6N  
X|'[\v2ld  
="('  #o  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 e9;5.m  
w }=LC#le  
P,s>xM  
<{cf'"O7)  
  一、常见错误1# :多次拷贝字符串 _[pbf ua  
TtWWq5X|  
 GUps\:ss  
7Tc^}Q  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 <u}[_  
8dLK5"_3  
++!'6! l  
yIu_DFq%  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: 2dCD.9s9~  
S=a>rnF  
pS~=T}o  
' j\~> a3\  
String s = new String ("Text here"); v=~+o[  
f+%s.[;A  
YJF|J2u  
MmQk@~  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: ID~}pEQ  
6SF29[&  
*o=[p2d"X  
!8I80 :e_~  
String temp = "Text here"; Y;{(?0 s  
String s = new String (temp); !8*7{7  
#'5{ ?Cb  
'd]t@[#  
+' SG$<Xv  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: J|u_45<  
eWr2UXv$  
1nR\ m+{  
6lm<>#_  
String s = "Text here"; S|O#KE  
G4^6o[x  
'3=@UBs  
%-L T56T  
  二、常见错误2#: 没有克隆(clone)返回的对象 GK[9Cm"v  
u =lsH  
+t8#rT ^B  
;+*/YTkC+P  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: #'97mg  
ZU;nXqjc  
xk7VuS *  
6GoQJ  
import java.awt.Dimension; Uka(Vr:  
/***Example class.The x and y values should never*be negative.*/ t4UL|fI  
public class Example{ :Q}Zb,32  
  private Dimension d = new Dimension (0, 0); Jo\karpb  
  public Example (){ } oxug  
]4B&8n!  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ _FkH;MGWS  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ #i=m%>zjN  
   if (height < 0 || width < 0) Lb~' I=9D  
    throw new IllegalArgumentException(); 825 QS`  
    d.height = height; y;f nC5Q  
     d.width = width; x b!&'cw  
  } 23X-h#w  
,MUgww!.  
  public synchronized Dimension getValues(){ hX:yn:P~  
   // Ooops! Breaks encapsulation C3KAQ U  
   return d; :464~tHI[`  
  } a&Me#H{  
} h>A~yDT[  
!1#=j;N`  
sY* qf=  
w& )ApfL  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: jP.dQj^j&  
ywj'O e41  
2p~G][  
#Eqx E o;  
Example ex = new Example(); R] l2,0:  
Dimension d = ex.getValues(); v7<r- <I[  
d.height = -5; WH<\f |xR  
d.width = -10; bp'\nso/  
nq\~`vH|Gd  
oGL2uQXX  
9O\yIL  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 |<#yXSi  
+A1xqOB  
PJAM_K;  
m,Mg  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 gR wRhA/  
s6!! ty;Y  
[f?x ,W~  
9 u>X,2gUR  
  更好的方式是让getValues()返回拷贝: 26MoYO!k  
Ki(0s  
=<Ss&p>  
K<v:RbU|[1  
public synchronized Dimension getValues(){ >fp_$bjd  
return new Dimension (d.x, d.y); ;".]W;I*O  
} A`V:r2hnb  
,qIut|C*  
cK75Chsu  
W{t- UK   
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 !!6g<S7)  
-ug -rdXV  
HFX,EE  
1%7zCM0s  
  三、常见错误3#:不必要的克隆 Q@rlqWgU ~  
#yOeL3|b'  
bLO^5`6  
 @4>?Y=#  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: 3m"9q  
x";4)u=  
uk3PoB^>  
Lt {&v ^y  
/*** Example class.The value should never * be negative.*/ MpJ]1  
public class Example{ }sM_^&e4X  
  private Integer i = new Integer (0); nr%P11U\c  
  public Example (){ } NYPjN9L  
,PX7}//X^  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ uP1]EA  
  public synchronized void setValues (int x) throws IllegalArgumentException{ )_K:A(V>  
   if (x < 0) \n-.gG  
    throw new IllegalArgumentException(); k!!d2y6  
    i = new Integer (x); &_3o1<  
  } )SfM`W)Y  
rrl{3 ?  
  public synchronized Integer getValue(){ q]v{o8:U  
   // We can’t clone Integers so we makea copy this way. =9A!5  
   return new Integer (i.intValue()); rR(\fX!dg  
  } 2K2_-  
} XW*d\vDun  
}KV)F,`  
/Mw0<#  
`91?^T;\F  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 <\L=F8[  
VKy3tW/_&  
i7`/"5I  
hho\e 8  
  方法getValue()应该被写为: 6g"qwWZp  
xfA@GYCfT  
Suk;##I  
K?=g IC:  
public synchronized Integer getValue(){ ^Fwdi#g  
// ’i’ is immutable, so it is safe to return it instead of a copy. |qb-iXW=  
return i; 3#h@,>Z;  
} cFoeyI#v  
_j]vR  
E>kgEfzxP  
%D[6;PT  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: !-8y;,P  
jC'Diu4|Q  
n;r W  
*m&(h@l  
  ?Boolean +P=I4-?eX  
   ?Byte }nWW`:t kx  
   ?Character {Yv5Z.L&(  
   ?Class O_Z   
   ?Double q`@8  
   ?Float j8?rMD~  
   ?Integer sB<y(}u  
   ?Long %kiPE<<x  
   ?Short y{QF#&lW  
   ?String *YWk.  
   ?大部分的Exception的子类 <2fvEW/#v  
^ ]SU (kY  
m"~),QwF9  
)IGx3+I ,  
 四、常见错误4# :自编代码来拷贝数组 Ce_l\J8G  
Og"\@n  
Wp//SV  
&MGM9 zm-]  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: 3@mW/l>X  
qZX\riR  
%>,Kd6bdg  
Cx ;n#dn*  
public class Example{ >>0c)uC|W  
  private int[] copy; 5}`e"X  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ B@,#,-=  
  public void saveCopy (int[] data){ 4|KtsAVp{  
   copy = new int[data.length]; oqy}?<SQ  
   for (int i = 0; i < copy.length; ++i) 'h;x>r  
    copy = data; `MXGEJF  
  } Dqm;twd>  
} VY "i>Ae  
pAc "Wo(Q  
$(;0;!t.  
Y!AQ7F  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: \rmge4`4  
KJ8Qi+cZ  
Eo_; N c  
@'FOM  
void saveCopy (int[] data){ 4z!(!J )  
  try{ G0v<`/|>}  
   copy = (int[])data.clone(); <84d Vg  
  }catch (CloneNotSupportedException e){ Iqb|.vLG  
   // Can’t get here. Z;J{&OJ3qM  
  } "9!CsloWhz  
} ^I6GH?19>e  
$|}PL[aA#  
Vc| uQ8Mi  
G:{\-R'  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: pc/]t^]p  
nWv6I&  
JiCy77H  
-LEpT$v|  
static int[] cloneArray (int[] data){ Qb&gKQtt@  
  try{ ah0  
   return(int[])data.clone(); !7aJfs2  
  }catch(CloneNotSupportedException e){ IT\ x0b cv  
   // Can’t get here. !<j)D_  
  } kIYV%O   
} 1IV R4:a  
6O'6,%#  
2V=bE-  
o|7 h  
  这样的话,我们的saveCopy看起来就更简洁了: kK$*,]iCp  
L=5Fvm  
{Aq:Kh`&  
?FwjbG<  
void saveCopy (int[] data){ ,{u'7p  
  copy = cloneArray ( data); RaLc}F)9   
} y`.m'n7>P  
$+@xwuY'+  
ouR(l;  
z7um9g  
  五、常见错误5#:拷贝错误的数据 vP{;'R  
hXz@ (cF  
 0@dN$e  
HVK./y qy  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: f/RDo4  
d3K-|  
Ld3!2g2y7&  
;OfZEy>7  
import java.awt.Dimension; _=RK  
/*** Example class. The height and width values should never * be k>q}: J9V  
negative. */ )90K^$93"  
public class Example{ D^e7%FX  
  static final public int TOTAL_VALUES = 10; /f) #CR0$  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; Z@b GLS  
  public Example (){ } &[|P/gj#>  
SHT^Etri  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ [*AWCV  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ gtV*`g  
   if (height < 0 || width < 0) ,7nA:0P  
    throw new IllegalArgumentException(); 2%(RB4+  
    if (d[index] == null) <{UjO  
     d[index] = new Dimension(); cJ!C=J  
     d[index].height = height; qmM%MPv  
     d[index].width = width; S2*ER  
  } KK4"H]!.  
  public synchronized Dimension[] getValues() "[BDa}Il  
   throws CloneNotSupportedException{ FkJX)  
    return (Dimension[])d.clone(); BbdJR]N/!h  
  } W:z!fh-  
} Ps_q\R  
N"wp2w  
[Y=X^"PF  
[#-!&>  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: Yab=p 9V;;  
jwd{CN%  
@"E{gM@B  
xMAb=87_  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ t=Um@;wh  
  Dimension[] copy = (Dimension[])d.clone(); T11;LSD  
  for (int i = 0; i < copy.length; ++i){ ?Ok@1  
   // NOTE: Dimension isn’t cloneable. =ap6IVR  
   if (d != null) b[_${in:  
    copy = new Dimension (d.height, d.width); )Mj $/  
  } c@1q8,  
  return copy; GxR, 3  
} f8'$Mn,  
/K@{(=n  
]9}T)D f'  
b-XC\  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: !(wH}ti  
>7^+ag~&  
'o*:~n  
&RP}w%I1  
public void store (int[] data) throws CloneNotSupportedException{ |rbl sL2?Z  
  this.data = (int[])data.clone(); 5h4E>LB.B  
  // OK :<$IGzw}.  
} ttK`*Ng  
>.-$?2  
hIr$^%  
njy^<7 ;  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: KtFxG6a  
AIXvS*Y,  
!dW77kLTg  
Q$(0Nx<  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ Fc"&lk4e  
  this.data = (int[][])data.clone(); // Not OK! v8`)h<:W?  
} M:~/e8Xv  
public void rightStore (int[][] data){ Ph&fOj=pFb  
  // OK! aumXidb S  
  this.data = (int[][])data.clone(); .u_k?.8|  
  for (int i = 0; i < data.length; ++i){ /@H2m\vBX  
   if (data != null) vsM] <t  
    this.data = (int[])data.clone(); Gkr^uXNg#  
  } ffQ%GV_  
} ($oO, c'z  
/~LXY< -(  
v^lR]9;  
{xC CUU  
| 3`qT#p{  
   六、常见错误6#:检查new 操作的结果是否为null YW}1iT/H  
x\Y%/C[Kc  
yt&eY6Xp  
#c0 dZ  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: y%H;o?<WX  
B=;pyhc  
lbES9o5  
LaE;{jY  
Integer i = new Integer (400); axY-Vj  
if (i == null) O8+[ )+6^  
throw new NullPointerException(); c#G]3vTdE  
i;0`d0^  
tH:K6^oR  
 afEp4(X~  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 4M8AYh2)  
;UgRm#  
v`hv5wQ  
0;)6ZU  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 H&L=WF+x  
QD:0iD?  
 七、常见错误7#:用== 替代.equals #^Dc:1,  
%f;(  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: Ar sMqb  
r1FE$R~C=  
KrkZv$u,  
Q:~w;I  
int x = 4; oQDOwM,  
int y = 5; s8|#sHT  
if (x == y) =tcPYYD  
  System.out.println ("Hi");  ZW2#'$b  
// This ’if’ test won’t compile. S'-<p<;D\B  
if (x.equals (y)) yj$S?B Ee  
  System.out.println ("Hi"); FGx_ qBG4|  
.bl0w"c^qq  
+^+wS`Y  
t%z7#}9$  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 [6S"iNiyKT  
SEchF"KJQF  
Y%:p(f<  
Wsz='@XvB  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 A2&&iL=j/  
`Ik}Xw  
+$>ut r  
LO61J_J<  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 g}^ /8rW  
Xy*X4JJh^  
,.FTw,<  
wH Z!t,g  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 CES FkAj~  
wI}'wALhA  
xN"KSQpu  
W,{`)NWg  
  八、常见错误8#: 混淆原子操作和非原子操作 iL 4SL}P  
z3*G(,  
0v7;Z xD  
w78Ius,  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: )Elr8XLw  
}2c}y7B,_  
{t/!a0\HS  
[ !R%yD;  
public class Example{ <uTsX v  
  private int value; // More code here... d7^XP  
  public void set (int x){ }ZmdX^xB  
   // NOTE: No synchronized keyword tiE+x|Ju"  
   this.value = x; 'c$9[|x  
  } X3m?zQbhv  
} ~v54$#CB  
>Au<y,Tw  
(x^|  
|(P;2q4>  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: pWx3l5)R  
}\0"gM  
xY'g7<})$  
YqJIp. Z  
public void increment (){ /MU<)[*Ro  
  // This is effectively two or three instructions: xRY5[=97  
  // 1) Read current setting of ’value’. #wjBMR%  
  // 2) Increment that setting. E7h}0DX  
  // 3) Write the new setting back. R$@.{d&:w  
  ++this.value; |wb7`6g  
} O(~Vvoq  
}[DAk~  
.E}});l  
.-|O"H$  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: rOD KM-7+  
qJ/C*Wqic  
ww,c)$  
>ATW/9r  
public synchronized void increment (){ zc1Zuco| R  
  ++this.value; Km nr }Lp9  
} ~+np7  
\S<5b&G  
,pASjFWi  
I>5@s;  
  九、常见错误9#:在catch 块中作清除工作 ^"l$p,P+  
)mPlB.  
g7P1]CZ}  
"n6Y^  
  一段在catch块中作清除工作的代码如下所示: +n,BD C;  
Fq9[:  
(Lo2fY5  
84[^#ke  
OutputStream os = null; W$JA4O>b  
try{ vRq xZN  
  os = new OutputStream (); V3oAZ34)  
  // Do something with os here. Vw*x3>`  
  os.close(); ^8m+*t  
}catch (Exception e){ cMl%)j-  
  if (os != null) cBI )?  
  os.close(); X u_<4  
} }SV3PdE  
_6m3$k_[MJ  
F&*M$@u5  
RF)B4D-W  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: i-?zwVmn  
T,uIA]  
|&Pl4P  
>u)ZT  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 \Wc/kY3&  
rl%Kn^JJ~  
XX-T",  
V@S/!h+  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 h,>L(=c$O  
hzLGmWN2j8  
$Jm2,Yv  
g8+,wSE  
  3.close()可能会抛出异常。 ikhX5 &e  
%20-^&zZ  
$/TA5h  
3}21bL  
  上面代码的一个更优版本为: {0e{!v  
-mJ&N  
Z5Ihc%J^  
z4641q5'm  
OutputStream os = null; r$FM8$cJ  
try{ PAwg&._K  
  os = new OutputStream (); xD# I&.  
  // Do something with os here. f#kevf9zc  
}finally{ ?0NSjK5ma  
  if (os != null) k6L373e#Q  
   os.close(); !>y}Xq{bm3  
} )m8>w6"  
oRThJB  
iT5SuIv  
-Y=c g;  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 +g6j =%  
4sBoD=e  
ff1B)e  
sK-|xU.  
  十、常见错误10#: 增加不必要的catch 块 pE/3-0;}N  
k^5R f  
~|{)h^]@  
zXPJ;^Xxa  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 L*01l"5  
Q(5:~**I  
%4|}&,%%r  
.cS,T<$  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 T{4fa^c2J  
XjZao<?u  
ilp;@O6  
Bb [e[,ah  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: PnIvk]"Ab  
wu!_BCIy  
sQ:VrXwP  
b/,!J] W  
try{ x";.gjI |g  
  // Nifty code here 6a]f&={E  
}catch(Exception e){ K: o|kd  
  throw e; Ya&\ly /i  
}finally{ a^\- }4yR  
  // Cleanup code here % r   
} 4a.e ,gitf  
]7S7CVDk4  
>)J47j7{c  
[^d6cMEOlc  
  不必要的catch块被删除后,上面的代码就缩短为: {iIg 4PzrU  
$F@L$& ~  
D0y,TF  
76_8e{zbr  
try{ <x0uO  
  // Nifty code here -=[o{r`  
}finally{ W3{<e"  
  // Cleanup code here C+(Gg^ w  
} OwiWnS<  
w HHF=Q  
U??f<  
vgQhdtt  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 Z!RRe]"y  
r6`^>c  
? W2I1HEy  
)-9w3W1r  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 :S99}pgY  
^I'Lw  
iVpA @p   
@[>+Dzn[6  
  小结 t=W$'*P0}  
ttbQergS  
k$0|^GL8  
cfC;eRgq~  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 01-\:[{  
76IALJ00V  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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