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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 Z&~k]R0y  
9b{g+lMZo  
"2y7&#l   
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 }e&KO?x+  
rXHHD#\oF  
X+(aQ >y  
S&4w`hdD>~  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 Sa?~t3*H  
rwi2kk#@P  
>#${.+y  
9*G L@_c  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 sg!=Q+  
&(z8GYBr  
x9XGCr  
hq|j C  
  一、常见错误1# :多次拷贝字符串 j8D$/  
u;l6sdo  
Og&0Z)%  
SdEb[  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 F1Zk9%L%9$  
\K4CbZ,.  
1s^$oi}  
kVB}r.NHP  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: L!G9O]WB  
^>P@5gcoE(  
-r6(=A  
Ep v3/ `I  
String s = new String ("Text here"); %k1q4qOG]^  
oKMg7 3*  
?kT~)k  
IdQwLt  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: e+]YCp[(  
ey9fbS ^I  
`9G$p|6  
{ra Esb-X  
String temp = "Text here"; [nhLhl4S  
String s = new String (temp); O*+w_fox  
z9uEOX&2\  
Eo25ir%  
eAenkUBz6,  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: e\|E; l  
-Z\UYt  
FZ}C;yUPD  
w oY)G7%  
String s = "Text here"; .{Eg(1At  
}E)8soQR  
x""Mxn]gD  
|)>GeE  
  二、常见错误2#: 没有克隆(clone)返回的对象 ><Mbea=U+  
a#^4xy:  
`OF ;>u*:  
K{[Fa,]'  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: >Y*iy  
SqT"/e]b'  
@Tj  6!v  
*{4{<O<4  
import java.awt.Dimension; sN[@mAoH  
/***Example class.The x and y values should never*be negative.*/ >P]I&S-.  
public class Example{ H$($l<G9C  
  private Dimension d = new Dimension (0, 0); < 8W:ij.`  
  public Example (){ } A%sxMA!K,  
,2:L{8_L  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ y(p:)Iv  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ "b+3 &i|  
   if (height < 0 || width < 0) 9iN!hy[  
    throw new IllegalArgumentException(); jAQ)3ON<  
    d.height = height; 0I AaPz/e  
     d.width = width; (WU~e!}  
  } p%M(G#gOgP  
enfu%"(K)  
  public synchronized Dimension getValues(){ N?u2,h-  
   // Ooops! Breaks encapsulation 6I6ZVSxb  
   return d; }M"'K2_Z  
  } 0"D?.E"$r  
} S+\Mt+o  
YJtOdgG|q  
B )3SiU  
?;r7j V/`j  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: -QHzf&D?  
B'#gs'fl  
d'eM(4R@  
,:Y=,[n  
Example ex = new Example(); =S?-=jPtg  
Dimension d = ex.getValues(); u BW  
d.height = -5; !z&seG]@  
d.width = -10; \2VZkVO9  
?2bE=|  
]a@v)aa-  
mm9S#Ya  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 cB{;Nh6"  
o@V/37!  
B2+_F"<;  
l#(g&x6J  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 ~'YSVx& )  
I]S(tx!  
looPO:bo^  
U=*q;$L#  
  更好的方式是让getValues()返回拷贝: qm&53  
$EHn ;~w T  
]r{ #268  
l9Cy30O6  
public synchronized Dimension getValues(){ &wWGZ~T  
return new Dimension (d.x, d.y); {&AT}7  
} xN~<<PIZ  
b|pNc'u:Cn  
'1T v1  
@X / =.  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 :$@zX]?M  
'2B0D|r"a  
Y(;[L`"  
uZ{xt6 f  
  三、常见错误3#:不必要的克隆 5D@Q1   
Q?'W >^*J  
ri.|EmH2:D  
KHC(MdZ  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: } jy7,+  
Bf}0'MK8zQ  
r -DD*'R  
3n"&$q6  
/*** Example class.The value should never * be negative.*/ j1C0LP8  
public class Example{ !7Q.w/|=  
  private Integer i = new Integer (0); 9bYHb'70  
  public Example (){ } Boz_*l|  
6(;[ov1  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ p<.!::*%(  
  public synchronized void setValues (int x) throws IllegalArgumentException{ !ilDR<  
   if (x < 0) kys-~&@+  
    throw new IllegalArgumentException(); o0v m?CL#  
    i = new Integer (x); iO#xIl<  
  } a\.?{/  
="*C&wB^  
  public synchronized Integer getValue(){ \fGYJ37  
   // We can’t clone Integers so we makea copy this way. 9#ay(g  
   return new Integer (i.intValue()); >L3p qK   
  } S6Xw+W02  
} 6I'V XdeN  
uqH! eN5  
. *+7xL  
bJu,R-f  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 FP cvkXQD  
hYQ%|CBXBR  
J!qEj{  
@o.i2iG  
  方法getValue()应该被写为: .St h  
%JU23c*  
a*@Z^5f  
|[t=.dK%  
public synchronized Integer getValue(){ 8&AorYw[  
// ’i’ is immutable, so it is safe to return it instead of a copy. Z\yLzy#8  
return i; D.JVEKLkU  
} x~I1(l7r  
_34YH5  
#k]0[;1os  
W8/6  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: Nr7.BDA  
'Z%aBCM  
= ft$j  
[<yUq zm  
  ?Boolean Y\2>y"8>$x  
   ?Byte =<tEc+!T3  
   ?Character MZ[g|o!)v  
   ?Class /60=N `i  
   ?Double >~r@*gml  
   ?Float !,WRXE&j  
   ?Integer n_ gB#L$  
   ?Long t$Ji{t-  
   ?Short Z%d4V<fn  
   ?String RB@gSHOc?  
   ?大部分的Exception的子类 @k;3$  
^Rh~+  
{:+^[rer j  
DO*C]   
 四、常见错误4# :自编代码来拷贝数组 0([jD25J!  
9Ei#t FMc  
un%"s:  
7E t(p'  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: ?n~j2-[<  
6@36 1f[  
u01^ABn  
jYx(  
public class Example{ /R?uxhV  
  private int[] copy; :H k4i%hGk  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ =?x=CEW  
  public void saveCopy (int[] data){ \M^4DdAy  
   copy = new int[data.length]; Q |r1.  
   for (int i = 0; i < copy.length; ++i) TuR?r`P%  
    copy = data; FC .-u"V  
  } OF}_RGKg3  
} TW? MS em  
4IpFT;`q  
,)m-nZ5  
oMf h|B  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: l$@lk?dc  
1a4$. {  
!0_Y@>2  
k!rz8S"  
void saveCopy (int[] data){ JB}h }nb  
  try{ k}7)pJNj  
   copy = (int[])data.clone(); 'v5gg2  
  }catch (CloneNotSupportedException e){ AE~}^(G`  
   // Can’t get here. <T9m.:l  
  } e6a8ad  
} 7]53GGNO  
|bUmkw  
z<XS"4l?W  
NsK>UJ'  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: At:C4>HE@  
x=+H@YO\  
1z!Lk*C)  
@1X1E 2:  
static int[] cloneArray (int[] data){ ~)(Dm+vZ  
  try{ 9a,CiH%@  
   return(int[])data.clone(); VUhu"h@w%  
  }catch(CloneNotSupportedException e){ 2sq<"TlQXI  
   // Can’t get here. w%na n=  
  } cE?J]5#^  
} Tl-B[CT  
cVi CWc2  
z81!F'x;  
3"RZiOyv  
  这样的话,我们的saveCopy看起来就更简洁了: oZw#Nd   
U{m:{'np(H  
QJ'C?hn  
-hfY:W`Dz  
void saveCopy (int[] data){ u{^Kyo#v  
  copy = cloneArray ( data); o^J&c_U\3'  
} bBL"F!.  
H%V[% T4=  
eZo%q,L  
7?@v}%w  
  五、常见错误5#:拷贝错误的数据 )HcC\[  
CdjGYS  
M3;B]iRQD  
OW^7aw(N6  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: Ac%K+Pgk.  
vN+!l3O  
$'wl{D"  
X[}%iEWzT  
import java.awt.Dimension; ponvi42u  
/*** Example class. The height and width values should never * be "Y6mM_flq  
negative. */ p5ihuV,   
public class Example{ 4G2V{(@QiZ  
  static final public int TOTAL_VALUES = 10; cgAcAcmY  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; U^I'X7`r  
  public Example (){ } 9wzYDKN}  
X8TZePh  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ Mf"B!WU>]B  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ G@2M&0'  
   if (height < 0 || width < 0)  (w fZ!  
    throw new IllegalArgumentException(); 9IMcp~zX  
    if (d[index] == null) e)8iPu ..  
     d[index] = new Dimension(); )k Uw,F=6  
     d[index].height = height; =lnz5H  
     d[index].width = width; Ek6W:Q:@  
  } 8 B5%IgA  
  public synchronized Dimension[] getValues() c+c^F/  
   throws CloneNotSupportedException{ Uyh#g^r  
    return (Dimension[])d.clone(); fAi113q!  
  } d29HEu  
} A |B](MW%O  
1d6pQ9 N  
|ouk;r24V  
,\ i q'}i  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: f]ef 1#  
E'}$'n?:  
cE[lB08  
=W=%!A\g  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ #</yX5!V  
  Dimension[] copy = (Dimension[])d.clone(); r`@Dgo}  
  for (int i = 0; i < copy.length; ++i){ qZ.\GHS  
   // NOTE: Dimension isn’t cloneable. ]n_A~Y r  
   if (d != null) ObUQB+  
    copy = new Dimension (d.height, d.width); Fu$otMw%l  
  } [iD!!{6+  
  return copy; xN]bRr  
} }Z|a?J@CZm  
l;ugrAo?  
p#w,+)1!d  
w~`P\i@  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: %9K@`v-  
D {mu2'q  
TBzM~y  
3 V<8  
public void store (int[] data) throws CloneNotSupportedException{ Ic*Q(X  
  this.data = (int[])data.clone(); P-C_sj A7  
  // OK sQkP@Y  
} q)/4i9  
C^a~)r.h  
Kt-@a%O0  
Ok@5`?08  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: 8`D_"3j3g\  
_Cxs"to  
Gs%kqD{=  
bFpwq#PDW>  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ A U](pXK;  
  this.data = (int[][])data.clone(); // Not OK! \,WPFV  
} t?s1@}G^  
public void rightStore (int[][] data){ ^l9S5 {  
  // OK! nm)F tX|A  
  this.data = (int[][])data.clone(); T M+7>a$  
  for (int i = 0; i < data.length; ++i){ xn-n{U"  
   if (data != null) }\@*A1*X2  
    this.data = (int[])data.clone(); ~HELMS~-  
  } Vrnx# j-U  
} [W2k#-%G  
#q\C"N5ip  
uwbj`lpf  
)3;S;b  
U@9n 7F  
   六、常见错误6#:检查new 操作的结果是否为null &0 @2JS/!  
$ZA71TzMV  
X0m\   
j,~h:MT  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: [P^ .=F  
P63f0 F-G  
noacnQ_I$  
!Ed';yfz\(  
Integer i = new Integer (400); >Zr`9$i  
if (i == null) Q@2Smtu~c  
throw new NullPointerException(); ~($h9* \  
o/Z?/alt4  
WKN\* N<  
,ujoGSx}  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 HH>]"mv  
GSypdEBj+w  
/Qbt  
xC|7"N^/  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 :}Z+K*%o-  
d8av`m  
 七、常见错误7#:用== 替代.equals E}~ GXG  
vrH/Z.WD  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: GR/ p%Y(  
NJ3b Oq  
v-OaH81&R  
qPWYY  
int x = 4; ucyxvhH^-  
int y = 5; n:HF&j4C,  
if (x == y) GQq2;%RrF  
  System.out.println ("Hi"); hMdsR,Iq  
// This ’if’ test won’t compile. J3C"W7 94}  
if (x.equals (y)) KN5.2pp  
  System.out.println ("Hi"); WG9x_X&XJ  
wF}/7b54  
\T>f+0=4  
 dY|(  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 !x[ +rf  
wuI+$?  
uS3J^=>@(a  
H~fZA)W 4Y  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 aabnlOVw  
T~_+\w  
8MV=?  
t-e:f0iz  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 4?;1cXXA  
BoXQBcG]w  
I@e{>}  
5yuR[ VU  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 njX!Ez  
6*Rz}RQ  
Jv a&"}Cb  
Busxg?=  
  八、常见错误8#: 混淆原子操作和非原子操作 &*r YY\I  
&?v^xAr?B  
+!CG'qyN>  
:(N3s9:vz  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: x%5n&B  
aOETmsw  
X^7bOFWE  
U8;k6WT|  
public class Example{ C([TolZ  
  private int value; // More code here... >^{}Hjt  
  public void set (int x){ PohG y  
   // NOTE: No synchronized keyword ?=$a6o  
   this.value = x; ,_D`0B6o  
  } %TP0i#J  
} 8N'[ )Jw  
5F18/:\n  
YOqGFi~`  
[g`P(?  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: MZv In ZS  
4,`Yx s)%  
vm_+U*%c  
.IE2d%]?  
public void increment (){ `,3;#.[D  
  // This is effectively two or three instructions: H_un3x1  
  // 1) Read current setting of ’value’. qn5e[Vn  
  // 2) Increment that setting. KQ9~\No]  
  // 3) Write the new setting back. W c{<DE?J  
  ++this.value; )k&<D*5s  
} \GO^2&g(  
S=*rWh8)%<  
7LbBS:@3z_  
hQv~C4Wfrf  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: 79^Y^.D  
Usx8  U  
N`h,2!(j  
:?S1#d_  
public synchronized void increment (){ IQAV`~_G  
  ++this.value; ;`p+Vs8C  
} 5B< em  
4"nb>tA  
p Wa'Fd  
Z%E;*R2+:>  
  九、常见错误9#:在catch 块中作清除工作 4V@raI-  
$WED]X@X!  
i 3?=up!  
N =FX3Z  
  一段在catch块中作清除工作的代码如下所示: <b.?G  
JK) )Cuh  
;'~U5Po8  
UzTFT:\  
OutputStream os = null; 0K<y }  
try{ mnh>gl!l  
  os = new OutputStream (); ;x^WPY Ej  
  // Do something with os here. N_Q)AXr)  
  os.close(); P:,'   
}catch (Exception e){  >\6Tm  
  if (os != null) P/6$ T2k_  
  os.close(); j")#"& m  
} I]+xerVd  
Wn6~x2LaV  
'#LbIv4  
R/Y9t8kk  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: n;+CV~  
R9@Dd  
.0+=#G>  
:Aj8u\3!@  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 GrPKJ~{6  
 ieo Naq  
{Rc mjI7  
o b;]  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 xVw9_il2a  
5#|D1A  
[CxnGeKK  
Mm7;'Zbg  
  3.close()可能会抛出异常。 q#s:2#=  
q$RJ3{Sf  
6Y9FU  
,\8F27  
  上面代码的一个更优版本为: gCfAy=-,V  
m.!n|_}]  
Qf|x]x*5  
!8YZ;l  
OutputStream os = null; k@:M#?(F  
try{ .\)`Xj[?  
  os = new OutputStream (); Ya~*e;CW2  
  // Do something with os here. F/O5Z?C?  
}finally{ &BTgISYi  
  if (os != null) i82sMN1jl7  
   os.close(); 9BR/zQ2  
} }9=X*'BO  
-7-r~zmr  
<5@VFRjc  
8G3CQ]G  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 W;L<zFFbU)  
^EtBo7^t  
8[v9|r  
|y%pJdPk=  
  十、常见错误10#: 增加不必要的catch 块 W3Gg<!*Uo  
>I*)0tE  
={g.Fn(_  
t"# .I?S0  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 <9f;\+zA  
[Ey[A|g  
a9LK}xc={  
O2;iY_P7lV  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 skaPC#u  
k|uW~ I)  
80m<OW1  
;[nomxu|?  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: D@W[Nd5MJ  
M$J{clr  
+>bm~6  
Y["aw&;#O\  
try{ 0c}pg:XT  
  // Nifty code here g}@W9'!  
}catch(Exception e){ TwfQq`  
  throw e; ^lvYj E  
}finally{ bqPaXH n  
  // Cleanup code here lKVV*RR}  
} G.{)#cR  
!m {d6C[  
1Jm'9iy3  
E^s<5BC;  
  不必要的catch块被删除后,上面的代码就缩短为: o,NTI h  
ccR#<Pb6q  
kz!CxI (  
9Gh:s6  
try{ +4 W6{`  
  // Nifty code here 3bsuE^,.@  
}finally{ u B~C8}  
  // Cleanup code here 6Dl]d %.  
} EN2H[i+,  
|(eRv?Qy@  
simD<&p  
!&(^R<-id  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 !#[B#DZc(  
rd_!'pG  
]nIH0k3y  
;9&#Sb/  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 ;6)Onwx  
2#jBh   
y/vGt_^;3<  
xcHuH -}  
  小结 3a Y^6&  
L$zB^lSM  
w|,BTM:e  
cM?i _m  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 F=g +R~F  
n9H4~[JiC  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

您目前还是游客,请 登录注册
如果您提交过一次失败了,可以用”恢复数据”来恢复帖子内容
认证码:
验证问题:
3+5=?,请输入中文答案:八 正确答案:八