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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 XBfiaj  
E3CiZ4=5  
AmT| %j&3  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 Hj5WJ{p.  
4 |:Q1  
E1Ru)k{B  
uPv;y!Lsa@  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 >wg9YZ~8  
<|KKv5[  
eZN3H"H  
<?q&PCAn^  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 G1#Bb5q:  
]YisZE4s  
z:ru68  
<B ]i80.  
  一、常见错误1# :多次拷贝字符串 Dyouk+08x  
q G :jnl  
Z]7;u>2  
Q.pEUDq/  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 b*'=W"%\  
!LHzY(  
0@sr NuW  
V7B=+(xK  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: H0D>A<Ue  
9Sx<tj_4P{  
WTV3p,;6a  
:|n>H+Y  
String s = new String ("Text here"); X%4uShM  
*O(/UVuD\  
| Q1ub S  
ecY ^C3+S  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: |"Xi%CQ2  
E]u'MX  
.WL\:{G8;  
 =BqaGXr  
String temp = "Text here"; 0_,3/EWa  
String s = new String (temp); S1H47<)UF  
zulf%aaL  
a O"nD_7  
YmO"EWb  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: 7U{b+=,wK  
{0A[v}X ~  
hVT=j ?~  
#czyr@  
String s = "Text here"; -~<q,p"e  
5,0 wj0l  
Ry8WNVO}R  
d}wa[WRv   
  二、常见错误2#: 没有克隆(clone)返回的对象 ~q8V<@?  
Zv1Bju*y  
8aZey_Hw;+  
sO{0hZkc  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: ~*' 8=D?)  
l $p_])x  
(Qx-KRH  
h87L8qh9  
import java.awt.Dimension; h-2E9Z  
/***Example class.The x and y values should never*be negative.*/ OU)p)Y_z  
public class Example{ L6rs9su=7  
  private Dimension d = new Dimension (0, 0); {x&jh|f`g  
  public Example (){ } 282+1X  
+QXYU8bYZ  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ uwH)/BW)[  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ EMW4<na[  
   if (height < 0 || width < 0) (AM,4)lW,  
    throw new IllegalArgumentException(); .kB3jfw0,  
    d.height = height; +9Hk+.  
     d.width = width; vHI"C %  
  } Top#u  
9s\i(/RxW  
  public synchronized Dimension getValues(){ XC$+ `?  
   // Ooops! Breaks encapsulation Y&05 *b"  
   return d; ](9{}DHV  
  } (1elF)  
} XftJ=  *  
i"sYf9,  
W3o }.|]  
S,"ChR  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: OO !S w  
?) ,xZ1"  
n6%jhv9H  
;8;~C "  
Example ex = new Example(); LKqog%,c  
Dimension d = ex.getValues(); 'a-5 U TT  
d.height = -5; *nsnX/e(-  
d.width = -10; ,8J*S  
LKf5r,C  
!aW*dD61  
:`>+f.)  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 Z z; <P  
{Jw<<<G  
W &0@&U  
XJxs4a1[t  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 zFdz]z3  
:WfB!4%!  
B 1d%#  
!(ux.T0  
  更好的方式是让getValues()返回拷贝: >D p6@%  
e\ l,gQP  
S)'q:`tZo  
YAC zznN  
public synchronized Dimension getValues(){ )(ZPSg$/F  
return new Dimension (d.x, d.y); o wpJ7S1~  
} #`vGg9  
#Rm=Em}d  
@Pb 1QLiz  
p\]LEP\z,  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 DO-K  
Ji}IV  
L$u&~"z-  
qT<qu(V:  
  三、常见错误3#:不必要的克隆 rCSG@D.  
<R~~yW:H  
*Xt c`XH  
0p>:rU~  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: -{:Lx E  
FvI0 J  
dVmAMQk.g  
{u_k\m[Y  
/*** Example class.The value should never * be negative.*/ 4|Gs(^nU  
public class Example{ |7'yk__m  
  private Integer i = new Integer (0); }PIGj}F/  
  public Example (){ } 9}qfdbI  
9CU6o:'fW  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ )V$!  
  public synchronized void setValues (int x) throws IllegalArgumentException{ }rMpp[  
   if (x < 0) dI0>m:RBz  
    throw new IllegalArgumentException(); hA,rSq  
    i = new Integer (x); pXT$Y8M  
  }  0[!gk]p  
lRATrp#T  
  public synchronized Integer getValue(){ jVFRqT%  
   // We can’t clone Integers so we makea copy this way. HH~  du  
   return new Integer (i.intValue()); @#--dOWYR  
  } Ye=7Y57Nr  
} hzPB~obC  
jQ\ MB  
#<i> <EG  
.McoW7|Y  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 Lc:SqF  
hIO4%RQj_  
vzrD"  
#&2N,M!Q  
  方法getValue()应该被写为: sv{0XVn+^  
^Lv ^W  
q;a*gqt   
yE|} r  
public synchronized Integer getValue(){ *sIG&  
// ’i’ is immutable, so it is safe to return it instead of a copy. l[\,*C  
return i; ?nGf Wx^  
} %:;[M|.  
K"6+X|yxE  
6!Ji>h.Ak  
_:=OHURc  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: twHM~cTS  
e}O-I  
NF\^'W@N  
UE`4$^qs  
  ?Boolean F']%q 0  
   ?Byte JX@6Sg<  
   ?Character ND9>`I 5  
   ?Class FZ.z'3I  
   ?Double Ty4%du6?d  
   ?Float -"dy z(  
   ?Integer tG ^?fc  
   ?Long ]-Y]Q%A4  
   ?Short Rb}&c)4  
   ?String iN/!k.ybW}  
   ?大部分的Exception的子类 [BR}4(7  
 H[!Q  
f, j(uP  
~Ow23N  
 四、常见错误4# :自编代码来拷贝数组 rKs WS~U  
?O>JtEz~lQ  
U W)&Eky  
FjLv*K[#d  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: *2C79hi1  
{f-/,g~  
% m5^p  
!2M[  
public class Example{ K2o0L5Lke  
  private int[] copy; -[7,ph  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ %TTL^@1!b  
  public void saveCopy (int[] data){ Qnb?hvb"d  
   copy = new int[data.length]; +ET  
   for (int i = 0; i < copy.length; ++i) hsVJ&-#  
    copy = data; Sq8Q *  
  } B';> Hk  
} XoQk'7"f  
QRh4f\fY  
#`)-$vUv^f  
}xlmsOHuI  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是:  D6!+  
;OCI.S8  
Odjd`DD1  
Bsk2&17z  
void saveCopy (int[] data){ oUKbzr/C  
  try{ 0?;Hmq3  
   copy = (int[])data.clone(); [T#a1!  
  }catch (CloneNotSupportedException e){ xI\s9_"Qy  
   // Can’t get here. Fl3r!a!P,  
  } d47:2Zj  
} '2J6%Gg  
QV7c9)<]'}  
o@`E.4  
Ollv _o3  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: '{k Nbx51  
YeVc,B'  
k:@Ls  
m+^;\DFJ,  
static int[] cloneArray (int[] data){ 3[i !2iL.  
  try{ ot<o&  
   return(int[])data.clone(); 9Kx:^~}20o  
  }catch(CloneNotSupportedException e){ >N1]h'q>  
   // Can’t get here. Y[]+C8"O  
  } HV7(6VSJ+  
} :#htOsP  
Qr-J-2s?B  
7-g4S]r<  
=&/a\z!  
  这样的话,我们的saveCopy看起来就更简洁了: p[cL# fBz  
l@J|p#0q  
RGuHXf  
j3-6WUO  
void saveCopy (int[] data){ ;fME4Sp  
  copy = cloneArray ( data); GE+csnA2  
} WB [G!'  
YaT+BRh?  
ko>O ~@r  
mKn357:  
  五、常见错误5#:拷贝错误的数据 F1*rUsRKN  
w>BFgb?  
&u\z T P  
RW^v{'o  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: +ENW=N  
(KImqB$i.  
CvWEXY_P2  
;C3?Ic  
import java.awt.Dimension; JJ=is}S|  
/*** Example class. The height and width values should never * be "{"2h>o#D}  
negative. */ vK7,O%!S  
public class Example{ ^J~4~!  
  static final public int TOTAL_VALUES = 10; m$qC 8z]  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; A1}+j-D7!y  
  public Example (){ } .FRF<_`^  
fqsp1m$  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ J15T!_AW<  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ PR6uw  
   if (height < 0 || width < 0) i8@e}O I  
    throw new IllegalArgumentException(); Y8{1?LO  
    if (d[index] == null) <FT\u{9$  
     d[index] = new Dimension(); #$C]0]|  
     d[index].height = height; $<mL2$.L~  
     d[index].width = width; |aJ6363f.  
  } n$Fm~iPo,  
  public synchronized Dimension[] getValues() H{zuIN/.1  
   throws CloneNotSupportedException{ W2Z]?l;vQQ  
    return (Dimension[])d.clone(); Z9~Wlt'?  
  } BXw,Rz }  
} 2e@\6l,!^  
H).5xx[`  
;iNx@tz4  
'[8jm=Q#'  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: [4rMUS7-m"  
Cfb-:e$0  
; 2-kQK9  
Q&Ahr  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ rL3Vogw'e  
  Dimension[] copy = (Dimension[])d.clone(); (gB=!1/|G  
  for (int i = 0; i < copy.length; ++i){ bx e97]  
   // NOTE: Dimension isn’t cloneable. K -1~K  
   if (d != null) \ySc uT  
    copy = new Dimension (d.height, d.width);   NX_S  
  } >*xzSd? \  
  return copy; ;FflEL<7Y  
} t3JPxg]0k'  
:"IH*7xp  
<yO9j   
y'/9KrV T  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: CoXL;\  
L%Q *\d  
08jQq#  
7N=VVD~!b  
public void store (int[] data) throws CloneNotSupportedException{ jk}PucV  
  this.data = (int[])data.clone(); &bu`\|V  
  // OK Lb 4!N` l  
} .tsB$,/  
cs;Gk:  
RUh{^3;~  
u Aa>6R  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: 7Apbi}")  
"T=LHjE  
%'O(Y{$Y.  
x:lf=D lA  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ l= S_#  
  this.data = (int[][])data.clone(); // Not OK! ]+9:i!s  
} U5 "v1"Ec  
public void rightStore (int[][] data){ !Sh5o'D28  
  // OK! 0N_Da N  
  this.data = (int[][])data.clone(); HbVm O]#$D  
  for (int i = 0; i < data.length; ++i){ OXV@LYP@  
   if (data != null) ;0q6 bp(<H  
    this.data = (int[])data.clone(); rdg1<Z  
  } {v 0(0  
} H`@7o8oj1  
i.4[]f[/h  
R~-q! nC  
=@l5He.]&  
-bfd><bs  
   六、常见错误6#:检查new 操作的结果是否为null [' 1?'*  
*E_= 8OV  
c7wgjQ[   
R.;59s  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: >z$|O>j  
DR8dJ#  
5UyK1e))  
xGL"N1  
Integer i = new Integer (400); QLl44*@  
if (i == null) Fj4:_(%nG  
throw new NullPointerException(); MWf%Lh;R  
b1!%xdy_T  
R!CUR~F  
v*v&f!Ym&s  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 Kn|dnq|G  
)dcGV$4t[  
*A`^ C  
6j#5Ag:  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 q1Mk_(4oJ  
o\[nGf C&  
 七、常见错误7#:用== 替代.equals ;ecF~-oku  
ElxbHQj6  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: 8~&v\GDkF  
Xw)+5+t"{  
s]OXB {M  
C?k4<B7V  
int x = 4; IRB;Q(Z   
int y = 5; ?zqXHv#x  
if (x == y) Gr?gHAT  
  System.out.println ("Hi"); *L_wRhhk  
// This ’if’ test won’t compile. '#?hm-Ga  
if (x.equals (y)) p9J(,}  
  System.out.println ("Hi"); Ycm1 _z  
u 05O[>w  
z)Gr`SA<  
>EjBk nl  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 b-XBs7OAx  
FliN@RNo  
"`zw(  
|kD?^Nx  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 @P/{x@J  
o? =u#=  
SZEr  
u#QQCgrs  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 \)hmg  
e2v,#3Q\  
O^GTPYW  
UF4QPPH4  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 );vU=p"@  
h/ic-iH(>  
%' Fc%3  
:tMWy m  
  八、常见错误8#: 混淆原子操作和非原子操作 ;Lx5r=<Hx  
;F5%X\ t-  
6}0#({s:R  
qSR %#  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: NH/H+7,o  
Ghz)=3  
%* 8QLI  
;(A'XA4 6N  
public class Example{ 4e4$AB"  
  private int value; // More code here... $!t!=  
  public void set (int x){ R?k1)n   
   // NOTE: No synchronized keyword <e"2<qVi  
   this.value = x; XOoND  
  } (1R,   
} 99x]DY  
<K~#@.^`  
|<S9nZg%p  
(fl2?d5+C  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: rmhB!Lo  
b^ZrevM  
' x|B'  
: f Wh7X3  
public void increment (){ f3O3pIA  
  // This is effectively two or three instructions: K>-m8.~\E  
  // 1) Read current setting of ’value’. J_tJj8  
  // 2) Increment that setting. _h#G-  
  // 3) Write the new setting back. 'RhMzPmY>  
  ++this.value; n*V^Q f  
} > 2$M~to"1  
/3fo=7G6  
k0,~wn\#h  
!Bd2$y.  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: ^#%[  
+r '  
\J6T:jeS,  
X~x]VKr/  
public synchronized void increment (){ t C&Xm}:  
  ++this.value; _ ge3R3  
} SYyH_0N  
rv^j&X+EH  
*fx<>aK  
nBQG.3  
  九、常见错误9#:在catch 块中作清除工作 VFyt9:a  
IV\@GM:ait  
m{' q(w}  
}b44^iL$9y  
  一段在catch块中作清除工作的代码如下所示: tNtP+v-{  
X|b~,X%N  
FT=w`NE,+  
StE4n0V  
OutputStream os = null; VF4F7'  
try{ ks! G \<I  
  os = new OutputStream (); tTY(I1  
  // Do something with os here. 7oUYRqd  
  os.close(); 4&?%"2  
}catch (Exception e){ ?qdG)jo=  
  if (os != null) ]wP)!UZ  
  os.close(); 7eY*Y"GX  
} >_R5Li  
h><;TAp  
'&\km~&  
-.xs=NwB.|  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: {8E hC/=  
t &*$@0A  
  ]3%Z  
=U?"#   
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 K,J:i^2  
~;{)S}U@R  
\wM r[_LW  
H>VuUH|  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 S\Q/ "Y  
. a~J.0co  
sLCL\dWT  
XI pXP,Yy  
  3.close()可能会抛出异常。 ;i1H {hB  
:.@gd7T  
<^M`U>   
1Azigd0%  
  上面代码的一个更优版本为: l( "_JI  
h!$W^Tm2g  
:?&N/ 7  
7D4P= $UJp  
OutputStream os = null; }F-WOQ  
try{ zK33.HY  
  os = new OutputStream (); #b:8-Lt:M  
  // Do something with os here. TfMuQi'>  
}finally{ op[5]tjL  
  if (os != null) KyDQ<Dq&  
   os.close(); v50=D/&w  
} afH`<!  
%U'YOE6  
*V>Iv/(  
U<*ZY`B3  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 Cdc6<8  
1}9@aKM  
D guAeK  
S=2-<R  
  十、常见错误10#: 增加不必要的catch 块 fk9FR^u  
9"oc.ue.2D  
Wl}d6ZTm  
Q4C28-#  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 ) =sm{R%T  
{3'z}q  
_"=Yj3?G%  
GV* B$  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 G=(F-U;*  
rj<r6  
K t9:V,  
On#RYy^}  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: N^B YNqr  
n a_Y<R`  
}h>QkV,{2  
]k5l]JB  
try{ 8I3"68c_a  
  // Nifty code here jCxw|tmgq  
}catch(Exception e){ -Y{P"!p0  
  throw e; nUD)G<v  
}finally{ d0eMDIm3R\  
  // Cleanup code here | x/,  
} $Ic: c  
L+bU~N,+A  
u-=%gx"Di  
>#<o7]  
  不必要的catch块被删除后,上面的代码就缩短为: fHdPav f,S  
# KK>D?.:  
8" XbW7^o  
(@>X!]{$  
try{ x<4-Q6'{S  
  // Nifty code here nJNdq`y2  
}finally{ T dlF~ca|  
  // Cleanup code here Oe5=2~4O  
} !0{":4 \  
?dY}xE  
9U^jsb<St>  
aj85vON1`  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 e}D#vPaSY  
.-Ggvw  
H[BY(a@c  
\E5%.KR  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 TeSF  
|/5j0  
f =B)jYI  
s8Xort&   
  小结 FE,&_J"  
IxHusB  
xQT`sK+  
*2Il{KO A^  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 |MY6vRJ(  
.n'z\] -/Q  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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