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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 j'40>Ct=i  
iZq@W3GL C  
t{S{!SF4  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 $Z%aGc*  
M}oFn}-T9a  
gM5p1?E  
X,Q=n2X?3  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 tId !C  
IL6f~!  
(~pEro]?+)  
F)5B[.ce  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 !|:q@|- %@  
t|U2 ws#  
~j&:)a'^  
k-ex<el)#  
  一、常见错误1# :多次拷贝字符串 7" STS7_  
$H:h(ia:  
} Ved  
:%b2;&A[  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 LI|HET_  
FPUR0myCU  
L|1zHDxQ  
FqUt uN  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: q}F%o0  
vBYT)S  
CygV_q  
v4>"p!_C  
String s = new String ("Text here"); x^O2Lj,w\  
+l?ro[#6&.  
73z|'0.  
eW*ae;-  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: >eTgP._  
oJJ k  
2SPFjpG8n  
=O'%)Y&  
String temp = "Text here"; ]|La MMD  
String s = new String (temp); hCvLwZ?LF  
Ufe  
:9 iOuu  
+ZA\ M:^b  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: 6BN(^y#-X  
kbT-Oz  2  
pdha" EV  
OUk5c$M(  
String s = "Text here"; IZv, Wo  
s>``- ]3  
= 4WZr  
Nl<,rD+KSD  
  二、常见错误2#: 没有克隆(clone)返回的对象 ^}7t:  
7RFkHME  
p+sPCF  
~5!TV,>ls  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: f<sPh>n  
d<'Yt|zt  
@gjdyz  
@bCiaBdi  
import java.awt.Dimension; 0#/ 6P&6  
/***Example class.The x and y values should never*be negative.*/ $z,DcO.vz  
public class Example{ VrE5^\k<a  
  private Dimension d = new Dimension (0, 0); 1LIV/l^}f  
  public Example (){ } ftH%, /,  
TIh zMW\/K  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ _%Ld E z  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ J9=0?^v-:B  
   if (height < 0 || width < 0) JIKxY$GS  
    throw new IllegalArgumentException(); ZpctsCz]  
    d.height = height; J'c9577$  
     d.width = width; 5"~^;O  
  } HgATH  
$(L7/M  
  public synchronized Dimension getValues(){ Hpg;?xAT  
   // Ooops! Breaks encapsulation b-zX3R;  
   return d; / cen# pb  
  } 1`_)%Y[ZJ  
} dsZ ( D:)  
sK/"  
i6:yNb ='  
<a[8;YQC  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: XK-x*|  
,wo"(E!4e  
rPpAg  
({nSs5)$  
Example ex = new Example(); ;OJ0}\*iP8  
Dimension d = ex.getValues(); swq!S p  
d.height = -5; fToI,FA  
d.width = -10; 5 t?2B]  
sLqvDH?V  
Rs[]i;  
LhRe?U\  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 *+Q*&-$  
l{o{=]x1  
ykhCt\t[  
%ys}Q!gR  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 @5G7bY7Nz  
y]4 `d  
 ly%B!P|  
i O|,,;_  
  更好的方式是让getValues()返回拷贝: rg/vxTl  
azc:C  
\o)4m[oF  
mM{v>Em2K#  
public synchronized Dimension getValues(){ ~Fb?h%w  
return new Dimension (d.x, d.y); swL|Ff`$  
} k\%v;3nBK  
<uwCP4E  
O9)}:++T  
FN EmGz/4  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 %{abRBny  
'k Z1&_{  
Ka\b_P&  
u*N8s[s'  
  三、常见错误3#:不必要的克隆 !z 5d+ M  
wu&7#![,  
*v/*_6f*  
:]Qx T8B  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: oa !P]r  
G@!9)v]9  
1^^D :tt  
S Tk#hhx  
/*** Example class.The value should never * be negative.*/ JHH&@Cn  
public class Example{ T=dvc}  
  private Integer i = new Integer (0); >v,j;[(  
  public Example (){ } (r\h dLX  
MXV4bgltT  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ 3~xOO*`o  
  public synchronized void setValues (int x) throws IllegalArgumentException{ =W*`HV-w  
   if (x < 0) @0'|Uygn  
    throw new IllegalArgumentException(); *7ro [  
    i = new Integer (x); ?} tQaj  
  } {K8T5zrV  
p;=(-4\V}  
  public synchronized Integer getValue(){ (k&aD2PH  
   // We can’t clone Integers so we makea copy this way. 0*@S-Lj^c  
   return new Integer (i.intValue()); D+""o"%  
  } 5K~6`  
} Ib2pV2`h(  
|R/50axI  
AB\4+ CLV  
n5>N9lc  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 ZS_f',kE  
{hR2NUm  
lXKZNCL  
#K w\r50  
  方法getValue()应该被写为: V7_??L%Ct`  
<5~>.DuE  
4HE4e  
 +'.Q-  
public synchronized Integer getValue(){ !;Nh7vG  
// ’i’ is immutable, so it is safe to return it instead of a copy. 7*"LW  
return i; qG]PUc>j  
} e|yuPd  
I0RWdOK8K  
*$D-6}Oay  
Ngnjr7Q={T  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: nB& 8=.  
5wX>PJS  
L9oZ7o  
G)7sXEe  
  ?Boolean q /?_djv  
   ?Byte Q2?qvNZ  
   ?Character Q~_x%KN/`  
   ?Class }L9j`17  
   ?Double `Cxe`w4  
   ?Float o w[qpP[  
   ?Integer glgk>83I+  
   ?Long sc60:IxgI  
   ?Short #mYxO  
   ?String =YIQ _,{u  
   ?大部分的Exception的子类 Hp!F?J7sx  
P7-3Vf_L  
IhLfuyFWu  
0aWb s$FyU  
 四、常见错误4# :自编代码来拷贝数组 C<>.*wlp=  
`f]O  
CI{x/ e^(  
GNOC5 E$I  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: O]lfs >>x  
 <@u6*]  
>k|[U[@  
e_V(G  
public class Example{ p;Kr664  
  private int[] copy; qE{S'XyM,  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ ]XU#i#;c  
  public void saveCopy (int[] data){ (xL=X%6a  
   copy = new int[data.length]; N{g=Pf?I}  
   for (int i = 0; i < copy.length; ++i) zhE7+``g  
    copy = data; {IWb:p#I]  
  } 2l?J9c}Wo  
} 7ow1=%Q  
f6 nltZ  
{wI0 =U  
 7I|Mq  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: +F|[9o z  
9OUhV [D  
S}X:LHr*  
4NV1v&"  
void saveCopy (int[] data){ p~IvkW>ln)  
  try{ )A%Y wI$  
   copy = (int[])data.clone(); G>x0}c  
  }catch (CloneNotSupportedException e){ ~55>uw<  
   // Can’t get here. 'oG'`ED"  
  } e-mlvi^-  
} fp0Va!T(V  
1~ Nz6  
~\P.gSiz  
1 <+^$QL  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: mLE`IKgd]  
] ?(=rm9u  
}g?]B+0  
X6RM2  
static int[] cloneArray (int[] data){ . {I7sUQ  
  try{ =%LS9e^7D  
   return(int[])data.clone(); Gj=il-Po  
  }catch(CloneNotSupportedException e){ Ry C7  
   // Can’t get here. bxs@_fH  
  } z61 o6mb  
} $G3P3y: [  
h*LIS@&9C5  
}qTvUs  
/hQ!dU.+  
  这样的话,我们的saveCopy看起来就更简洁了: X}$S|1CjO  
Dg`W{oj  
dkjL;1  
Jp- hFD  
void saveCopy (int[] data){ \Z8!iruN  
  copy = cloneArray ( data); \B)<<[ $  
} wr`eBPu  
v|6fqG+Q\  
y@I"Hk<T  
pN[i%\vh  
  五、常见错误5#:拷贝错误的数据 \XC1/LZQ  
c{~*\&  
*"@P2F&  
v&Kw 3!X#E  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: eC?N>wHH  
/1*\*<cs  
_N6GV$Q  
~&kV  
import java.awt.Dimension; TUG3#PSnm*  
/*** Example class. The height and width values should never * be Mtu8zm  
negative. */ x)*[>d2yd  
public class Example{ rlD@O~P4  
  static final public int TOTAL_VALUES = 10; Ch3##-  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; U/>5C:  
  public Example (){ }  l}JVRU{  
~0L>l J  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ E%TvGe;#  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ vsK>?5{C-  
   if (height < 0 || width < 0) -Db(  
    throw new IllegalArgumentException(); g(1'i1  
    if (d[index] == null) Uu ,Re  
     d[index] = new Dimension(); ~c4Y*]J  
     d[index].height = height; Ae1},2py  
     d[index].width = width; "'%x|nB  
  } xfb%bkr  
  public synchronized Dimension[] getValues() J#\/znT  
   throws CloneNotSupportedException{ ~jgd92`{z  
    return (Dimension[])d.clone(); V;$lgTs|'  
  } ?S"xR0 *  
} &3rh{"^9  
AK[c!mzx  
52oR^ |  
<iMLM<J<w  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: '2NeuK-KD  
--FvE|I  
yDPek*#^"q  
'?\Hm'8  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ xe d$z  
  Dimension[] copy = (Dimension[])d.clone(); @_;6 L  
  for (int i = 0; i < copy.length; ++i){ uaiG (O   
   // NOTE: Dimension isn’t cloneable. PqfH}d0l  
   if (d != null) ^pn:SV  
    copy = new Dimension (d.height, d.width); s:%>H|-  
  } NFQ0/iuW  
  return copy; l 1@:&j3h  
} "YivjHa7H  
K.z@Vx.  
%lujme  
H]cCyuCdH  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: ak%8|'}  
\o/oM,u  
PWTAy\  
#N*~Q  
public void store (int[] data) throws CloneNotSupportedException{ p0Vw@R=  
  this.data = (int[])data.clone(); o;t{YfK  
  // OK [=Xvp z  
} W_?S^>?l/  
0'gJSrgNI  
)pg?ZM9  
lm$T`:c  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: wDn5|F}i&  
"F=O   
_]B'C  
5'X.Z:  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ rKO[;]_*  
  this.data = (int[][])data.clone(); // Not OK! ^+-i7`|=  
} Yt&^ i(  
public void rightStore (int[][] data){ DwoO([&I  
  // OK! {&xKS WNc  
  this.data = (int[][])data.clone(); \2uQ"kJC  
  for (int i = 0; i < data.length; ++i){ 905 /4z'  
   if (data != null) Jg@PhN<9  
    this.data = (int[])data.clone(); j &~OR6  
  } (i {  
} S^3I"B  
1Eh (U  
*\emRI>  
 $///N+B  
f)>=.sp  
   六、常见错误6#:检查new 操作的结果是否为null }z}oVc  
v=!]t=P)t  
 0N md*r  
K?) &8S  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: QHK$2xtq|  
<!m'xOD  
l%^VBv> 2  
2qQG  
Integer i = new Integer (400); n9p_D  
if (i == null) V% PeZ.Xv  
throw new NullPointerException(); dd{pF\a  
,|u^-J@  
%hnv go:^g  
pA|Z%aL  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 fVJsVZ"6v`  
zVL"$ )  
9f/RD?(1O  
U|2*.''+Q  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 J/ ! Mt  
%DqPRl.Gu  
 七、常见错误7#:用== 替代.equals 1B#Z<p  
G,]%dZH e  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: WBIJ9e2~  
Rfuq(DwD6  
f5p:o}U*  
wE*jN~  
int x = 4; ;3 |Z}P  
int y = 5; V < ;vy&&  
if (x == y) H)u<$y!8  
  System.out.println ("Hi"); Frxim  
// This ’if’ test won’t compile. h0v4!`PQ-  
if (x.equals (y)) XC NM  
  System.out.println ("Hi"); ]z{f)`;I  
AR}q<k6E  
|5(< Vk=  
'tRaF  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 Kq. MmR!gl  
mxxuD"5  
VUD ?iv7  
nm3/-Q},  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 xdqiogue  
D%k`udz<  
'i5V6yB  
#4Z]/D2G  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 ~ R:=zGDV  
qDzd_E@aR  
W\W|v?r  
J#L"kz  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 M1sR+e$"  
p~h)@  
={GYJ. *Ah  
M:*^k  
  八、常见错误8#: 混淆原子操作和非原子操作 ;K+'J0  
a*fUMhIi  
NE'4atQ|  
B"9/+Yj  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: 5qx,b&^w  
AnUOv 2  
y66V&#`,e0  
F_ Cp,  
public class Example{ 5*#!w1X  
  private int value; // More code here... E$w2S Q  
  public void set (int x){ ! 4ZszQg  
   // NOTE: No synchronized keyword k'hJ@ 6eKS  
   this.value = x; Gx.iZOOH/  
  } 9sR?aW^$,/  
} mV58&SZT  
pO~lVM  
`QIYnokL  
w&F/P]1  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: |D ?}6z  
lN<,<'&^.  
4kZ9]5#.  
X9lh@`3  
public void increment (){ fT&>L  
  // This is effectively two or three instructions: RkW)B^#  
  // 1) Read current setting of ’value’. %#^)hX,+Q  
  // 2) Increment that setting. AZi|85rN  
  // 3) Write the new setting back. >We:g Kxr  
  ++this.value; b<N962 q$q  
} 13X0LN  
3Xun>ZQ-  
IQz:D J  
+/L "A  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: <Fc @T4Q,  
rps2sXGr  
^JKV~+ Q  
f"8!uE*;  
public synchronized void increment (){ JDIQpO"Qji  
  ++this.value; cc"L> XoK  
} w,'"2^Cwy  
XehpW}2\  
@7C?]/8#  
o,#[Se*n  
  九、常见错误9#:在catch 块中作清除工作 D m|_;iO,  
%S2^i3  
/%fa_+,|-  
0%9Nf!j  
  一段在catch块中作清除工作的代码如下所示: pX nY=  
#DL( %=:  
oZY2K3J)  
0^27grU>   
OutputStream os = null; Ot]Y/;K  
try{ 2I 2#o9(Ar  
  os = new OutputStream (); w# t[sI"IT  
  // Do something with os here. \; b)qB  
  os.close(); 6"d^4L?  
}catch (Exception e){ H| uvcvf  
  if (os != null) -RSPYQjz  
  os.close(); YRkp(}*!\  
} $SP*hkU  
jf_0IE  
e2SU)Tr%b  
|+^-b}0  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: fCA/   
*=-o0c  
gD[Fkq$]  
}P5zf$  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 _>G=v!  
w_gPX0N}3n  
Pmj]"7Vd[  
BZXP%{njS  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 #b~wIOR)Z  
Llf |fayq  
(ei;Y~i  
=-ky%3:`@  
  3.close()可能会抛出异常。 y11/:|  
9Yh0' <Z  
J| orvnkK  
09f:%!^u  
  上面代码的一个更优版本为: pP4i0mO{Dv  
N}Ol`@@#h  
iaqhP7!  
\LFRu  
OutputStream os = null; q/o|uAq  
try{ n\nC.|_G@  
  os = new OutputStream ();  Yfk){1  
  // Do something with os here. 5$r`e+Nf'  
}finally{ kKFSCl/g  
  if (os != null) b6IYo!3  
   os.close(); B%v2)+?@  
} X(-e-:B4;  
Y* #'Gh,  
kAbkhZ1^  
z2m%L0  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 %SRUHx[D  
1PMBo=SUe8  
d9zI A6y  
>uok\sX  
  十、常见错误10#: 增加不必要的catch 块 O7#ECUH  
~~?4w.k  
k)W8%=R  
BReNhk)S  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 f6 zT  
6]i"lqb  
8{5Y%InL  
Hev S}L  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 vG(Gs=.U  
iOB]72dh  
}+[H~8)5  
y.AF90Q>)  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: UFxQ-GV4  
KzRw)P  
[sC]<2 r  
{Gnji] v  
try{ w][1C\8m  
  // Nifty code here ckqU2ETpD}  
}catch(Exception e){ G?LPj*=$?  
  throw e; /znW$yh o  
}finally{ v535LwFW  
  // Cleanup code here SRl:+!@.  
} |-N\?N9"  
&zsaVm8  
K2T&U$ ,  
*p;Fwj]  
  不必要的catch块被删除后,上面的代码就缩短为: 1}e1:m]r  
XqVhC):  
K/Q^8%Z  
aOq>Ra{T  
try{ [>P@3t(/  
  // Nifty code here ^$):Xz  
}finally{ 6!} @vp![  
  // Cleanup code here .6%-Il  
} =,0E]M Z  
QN_Zd@K*A  
Zx(VwB2   
1F*gPhm  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 8LP L4l  
_ x&Y'X|  
8(UUc>g  
ylF%6!V}4V  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 ':8yp|A|  
>Vr+\c  
zbdmz  
#C1u~db  
  小结 }EmNSs`$r  
6P=6E   
VLW<"7I 6\  
0c4H2RW  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 i]8HzKuiW  
Rh-e C6P  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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