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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 1X:whS5S  
0{Ll4  
0Ukl#6  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 (j8,n<o  
Q8/0Cb/  
o4 OEA)k)=  
s$y#Ufz  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 Oj|p`Dzh  
9p'J(`  
36Y[7 m=  
+Q#Qu0_   
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 *gpD4c7A\  
.nY6[2am  
ob5nk ^y  
7;-i_&vws  
  一、常见错误1# :多次拷贝字符串 %_=R&m'n`  
1kw4'#J8  
JY8"TQ$x  
>\x 39B  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 HnY"6gTNK  
G+^$JN=  
pP oC61F  
iBW6<2@oZF  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: e(% Solkm?  
Nn7@+g)  
s ^@Cq=  
uiEA=*axp  
String s = new String ("Text here"); M{RZ-)IC  
*pUV-^uo  
4'b]2Mn3   
VIdoT2  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: ~"0X,APR5  
tBUQf*B  
 Hu2g (!  
?bDae%>.d,  
String temp = "Text here"; la7QN QW  
String s = new String (temp); ]lYEJ`  
t? J a q  
oT{yttSNo  
''07Km@x  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: -{SiK  
B;je|M!d  
X_@@v|UF  
zm"g,\.d  
String s = "Text here"; <]qd9mj5  
tX}S[jdq  
DA@hf  
/ {~h?P}  
  二、常见错误2#: 没有克隆(clone)返回的对象 lc#zS_  
 P;/wb /  
%-|q3 ^s  
DN0b.*[`3  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: Sylsp%A  
0J</`/gH  
B;_3IHMO  
$zi\ /Yw  
import java.awt.Dimension; SnU{ZGR>sP  
/***Example class.The x and y values should never*be negative.*/ A6.'1OD  
public class Example{ vBnHG-5;P  
  private Dimension d = new Dimension (0, 0); 6u;(R0n  
  public Example (){ } Wy )g449  
<ft9B05*  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ dF]8>jBOL  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ H2cc).8"  
   if (height < 0 || width < 0) X6 cb#s0|  
    throw new IllegalArgumentException(); dq(L1y870  
    d.height = height; e1Hx"7ew_  
     d.width = width; K a|\gl;V  
  } E=trJge  
EBLoRW=8ld  
  public synchronized Dimension getValues(){ k@U`?7X  
   // Ooops! Breaks encapsulation h!GixN?  
   return d; ~C x2Q4E  
  } k<qH<<r*  
} ^u,x~nPXg  
5 Vqvb|  
Hp AZ{P7  
*X=-^\G  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: W7"sWaOhW  
!{;RtUPz*  
e[!>ezaIY  
eO G%6C%a  
Example ex = new Example(); )>p6h]]a  
Dimension d = ex.getValues(); >FNt*tX<0  
d.height = -5; }iAi`_\0;  
d.width = -10; ~T9[\nU\  
it vdzPO  
a| cD{d  
rd{( E  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 SbivW5|61  
X_l,fu^C#$  
)v0vdAh'b  
(5_(s`q.  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 hBu =40K  
t57b)5{FM  
mo$*KNW%\  
k>`X! "  
  更好的方式是让getValues()返回拷贝: &pz8vWCk  
yqwr0yDAl  
v g]&T  
p6)UR~9Rs  
public synchronized Dimension getValues(){ p<e~x/@m*  
return new Dimension (d.x, d.y); A[bxxQSP\H  
} L+L9)8FJ  
06$9Uz9  
P0=F9`3wb  
h@d m:=ul  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 = xk@Q7$  
5WYU&8+]{:  
DM95Il[/  
uX[ "w|  
  三、常见错误3#:不必要的克隆 Ex3woT-  
+n dyR  
r N7"%dx  
 HV(Kz  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: Jt8 v=<@  
!A o?bs'  
lOui{QU  
$Vzfhj-if  
/*** Example class.The value should never * be negative.*/ Y')+/<Q2E  
public class Example{ b'YbHUyu  
  private Integer i = new Integer (0); M&dtXG8<^  
  public Example (){ } *gn*S3Is[j  
W% ud nJ  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ _?ZT[t<  
  public synchronized void setValues (int x) throws IllegalArgumentException{ Jk*MxlA.b  
   if (x < 0) -E7\ .K3  
    throw new IllegalArgumentException(); T2{+fR v N  
    i = new Integer (x); KX`,7-  
  } e j9G[  
|.A>0-']M  
  public synchronized Integer getValue(){ ?H&p zY~H  
   // We can’t clone Integers so we makea copy this way. `O/)q^m1L  
   return new Integer (i.intValue()); L/I-(08!Y:  
  } 0bE_iu>f'  
} _f`m/l  
nq=fSK(  
>. Y ~F(  
)[1m$>  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 Gf?KpU  
[>;O'>  
"^H+A-R[  
D }\`5L<  
  方法getValue()应该被写为: $c-3Q|C  
H &JKja}`  
J*k4&l  
o2~x'*A0I  
public synchronized Integer getValue(){ ,#G@ri:B  
// ’i’ is immutable, so it is safe to return it instead of a copy. ]^y}}y  
return i; m_B5M0},  
} P*SXfb"HC  
;IT^SHym  
>BX_Bou  
JI&>w-~D  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: oZ}e w!V  
"hfwj`U  
\&H%k   
0`W~2ai  
  ?Boolean OjN]mp-q  
   ?Byte [>-k(D5D  
   ?Character sCUPa-cHF  
   ?Class XP7A.I#q0  
   ?Double F!xK#~e   
   ?Float ld $`5!Z  
   ?Integer >HFJm&lQ  
   ?Long Y%faf.$/9  
   ?Short sqw _c{9  
   ?String "a: ;  
   ?大部分的Exception的子类 `G'V9Xs(  
Q-"FmD-Yw  
(:\hor%  
:.dQY=6I  
 四、常见错误4# :自编代码来拷贝数组 ~K[rQ  
*=v RX!sI,  
5?b9[o+ D  
L4NC -  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: a-3~HH  
g5 E]o)  
U|zW_dj  
qSQjAo4t@  
public class Example{ iJYr?3nw;  
  private int[] copy; F JzjS;  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ -l\@50, D  
  public void saveCopy (int[] data){ zm e:U![  
   copy = new int[data.length]; 0h7\zoZ5  
   for (int i = 0; i < copy.length; ++i) 1)r1/0  
    copy = data; ,y0kzwPR1  
  } ;#;X@BhS  
} gQ?k}D  
+o/q@&v;Ax  
$d"6y  
6+It>mnR  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: ~DJ/sY2/  
;'h7 j*6  
r=9*2X#  
)S%mKdOm $  
void saveCopy (int[] data){ t`LH\]6@  
  try{ xWDwg@ P  
   copy = (int[])data.clone(); ?*T`a oB  
  }catch (CloneNotSupportedException e){ +z4NxR   
   // Can’t get here. G67BQG\av  
  } iz'8P-]K>  
} {*|yU"  
&L/ C:<.  
:>,d$f^tqE  
M6e"4Gh  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: H1l' \  
os2yiF",   
u%|VmM>  
X)yTx8v4  
static int[] cloneArray (int[] data){ lu>>~vy6  
  try{ nhIITfJJ  
   return(int[])data.clone(); J@Li*Ypo  
  }catch(CloneNotSupportedException e){ F/!C=nS  
   // Can’t get here. v7ae^iU  
  } #&@&BlIe  
} 5'o.v^l  
OxD\e5r  
nK:39D$(  
2Two|E  
  这样的话,我们的saveCopy看起来就更简洁了: %(NRH?  
6@T_1  
Y`M.hYBXk  
#>233<  
void saveCopy (int[] data){ N+r~\[N\9  
  copy = cloneArray ( data); 9oaq%Sf  
} 5B51^"  
bDr'W   
r2Q"NVw  
o$Jk2 7  
  五、常见错误5#:拷贝错误的数据 lFB Ka ,6  
wo*/{KFvh  
[0 W^|=#K  
_z}d yp"I  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: ]AN)M>  
I\[*vgjm3G  
9_HEImk  
[*1c.&%(  
import java.awt.Dimension; |5O%@  
/*** Example class. The height and width values should never * be QJv,@@mu  
negative. */ k_!z=6?[:  
public class Example{ x.d9mjLN8m  
  static final public int TOTAL_VALUES = 10; ncWASw`  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; [%b<%m}L-  
  public Example (){ }  6E  
)d s(/P5b  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ =P5SFMPN  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ T *$uc,  
   if (height < 0 || width < 0) %D&FnTa  
    throw new IllegalArgumentException(); D,X$66T ^  
    if (d[index] == null) :\.v\.wm  
     d[index] = new Dimension(); s_RYYaM  
     d[index].height = height; $+?6U  
     d[index].width = width; 0|HhA,u  
  } D]4?UL  
  public synchronized Dimension[] getValues() 6R?J.&|  
   throws CloneNotSupportedException{ zis-}K<   
    return (Dimension[])d.clone(); #!<x|N?_<  
  } iA%3cpIc(Z  
} q6osRK*20  
xvgIYc{  
Ue8_Q8q5  
;  I=z  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: E fqa*,k  
c>]_,Br~  
Tvk=NJ  
X-t4irZ)  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ #BM *40tch  
  Dimension[] copy = (Dimension[])d.clone(); Qi[T!1  
  for (int i = 0; i < copy.length; ++i){ 'dBzv>ngD  
   // NOTE: Dimension isn’t cloneable. C@KYg/nYw  
   if (d != null) |Q7Ch]G  
    copy = new Dimension (d.height, d.width); V,2O `D%  
  } u_mm*o~)g  
  return copy; YXBS!89m  
} \Ud2]^D=  
F.O2;M|x  
Va9vDb6  
E{j6OX\  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: /AWHG._  
2y,~i;;_  
89WuxCFS  
jkfI,T  
public void store (int[] data) throws CloneNotSupportedException{ 2wu 5`Z[E  
  this.data = (int[])data.clone(); m@jOIt!<  
  // OK +L_.XToq-  
} H4%wq  
0{Tf;a<  
CMTy(Z8_)  
FmnA+fA  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: S>**hM U%  
HI:E&20y  
b"x:IDW qG  
ujwI4oj"c  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ "ebn0<cZ  
  this.data = (int[][])data.clone(); // Not OK! F.AO  
} B[y1RI|9  
public void rightStore (int[][] data){ K5k,47"  
  // OK! ukri7 n*  
  this.data = (int[][])data.clone(); @89mj{  
  for (int i = 0; i < data.length; ++i){ &\1Dy}:  
   if (data != null) ay4|N!ExO  
    this.data = (int[])data.clone(); } 1c5#Ym  
  } C?b Mj[$  
} !(+?\+U lE  
e _,_:|t  
p|W:;(  
;M}bQ88  
}LE.kd&  
   六、常见错误6#:检查new 操作的结果是否为null J| 3CG;+  
u,&Z5S  
:<nL9y jt  
Av/|={i  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: 5*ABw6'6  
bZa?h.IF  
vR:t4EJ`  
t6LTGWs/_o  
Integer i = new Integer (400); >R.~'A/$F  
if (i == null) <$ '#@jW  
throw new NullPointerException(); \7W {/v4^  
na%9E8;:&v  
Rk`c'WP0*  
@ /c{gD  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 `SOaQ|H  
p61"a,Xc  
5%+T~ E*  
YMz[je  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 b$g.">:$  
_Z9I')  
 七、常见错误7#:用== 替代.equals 8f#YUK sW=  
EMJ}tvL0Tp  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: 1=#`&f5f&  
#bf^Pq'8  
=(v/pLLK?  
-Xx,"[sN\w  
int x = 4; o'R_kadN[T  
int y = 5; K@ W~  
if (x == y) IgSe%B  
  System.out.println ("Hi"); .8g&V|  
// This ’if’ test won’t compile. R:OoQ^c  
if (x.equals (y)) 6eQrupa  
  System.out.println ("Hi"); T*'5-WV|3t  
=g?r.;OO  
Hs2L$TX  
XbG=H-|  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 l$PO!JRD  
|RHX2sso  
2R.YHj  
4|x5-m+T  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 >iaZGXje  
hLO nX<%a  
]_5C5m  
jj.)$|&#`  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 |1rBK.8  
{:fyz#>>^  
@bS>XWI>  
gl00$}C  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 ZH/|L?Q1U  
XBi@\i=  
A9F&XF7{  
A qE,zW  
  八、常见错误8#: 混淆原子操作和非原子操作 +U@P+;  
i Ri1E;  
m;8_A|$A  
cLJ|VD7  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: {}vW=  
iZ)7%R?5  
+ ^4"  
<XGOcekG  
public class Example{ L"#Tas\5  
  private int value; // More code here... *$uKg zv3  
  public void set (int x){ RrGS$<  
   // NOTE: No synchronized keyword W9.Z hpM  
   this.value = x; | u36-  
  } qo4AQ}0 <  
} , lT8gQ|u  
x4Eq5"F7}  
w QwY_ _  
CuU"s)  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: (\#j3Y)r  
h jW RU#  
'}LH,H:%G  
TY~0UU$  
public void increment (){ sK}Ru?a)  
  // This is effectively two or three instructions: Xs*~ [k'  
  // 1) Read current setting of ’value’. Z A7u66  
  // 2) Increment that setting. Po93&qE  
  // 3) Write the new setting back. G74<sD  
  ++this.value; Nf1) 5  
} 3p$ZHH.UP  
> aN@)=h}  
wf<uG|90  
<&b ~(f  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: B<LQ;n+  
.|x0du|  
%|r@q  
D)4p8-=t  
public synchronized void increment (){ ]!0 BMZmf  
  ++this.value; v;jrAND  
} u&r @@p.  
)QFT$rmX  
;k(|ynXv  
~d){7OG  
  九、常见错误9#:在catch 块中作清除工作 (f $Y0;v>}  
L.ndLd  
Br1JZHgA  
F_\\n#bv  
  一段在catch块中作清除工作的代码如下所示: tgc&DT; E  
7s>d/F3*  
sW|u}8`  
)<IbQH|_  
OutputStream os = null; kZSe#'R's  
try{ a5]~%xdK  
  os = new OutputStream (); B~yD4^  
  // Do something with os here. .J&~u0g  
  os.close(); ",Ek| z  
}catch (Exception e){  //K]zu  
  if (os != null) !Z<Z"R/  
  os.close(); w[:5uo(  
} ra$_#HY  
u\s mQhQGE  
69O?sIk  
2zArAch  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: o NJ/AT  
{RwwSqJ  
S#2 'Jw  
B>YrDJUN  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 9Ni$nZN  
Ya304Pjd  
DCP "  
(J$JIPF  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 3l5q?"$  
2Xe2 %{  
d=N5cCqq  
_S@s  
  3.close()可能会抛出异常。 dpGaI  
Hagj^8  
?8YHz  
c\]h YKA  
  上面代码的一个更优版本为: 89+m?H]K  
9FH=Jp  
93[`1_q7\  
LOR$d^l  
OutputStream os = null; /DZKz"N  
try{ kf&id/|  
  os = new OutputStream (); ;)c SdA9  
  // Do something with os here. ~A>3k2 N/e  
}finally{ $o {f)'.>n  
  if (os != null) WoesE:NiR  
   os.close(); 3U1xKF  
} 2$\Du9+  
m' z<d  
l&;#`\s!V  
PyzW pf  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 vHJ~~if  
9QWS[E4  
k NK)mE  
Tet,mzVuu  
  十、常见错误10#: 增加不必要的catch 块 JF24~Q4P  
L{<E'#@F  
Il*wVNrZI  
E83{4A4  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 ?}B_'NZ%  
]5uCs[  
6Dw[n   
~;Xdz/  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 $;$_N43  
FkKx~I:  
]U'KYrh  
A3M)yWq  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: oy/#,R_n%  
z4_>6sf{  
DFqXZfjm  
AGPZd9  
try{ H }</a%y  
  // Nifty code here YuLW]Q?v  
}catch(Exception e){ Eh8.S)E  
  throw e; j YO #  
}finally{ v3.JG]zLpP  
  // Cleanup code here eUx|_*`  
} ?ry`+nx  
#L BZ%%v  
/v=MGX@r  
;Fp"]z!Qh+  
  不必要的catch块被删除后,上面的代码就缩短为: 8 :Z3Q  
 A5Y z|  
GP`_R  
,58D=EgFy  
try{ ;`s/|v  
  // Nifty code here 95?$O~I  
}finally{ gbQrSJs!Zh  
  // Cleanup code here ix*n<lCoC  
} dM#\h*:=  
o!\Vk~Vi&  
~ hYG%  
0j_`7<,:  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 a|lcOU  
wGLZzqgq  
AdR}{:ia  
o}Dy\UfU  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 RzFv``g  
~qco -b  
Ol D]*=.cO  
J?u@' "u  
  小结 I:bi8D6  
vezX/xD?  
^5j9WV  
|c dQJW  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 $WrDZU 2z  
h]vA%VuE'E  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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