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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 jN*wbqL  
/!?Tv8TPp  
U5~aG!E  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 6S3D#SY  
AzZhIhWl">  
32SkxcfrCK  
=p=/@FN  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 :A @f[Y'9  
z\ONw Ml  
)8#-IXxp  
%W7%]Z@j  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 #V]8FW  
K\;4;6 g  
eZ[Qhrc  
$t}W,?   
  一、常见错误1# :多次拷贝字符串 A- Abj'  
6Y,&q|K  
hHVAN3e  
O#18a,o@  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 CdmpKkq#  
WoGnJ0N q  
71P. 9Iz  
![r)KE=v8I  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: 8,[ *BgeX  
.JB1#&B +  
<X4f2z{T{@  
lemV&$WN|  
String s = new String ("Text here"); Z]TQ+9t  
aZ\Z7(  
^w``(-[*  
Vq`/]&  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: p=> +3  
cQThpgha  
~uZ9%UB_m  
_xi &%F/  
String temp = "Text here"; j #P4&  
String s = new String (temp); OAW_c.)5D  
B]<N7NYn1  
vf<Dqy<M.  
rKslgZhQ  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: @jMo/kO/A  
-X7x~x-  
!A% vR\  
CVkJMH_  
String s = "Text here"; 4xalm  
W=293mME  
~'0n ]Fw  
0]'  2i  
  二、常见错误2#: 没有克隆(clone)返回的对象 8$47Y2r@  
piIz ff  
>d]-X]  
MMET^SO  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: a`^$xOK,  
n[K%Xs)  
!.O[@A\.-  
K,|3?CjS  
import java.awt.Dimension; J>#yA0QD2  
/***Example class.The x and y values should never*be negative.*/ c?c\6*O  
public class Example{ )z z{~Cf  
  private Dimension d = new Dimension (0, 0); # .(f7~  
  public Example (){ } u^E0u^  
7SYe:^Dx  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ d#bg(y\G|  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ )T gfd5B  
   if (height < 0 || width < 0) 7p':a)  
    throw new IllegalArgumentException(); . a @7  
    d.height = height; \vc&V8  
     d.width = width; ~~k0&mK|Q  
  } AT3HH QD  
D aHbOs_<  
  public synchronized Dimension getValues(){ !- QB>`7$  
   // Ooops! Breaks encapsulation 0k?]~ f  
   return d; Y`-q[F?\y  
  } t4:/qy  
} 7zE1>.  
"oZ_1qi<  
<^{(?*  
/=9dX; #  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: KV&6v`K/N  
F 8sOc&L  
Wrp+B[ {r\  
r]D>p&4  
Example ex = new Example(); }u0&>k|y  
Dimension d = ex.getValues(); +cmi?~KS*  
d.height = -5; <GQ=PrT|/  
d.width = -10; \vV]fX   
u 6l)s0Q  
xnWezO_  
MwSfuP  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 0~W XA=XG  
Th\T$T`X$  
'4u/g  
 g;AW  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 d*k5h<jM  
Rb:?%\=  
z+wegF  
c>/7E-T  
  更好的方式是让getValues()返回拷贝: lAC "7 Z?F  
j^U"GprA  
T7/DH  
$;=?[Cn  
public synchronized Dimension getValues(){ x]%,?Vd?  
return new Dimension (d.x, d.y); Gkfzb>_V]  
} \k=%G_W  
Oz]$zRu/0  
+CSR!  
.Sa=VC?EZ  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 0Db=/sJ>  
R!X+-  
gC kR$.-E  
ZDI?"dt{  
  三、常见错误3#:不必要的克隆 O6b+eS  
w}$;2g0=a<  
FrLv%tK|  
>z fx2wh\a  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: HP<a'|r  
KX cRm)  
b~)2`l  
E|_8#xvb  
/*** Example class.The value should never * be negative.*/ a[}?!G-Wt|  
public class Example{ +`B^D  
  private Integer i = new Integer (0); !a!4^zqp  
  public Example (){ } {dE(.Z?]!#  
RK$(  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ pTTM(Hrx  
  public synchronized void setValues (int x) throws IllegalArgumentException{ 7aPA+gA/  
   if (x < 0) :h3U^  
    throw new IllegalArgumentException(); <)sL8G9Y  
    i = new Integer (x); *(]ZdB_2  
  } `}$bJCSF.n  
oGg<s3;UND  
  public synchronized Integer getValue(){ ]E DC s?,  
   // We can’t clone Integers so we makea copy this way. QpoC-4F  
   return new Integer (i.intValue()); x6Gl|e[jv  
  } i$6a0'@U  
} w6Mv%ZO_  
TMs Cl6dB  
G6x'Myg I  
itiSZL,  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 |_+l D|'  
KV6D0~  
P (Y\l  
e6{E(=R[M  
  方法getValue()应该被写为: H`q[!5~8  
1Id"|/b%$  
@"^7ASd%  
{KEmGHC4R  
public synchronized Integer getValue(){ H%Lln#  
// ’i’ is immutable, so it is safe to return it instead of a copy. m,]9\0GUd  
return i; l4iklg3  
} ]8Xip/uE  
Q6 m.yds  
lU$0e09  
]\}MSo3  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: A =&`TfXu  
-'*<;]P+.  
01RW|rN  
H}CmSo8&  
  ?Boolean m$pRA0s2`  
   ?Byte [!uVo>Q4  
   ?Character +/M%%:>mY  
   ?Class @*=5a (#  
   ?Double jPx}-_jM  
   ?Float {L.uLr_?e  
   ?Integer _nX8f &  
   ?Long -m ;n}ECg  
   ?Short 08%Bx~88_%  
   ?String itc\wn  
   ?大部分的Exception的子类 %S$$*|_G  
pNmWBp|ER  
Xi\c>eALO  
M&Ln'BC  
 四、常见错误4# :自编代码来拷贝数组 n:1Ijh 1  
e VQ-?DK  
inK;n  
tAY{+N]f  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: WlGT&m&2  
d 792#Dc  
O;}K7rSc  
[U"/A1p  
public class Example{ Jm< uE]9  
  private int[] copy; jPZpJ:  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ aS\$@41"  
  public void saveCopy (int[] data){ tB(~:"|8  
   copy = new int[data.length]; puMb B9)  
   for (int i = 0; i < copy.length; ++i) zf^|H% ~^  
    copy = data; /Ah&d@b  
  } ^kz(/c/?  
} P46Q3EE  
?gjx7TQ?  
v#X#F9C  
'4Qsl~[Eh  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: ja&m-CFK  
)%n $_N n  
MQ0r ln?  
b&LAk-}[  
void saveCopy (int[] data){ O(D2F$VlL  
  try{ 27$,D XD  
   copy = (int[])data.clone(); d/~g3n>|  
  }catch (CloneNotSupportedException e){ u3tT=5.D  
   // Can’t get here. * >8EMq\^  
  } I:UDEoQo  
} iXvrZofE  
(vchZn#  
_)~VKA]""  
?~yJ7~3TS<  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: K1]3zLnS  
*-Vr=e<8   
%yk_(3a  
FC+h \  
static int[] cloneArray (int[] data){ AS;qJ)JfzQ  
  try{ @' ;.$  
   return(int[])data.clone(); Aq3\Q>klH)  
  }catch(CloneNotSupportedException e){ &Vgpv#&Cfx  
   // Can’t get here. @>V;guJC%  
  } *vvm8ik  
} ~oT*@  
4udj"-V  
S'hUh'PZ  
~{vB2  
  这样的话,我们的saveCopy看起来就更简洁了: kY{$[+-jR  
kOq8zYU|  
>s0![coz  
~<s =yjTu+  
void saveCopy (int[] data){ oDi+\0  
  copy = cloneArray ( data); Qh-:P`CN  
} n&?)gKL0g  
Dh?I   
M'|p<SO]  
4i^WE;|s  
  五、常见错误5#:拷贝错误的数据 K{"hf:k  
u |.7w 2  
u*,>$(-u  
c/v|e&q  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: o; U!{G(X  
N3@[95  
N#t`ZC&m'  
MtN!Xx  
import java.awt.Dimension; D3P/: 4  
/*** Example class. The height and width values should never * be t4/ye>P &  
negative. */ P t/]Z<VL  
public class Example{ lI.oyR'  
  static final public int TOTAL_VALUES = 10; DX+zK'34  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; C_8_sb Z/  
  public Example (){ } mZPvG  
j0a=v}j3  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ cGdYfi  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ `ZI-1&Y3  
   if (height < 0 || width < 0) (K84J*;  
    throw new IllegalArgumentException(); X?n=UebO^  
    if (d[index] == null) : T7(sf*!*  
     d[index] = new Dimension(); \x]\W#C  
     d[index].height = height;  P Je_qP  
     d[index].width = width; L G5_\sY!  
  } 8UqH"^9.Q7  
  public synchronized Dimension[] getValues() xSSEDfq  
   throws CloneNotSupportedException{ tpO '<b  
    return (Dimension[])d.clone(); ,-8 -Y>[  
  } 5I^;v;F  
} `M 'tuQ M  
NRp  
hwJ>IQ1  
=y)K er  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: g=@d!]Z~[  
^+CHp(X  
~!8j,Bqs+z  
ka8Y+Gs  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ b.@4yW  
  Dimension[] copy = (Dimension[])d.clone(); m_@XoS yxI  
  for (int i = 0; i < copy.length; ++i){ *pv<ZF0>  
   // NOTE: Dimension isn’t cloneable. q^Oj/ws  
   if (d != null) dIYf}7P  
    copy = new Dimension (d.height, d.width); 9!W$S[ABRB  
  } +jF2 {"  
  return copy; q#8yU\J|,  
} Ro"'f7(v.  
PoPR34] ^J  
jlU6keZh`  
 HG?+b  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: Fs%`W4/  
.SER,],P  
ljOY;WV3  
"`4ky ]  
public void store (int[] data) throws CloneNotSupportedException{ mTxqcQc:7  
  this.data = (int[])data.clone(); N!3Tg564j  
  // OK z8JW iRn  
} 2b^Fz0 w4  
rqqd} kA  
*q k7e[IP  
liH#=C8l*%  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: 'Kbrz  
:-JryiI  
/W BmR R  
QDJ "X  
public void wrongStore (int[][] data) throws CloneNotSupportedException{  QSY>8P  
  this.data = (int[][])data.clone(); // Not OK! h@G~' \8t  
} LSJ.pBl\X  
public void rightStore (int[][] data){ tO:JB&vO2  
  // OK! c$7~EP  
  this.data = (int[][])data.clone(); gK({InOP  
  for (int i = 0; i < data.length; ++i){ KU9FHN  
   if (data != null) PgT8 1u  
    this.data = (int[])data.clone(); 1Z @sh>X|  
  } =f{v:n6  
} rz k;Q@1  
sg2%BkTI  
6WG g_x?3  
}P.Z}n;Uj  
;<m`mb4x[  
   六、常见错误6#:检查new 操作的结果是否为null /r"<:+  
Hcu!bOQ  
d8w3Oz54  
\WE&5 9G  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: ~U"m"zpLP  
&s vg<UZ  
bHv"!  
n{sk  
Integer i = new Integer (400); "YgpgW  
if (i == null) kodd7 AD  
throw new NullPointerException(); nk%v|ZxoFv  
52tc|j6~#  
0 h!Du|?  
L#byYB;E{  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 T[k$[  
|yeQz  
0h*Le  
<;PKec  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。 J*$%d1  
$$1t4=Pz  
 七、常见错误7#:用== 替代.equals "}*D,[C5e  
wb?k  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: ge GhM>G  
[=q/f2_1.  
eQu(3sYb  
j0; ~2W#G*  
int x = 4; :1j8!R5  
int y = 5; X%IqZ{ {  
if (x == y) -GPJ,S V>  
  System.out.println ("Hi"); Nyy&'\`!  
// This ’if’ test won’t compile. jo<xrn\  
if (x.equals (y)) +?o!"SJ  
  System.out.println ("Hi"); (!5Ta7X  
JpC=ACF  
eb\SpdM6  
S7f.^8  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 e>Z&0lV:  
b3E1S+\=~  
S=!WFKcJR  
<7\j\`  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 i3N{Dt  
(is',4^b  
$It mYj.m  
s"`~Xnf  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 m.m6.  
:&vX0 Ce:  
j}ob7O&U'w  
0@-4.IHl  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 #:gl+  
[8sYEh  
OVi < d  
Ul_Zn  
  八、常见错误8#: 混淆原子操作和非原子操作 5+M,X kg  
`5?0yXK  
`z(o01y  
}h45j84)  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: <WZ{<'ajI  
?Te#lp;`~  
Jq &Hz$L|  
,Zn6T"[$  
public class Example{ H%vfRl3rB  
  private int value; // More code here... 8>e YM  
  public void set (int x){ HfVHjF)  
   // NOTE: No synchronized keyword M=liG+d  
   this.value = x; K'Ywv@  
  } *HR pbe2  
} ?K[Y"*y2  
j9 >[^t3U  
Unb2D4&'  
KSchgon0V  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: <!Cjq,Sk7  
eavn.I8J  
Ra|P5  
bQautRW  
public void increment (){ HXKM<E{j  
  // This is effectively two or three instructions: 6T$=(I <4  
  // 1) Read current setting of ’value’. , yltt+ e  
  // 2) Increment that setting. AyO%,6p[  
  // 3) Write the new setting back. KBB)xez8  
  ++this.value; M/p9 I gp  
} GB|>eZLv<  
/&Oo)OB;  
l|WFS  
i|1*bZ6'  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: %Z_O\zRqy)  
(Ut8pa+yX  
p*Q-o  
(a_bU5)  
public synchronized void increment (){ B8Fb$  
  ++this.value; RD:G 9[  
} $^iio@SW{  
w UxFE=ia  
#4bT8kq  
u4~+Bc_GL  
  九、常见错误9#:在catch 块中作清除工作 \.mVLLtG  
OK80-/8HI  
"++\6 H<  
1@L18%h  
  一段在catch块中作清除工作的代码如下所示: n/5T{NfG  
O.B9w+G=  
2/ 4zg  
t <` As6}  
OutputStream os = null; Nj4CkMM[3  
try{ JW[6 ^Rw  
  os = new OutputStream (); D-BT`@~l  
  // Do something with os here. RdPk1?}K  
  os.close(); i4|R0>b  
}catch (Exception e){ \lQ3j8 U  
  if (os != null) [L+*pW+$\.  
  os.close(); k4V3.i!E  
} ?-)!dl%N  
k 3m_L-  
-rsbSt ?_  
(Y)2[j  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: OWewV@VXR  
lk 1\|Q I  
&'>m;W  
hEB5=~A_  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 jV}8VK*`+  
Np+PUu>  
5bt>MoKxv  
Jo\MDyb]  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 Z|E9}Il]  
N5*Q nb8  
4tCM 2it%  
Vr},+Rj  
  3.close()可能会抛出异常。 I*N"_uKU  
csW\Q][  
9s"st\u 4  
Z>`\$1CI  
  上面代码的一个更优版本为: N~=I))i  
s9+):,dKP  
^ 4<D%\  
B$2b =\  
OutputStream os = null; g{DehBM  
try{ LXo$\~M8G8  
  os = new OutputStream (); 9PKXQp  
  // Do something with os here. 32 i6j  
}finally{ 7{}E{/  
  if (os != null) 7_2D4CI  
   os.close(); sg7h&<Xx  
} CnB[ImMs(A  
j<~Wp$\i7>  
3FR(gr$X  
SQ,-45@W  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 -kk7y  
G~1;_'  
TMMKRC1<  
!=:>yWQ  
  十、常见错误10#: 增加不必要的catch 块 \B4H0f  
h]s6)tI I  
XA!a^@<H  
3l?|+sU >O  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 AT1cN1:4?  
SvSO?H!-  
o08g]a  
D@La-K*5  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 N] sbI)Z@  
&AJ bx  
;=,-C ;`  
`6VnL)  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: O z0-cM8t  
3tf_\E+mIi  
k3$'K}=d  
{*O%A  
try{ 0FcDO5ia  
  // Nifty code here vSnVq>-q&  
}catch(Exception e){ CBd%}il  
  throw e; &tZIWV1&  
}finally{ v<v;ZR)  
  // Cleanup code here Jt5V{9:('  
} 5&s6(?,Eu  
@ 3=pFYW)  
F[}#7}xjA  
`$ f`55e  
  不必要的catch块被删除后,上面的代码就缩短为: "]=OR>  
@!")shc  
4JK6<Pk  
nCi ]6;Y  
try{ W5Z-s.o  
  // Nifty code here :<P4=P P  
}finally{ GPHb-  
  // Cleanup code here fsjLD|?|:  
} i[KXkjr  
Fl.?*KBz  
V| Fo@  
c)#7T<>*'  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 q.=Q  
H7+z"^s*  
"~ID.G|<  
SOR\oZ7  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 /}@F q  
zY\u" '4  
PFp!T [)  
IQ<G .  
  小结 : 2%eh  
:(XyiF<Ud  
TQO|C?  
G@DNV3Cc  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 iqR6z\p&  
[l^XqD D4  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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