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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 DvhJkdLB>  
1ayL*tr  
jOV,q%)^,:  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 EdR1W~JZ  
_9*3Mr)2N  
^VabXGzo#  
h)7hk*I  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 =MMU(0 E  
/{il;/Vj  
dz_~_|  
h'%iY6!fA  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 _[M*o0[@W  
Qu]F<H*Y|  
;&=c@>!xP#  
vuN!7*d+  
  一、常见错误1# :多次拷贝字符串 :Aq==N_/2  
R<]f[  
!X5n'1&  
|}$ZOwc  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 $IUe](a{d  
Qx<86aKkF  
w`ebZa/j  
d^pzMaCI  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: .Aj4?AXWc  
H+lBb$  
(m:ktd=x  
B bP&-c  
String s = new String ("Text here"); pQ2'0u5w5  
n;QMiz:yY  
S3fyt]pp  
O S?S$y  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: 'qoDFR\v  
4+?d0  
8p"R4  
@?bO@  
String temp = "Text here"; {XR 3L'X  
String s = new String (temp); NW?.Ge.!P  
~[*\YN);  
VC T~"T2R  
GeydVT-  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: jT:z#B%  
KB@F^&L {  
S!oG|%VuB#  
\""sf{S9  
String s = "Text here"; :i};]pR   
8`]1Nt!*B  
~E^lKe  
Gm1[PAj  
  二、常见错误2#: 没有克隆(clone)返回的对象 y/9aI/O'  
C]01(UoSZ  
D-KQRe2@  
=G<i6%(^g  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: 7SVq fWp  
q-<t'uhs[  
%4#Q3YlyD  
FBk_LEcX  
import java.awt.Dimension; ]>_Ie?L)<  
/***Example class.The x and y values should never*be negative.*/ aePLP  
public class Example{  Oye:V  
  private Dimension d = new Dimension (0, 0); 9V/:1I0?&0  
  public Example (){ } ^hyY,X  
k. @OFkX.  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ {9_}i#,vR  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ A8jj]J+  
   if (height < 0 || width < 0) }<7S% ?TY  
    throw new IllegalArgumentException(); GYJ lX  
    d.height = height; &ZR}Z7E*=  
     d.width = width; V'Z Z4og  
  } uW{;@ 7N  
mSFh*FG  
  public synchronized Dimension getValues(){ 9L+g;Js$4  
   // Ooops! Breaks encapsulation sgxD5xj}4  
   return d; zQ>|`0&8   
  } a`t <R  
} *wu:fb2[(  
W3~xjS"h  
xp68-&  
*;u'W|"/~  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: 8p0ZIrD%  
G\4*6iw:  
l2|[  
T=~D>2C  
Example ex = new Example(); GUH-$rA  
Dimension d = ex.getValues(); lXnzomU  
d.height = -5; sngM4ikhs  
d.width = -10; Bkaupvv9S  
]Te,m}E  
xa&5o`>1G  
PN"s ^]4  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 oEN^O:9e  
ed\umQ]   
%K/zVYGm&  
Z!eW_""wp  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 tQYkH$e`/{  
}^a" >$DU  
HA#9y;\  
>JUOS2  
  更好的方式是让getValues()返回拷贝: yZc_PC`  
0*{ 2^\  
*rH# k?  
|9*8u>|RC  
public synchronized Dimension getValues(){ }\Ri:&?  
return new Dimension (d.x, d.y); $AyE6j_1gX  
} b>]MZhLJe  
K@R * V  
G.l ~!;  
xk\n F0z  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 N:% }KAc  
Spm7kw  
2zN"*Wkn  
i[V\RKH*F  
  三、常见错误3#:不必要的克隆 P+2@,?9#  
2^f6@;=M  
*{fL t  
'OjsV$_  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: )wdTs>W7  
79MF;>=tV  
Gw@]w;ed  
- :~"c@D  
/*** Example class.The value should never * be negative.*/ v3I-i|L<)  
public class Example{ P g.j]  
  private Integer i = new Integer (0); Bh0hUE  
  public Example (){ } FzM<0FJRX  
<Y"h2#M"  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ mR3-+dB/  
  public synchronized void setValues (int x) throws IllegalArgumentException{ lFT` WO  
   if (x < 0) `~;`q  
    throw new IllegalArgumentException(); NO'37d  
    i = new Integer (x); Q XLHQ_V  
  } zNRR('B?  
HpGI\s  
  public synchronized Integer getValue(){ Zv|TvlyT"  
   // We can’t clone Integers so we makea copy this way. Uw5AHq).  
   return new Integer (i.intValue()); =6H  
  } EgB$y"fs  
} RIV + _}R  
n5s2\(  
6*r#m%|   
Zog&:]P'F  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 fMl uVND  
`2l j{N  
3D^!U}E  
mnm 7{?#[  
  方法getValue()应该被写为: IDn$w^"  
+JlPQ~5  
~/m=Q<cV  
dW#T1mB  
public synchronized Integer getValue(){ 5h7M3s  
// ’i’ is immutable, so it is safe to return it instead of a copy. ,We'A R3X  
return i; -.t/c}a#  
} ]X\p\n'@j  
'MK"*W8QRM  
?&_u$Nn  
sp8P[W1a  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: rF\L}& Sw  
4Gor*{  
9?38/2kX4  
:c}"a(|  
  ?Boolean u6MHdCJ0y  
   ?Byte ]9hXiY  
   ?Character GJj}|+|  
   ?Class k\<8h%  
   ?Double :/XWk %  
   ?Float }O+`X) 9  
   ?Integer oa<%R8T?@  
   ?Long M"!{Dx~  
   ?Short o ~`KOe  
   ?String yBkcYHT  
   ?大部分的Exception的子类 6R'z3[K9  
kkU#0p?7  
kA4bv}  
1Rd2Xb  
 四、常见错误4# :自编代码来拷贝数组 35 d:r:  
ArVW2gL  
uWDWf5@  
4`zK`bRcK#  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: 5iZx -M  
hn[lhC  
opfg %*  
kps}i~Jb  
public class Example{ |YcYWok  
  private int[] copy; !$pnE:K  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ 32z2c:G  
  public void saveCopy (int[] data){ *6/OLAkyF  
   copy = new int[data.length]; x%`tWE|  
   for (int i = 0; i < copy.length; ++i) 1<D^+FC4b,  
    copy = data; b"pN;v  
  } /C6$B)w_*{  
} 3 4:Y_*  
2OZ<t@\OY  
mTBSntZx  
9+"ISXS  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: `;)op3A'  
RXw }Tb/D8  
&|I{ju_  
-58Sb"f  
void saveCopy (int[] data){ 1qm _Qs&  
  try{ {xu~Dx  
   copy = (int[])data.clone(); IylfMwLC  
  }catch (CloneNotSupportedException e){ #ja6nt8GC  
   // Can’t get here. e !N%   
  } @d_9NOmNT  
} ;MH_pE/m  
ZLlAK?N  
@pN6uDD}R  
yW@YW_2;4  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: @ S)p{T5G  
4|h>.^  
8SOfX^;o  
Wxzh'c#\8  
static int[] cloneArray (int[] data){ v-&@c  
  try{ F@<^  
   return(int[])data.clone(); "sJ@_lp  
  }catch(CloneNotSupportedException e){ CF]#0*MI  
   // Can’t get here. PwC^ ]e  
  } Jix;!("  
} ODCv^4}9  
lS |:4U.  
Z+agS8e(  
0,1)Sg*  
  这样的话,我们的saveCopy看起来就更简洁了: R47tg&k6[  
y\XWg`X y  
g`I$U%a_2  
51'{Jx8  
void saveCopy (int[] data){ ?o`fX wE  
  copy = cloneArray ( data); gr\vC  
} RU+F~K<  
^2LqKo\T  
nVoP:FHH  
xG:7AGZ$[  
  五、常见错误5#:拷贝错误的数据 C] M{  
[[ uZCKi  
7VW/v4n  
IPk"{T3  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: \4Z"s[8}  
'tY y_  
C^ZD Uj`  
Bxk2P<d  
import java.awt.Dimension; ofuQ`g1hb  
/*** Example class. The height and width values should never * be UQO?hZ!y/.  
negative. */ }*,z~y}V#  
public class Example{ 5!qLJmd=  
  static final public int TOTAL_VALUES = 10; CO{AC~  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; kk ZMoK  
  public Example (){ } b|u,[jEB  
v-XB\|f  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ no9=K4h`  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ %h}3}p#4  
   if (height < 0 || width < 0) 'Ooq.jaK;/  
    throw new IllegalArgumentException(); q],/%W  
    if (d[index] == null) # 66vkf*  
     d[index] = new Dimension(); xPMX\aI|l  
     d[index].height = height; <5npVm  
     d[index].width = width; T#ehJq 5  
  } [='<K  
  public synchronized Dimension[] getValues() ~QU\kZ7Z  
   throws CloneNotSupportedException{ LsaRw-4.c  
    return (Dimension[])d.clone(); }0 =gP?.kE  
  } vg\fBHzn  
} oB%j3aAH  
wj9 Hh  
`g'z6~c7n  
5Eu`1f?  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: Z[9f8/6<b  
seA=7c5E  
/OeOL3Y  
:s#&nY  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ YQaL)t$0  
  Dimension[] copy = (Dimension[])d.clone(); %kL]-Z  
  for (int i = 0; i < copy.length; ++i){ \= Wrh3  
   // NOTE: Dimension isn’t cloneable. w C-x'  
   if (d != null) T^H`$;\  
    copy = new Dimension (d.height, d.width); *wV`7\@  
  } Z(hRwIOF  
  return copy; I ka V g L  
} >:P-3#e*  
6B@{X^6y  
Jqqt@5Ni  
8g6G},Y0  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: `.YMbj#T  
-XWlmw*i(g  
ty b-VO  
yOE N*^6  
public void store (int[] data) throws CloneNotSupportedException{ ^vc#)tm5p  
  this.data = (int[])data.clone(); uY:u[  
  // OK J#Agk^Y 5  
} wu19Pg?F  
0ae}!LO  
3/*<i  
s3oQ( wC %  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: Mf`@X[-;  
* NdL4c~  
yYvv!w+@Q  
PZhpp"  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ bf$4Z: Y  
  this.data = (int[][])data.clone(); // Not OK! fe7DS)U  
} q[TW  
public void rightStore (int[][] data){ 9FmX^t$T  
  // OK! qrY]tb^K  
  this.data = (int[][])data.clone(); d5 U+]g  
  for (int i = 0; i < data.length; ++i){ ?o_ D#gG*  
   if (data != null) ,{sCI/  
    this.data = (int[])data.clone(); *+>QKR7  
  } +t p@Tb  
} 7_ao?}g  
hlBqcOpkKg  
~4u[\&Sh  
6q@VkzF  
AHdh]pfH  
   六、常见错误6#:检查new 操作的结果是否为null U[c^xz&  
jmva0K},SE  
99?: 9g  
pR7D3Q:^7  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: d1n*wVl  
<amdPo+2D  
{KM5pK?,BJ  
'L ]k \GO  
Integer i = new Integer (400); H05U{vR  
if (i == null) kUQdi%3yY;  
throw new NullPointerException(); NZt 8L?  
0uS6F8x@  
@ \JoICz  
gBJM|"_A?  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 K)TMr"j\  
8aa`0X/6  
#H&`wMZZ:  
j4!oBSp  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 h|XLL|:  
(-esUOB.  
 七、常见错误7#:用== 替代.equals ]B9Ut&mF;  
[:bYd}J  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: K) {\wV="  
F@jyTIS^  
Oo8"s+G  
d(;Qe}ok>  
int x = 4; DT>Giic  
int y = 5; aDVBi: _  
if (x == y) TZ]o6Bb  
  System.out.println ("Hi"); jt4c*0z  
// This ’if’ test won’t compile. <h mRr  
if (x.equals (y)) KcF#c_f   
  System.out.println ("Hi"); =Vi>?fWpn=  
AJR`ohh  
cj9<!"6  
FdM xw*}  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 )L%[(iI,x  
^HI}bS1+|  
wsyAq'%L  
b%D}mxbS  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 ky |Py  
h-=lZ~W~  
t.= 1<Ed  
9e'9$-z  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 Yb Dz{m  
Zh 3hCxXa  
}pL#C  
a^.5cJ$]  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 f)%8*B  
TaE&8;H#N  
~t.M!vk  
7&{[Y^R]"  
  八、常见错误8#: 混淆原子操作和非原子操作 D+69U[P_A  
8^av&u$  
&/tGT3)  
E>3(ff&  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: A]q"+Z]  
"`aLSw75x  
R[{s\  
iK <vr  
public class Example{ 7S)u7  
  private int value; // More code here... eBxOa  
  public void set (int x){ >Mc,c(CvU  
   // NOTE: No synchronized keyword Pq)C(Z  
   this.value = x; d6;"zW|Ec  
  } >Sua:Uff  
} D}6~2j  
pG(%yIiAi  
Q R<q[@)F  
DSc:>G  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: kPg| o3H  
s'^"s_j  
Y76UhtYH  
NY9\a[[^[8  
public void increment (){ Gtpl5gQH  
  // This is effectively two or three instructions: i\z,)xp  
  // 1) Read current setting of ’value’. xix: = a  
  // 2) Increment that setting. ]Y@B= 5e/  
  // 3) Write the new setting back. n*vzp?+Y  
  ++this.value; l~i&r?,]^  
} % C.I2J`_  
Qfd4")zhG  
13KfI  
uf<nVdC.  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: N)b.$aC  
2#?qey  
l=?G"1  
C AvyS  
public synchronized void increment (){ BA t0YE`-,  
  ++this.value; yPhTCr5pK  
} %`1 p8>n  
tsvh/)V  
Uel^rfE`  
T\Ld)'fNv  
  九、常见错误9#:在catch 块中作清除工作 qKL mL2O  
Y6r<+#V  
U";8zplU  
'#p2v'A  
  一段在catch块中作清除工作的代码如下所示: 7lYiufg  
G>yTv`-  
:Lze8oY(D}  
zxffjz,Fe:  
OutputStream os = null; c-gpO|4>  
try{ POtwT">z  
  os = new OutputStream (); 6o!Y^^/U  
  // Do something with os here. V'jvI  
  os.close(); 5fqQ;r  
}catch (Exception e){ "hi)p9 _cR  
  if (os != null) HE0@`(mCpa  
  os.close(); sp'f>F2]  
} d iGkwKj  
jdWA)N}kDG  
dZ"w2ho  
ROc)LCA  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: z.%K5vrO>  
MmPLJ  
s 8 c#_  
WY 'QhieH  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 F.[E;gOTo  
q"O4}4`  
%;-] HI  
u~y0H  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 fce~a\y0  
"fTW2D74  
AV%t<fDG#  
/$NZj" #  
  3.close()可能会抛出异常。 o+j~~P  
<+\ w.!  
|}Wm,J  
B(TE?[ #  
  上面代码的一个更优版本为: # 2qDn^s  
oYn|>`+6:y  
CV )v6f  
VA^yv1We  
OutputStream os = null; [9U: :  
try{ 0V_dg |.  
  os = new OutputStream (); }2nmfm!  
  // Do something with os here. mOQN$d[  
}finally{ e[)oT  
  if (os != null) yRF %SWO  
   os.close(); ;:5Ahfo \  
} :t8?!9g  
zm7IkYF  
zF-R$_]av  
Y)oF;ko:  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 ^vA"3Ixb!  
$>csm  
}> pNf  
luj UEHzp  
  十、常见错误10#: 增加不必要的catch 块 7j22KQ|EX^  
|k ]{WCD]  
S(\<@S&  
w#Di  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 P^Tk4_,0  
j{?ogFfi  
vl,Ff9  
3{*nG'@Mal  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 TyG;BF|rwk  
jf WZLb)  
v:7_ZD6kR  
aViZKps`m  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: (SnrY O`#  
kl0|22"Gz  
 a\@k5?  
J+o6*t2|  
try{ x $@Gp  
  // Nifty code here ys~oJb~  
}catch(Exception e){  ZFH;  
  throw e; :*6#(MX  
}finally{ ,u&K(Z%  
  // Cleanup code here |Y")$pjz  
} "gCqb;^  
6PyODW;R/5  
P1>?crw  
&4R -5i2a  
  不必要的catch块被删除后,上面的代码就缩短为: ]QJWqY  
(G<fvl!~  
1@"os[ 9  
alV{| Vf[6  
try{ Wn kIi,<  
  // Nifty code here \]y /EOT  
}finally{ =)Z~ w`  
  // Cleanup code here $[1J[eY*  
} s-"oT=  
(l ]_0-Z  
zS<idy F`  
f(Uo?_as  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 Rxfhk,I  
.FWi$B';  
Q GoBugU  
%%h0 H[5*  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 YM<F7tp4  
J7Y lmi  
'i5,2vT0  
La 9:qpj  
  小结 W0qn$H  
?Zv>4+Y'  
["7]EW\!:  
>)6d~  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 id:6O+\  
iR39lOr  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

您目前还是游客,请 登录注册
欢迎提供真实交流,考虑发帖者的感受
认证码:
验证问题:
3+5=?,请输入中文答案:八 正确答案:八