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

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

级别: 终身会员
发帖
3743
铜板
8
人品值
493
贡献值
9
交易币
0
好评度
3746
信誉值
0
金币
0
所在楼道
代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。 0,)Ao8  
**\BP,]}  
h&|wqna  
  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。 - w41Bvz0  
o`^GUY}  
Y-(),k_Q:  
HV:mS*e  
 正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查?由同事们寻找代码中的错误?所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。 cv fh:~L  
"BB#[@  
8+^?<FKa  
z`gdE0@;d3  
  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。 QusEWq)}<  
n^QOGT.s6`  
bDdJh}Vz  
>`rK=?12<  
  一、常见错误1# :多次拷贝字符串 }qUNXE@  
6 bL+q`3>  
7?6?`no~JJ  
)k5lA=(Yr+  
  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。 /a7tg+:  
,e"A9ik#  
.y7&!a35  
w, 0tY=h6  
  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作: )"7hyW5  
KZ ezA4  
3[Pa~]yS  
YxMOr\B  
String s = new String ("Text here"); ]a% *$TF  
T!6H5>zA  
1j*I`xZ  
'[shY  
  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码: _E5%Px5>L  
2A3;#v  
\Cx) ~bq<  
<YbOO{  
String temp = "Text here"; $)| l#'r  
String s = new String (temp); W(*:8}m,p  
e_J_rx  
]pLQ;7f7D  
cmDskQ:  
  但是这段代码包含额外的String,并非完全必要。更好的代码为: E-,74B&H  
A.9,p  
H[o'j@0  
&]~z-0`$!  
String s = "Text here"; @+",f]  
G'XlsyaWrb  
bw#zMU^E  
3j.Ft*SV  
  二、常见错误2#: 没有克隆(clone)返回的对象 9GS<d.#Nvc  
Cna@3)_  
dN>XZv  
W38My j!  
  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便??Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点: 0pYz8OB  
b2 ~~ !C  
fys@%PZq  
qs6yEuh#  
import java.awt.Dimension; <!:,(V>F(C  
/***Example class.The x and y values should never*be negative.*/ 8k'UEf`'(  
public class Example{ sm}q&m]ad  
  private Dimension d = new Dimension (0, 0); {+f@7^/i.  
  public Example (){ } Df;FOTTi%  
HzB&+c? Z  
  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/ 76[aOC2Ad  
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{ U{D ?1tF  
   if (height < 0 || width < 0) F#_7mC   
    throw new IllegalArgumentException(); JJ56d)37.  
    d.height = height; XF2u<sDe  
     d.width = width; &0TOJ:RP  
  } rWbuoG+8  
!lE (!d3M  
  public synchronized Dimension getValues(){ Oa~t&s  
   // Ooops! Breaks encapsulation k%QhF]  
   return d; t~p9iGX<  
  } zW%-Z6%D  
} !m pRLBH  
D8_m_M| P  
'j$iSW&  
?n/:1LN,  
  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码: h 88iZK  
f(DGC2R <  
A <iF37.  
e =& abu  
Example ex = new Example(); ld94ek  
Dimension d = ex.getValues(); 7"=  
d.height = -5; ,oDZ:";  
d.width = -10; g'Ft5fQ"o/  
j._9;HifZ  
fl~k')s  
V~5vVY_HG&  
  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。 ))!Z2PfD  
%Ua*}C   
D`e!CprF  
>8SX,  
  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。 N##T1 Qm)  
=KNg "|  
 <_MQC  
%-]j;'6}cX  
  更好的方式是让getValues()返回拷贝: !'ajpK  
5@j?7%_8  
@okC":Fw,  
a#!Vi93  
public synchronized Dimension getValues(){ 'O]_A57  
return new Dimension (d.x, d.y); /{7x|ay]  
} ? $pGG  
%xLziF  
+d\"n  
1SkGG0 W  
  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。 BmUEo$w  
4cJ^L <  
9`.b   
8nES=<rz  
  三、常见错误3#:不必要的克隆 n_v c}ame  
'. atbl  
WKBPqfC  
gU>Y  
  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对: /j -LW1:N  
i1vBg}WHN  
n5UcivyX  
(W3R3>;  
/*** Example class.The value should never * be negative.*/ abD55YJY  
public class Example{ ;eG%#=>  
  private Integer i = new Integer (0); bm%2K@ /U  
  public Example (){ } 8[f]9P/i  
xQ1&j,R]  
  /*** Set x. x must be nonnegative* or an exception will be thrown*/ @)VJ,Ql$Y  
  public synchronized void setValues (int x) throws IllegalArgumentException{ O:r<es1  
   if (x < 0) CJjma=XH  
    throw new IllegalArgumentException(); / c/!13|  
    i = new Integer (x); MnKEZ: 2  
  } jY>KF'y  
8<)[+ @$0  
  public synchronized Integer getValue(){ - K}@Gp  
   // We can’t clone Integers so we makea copy this way. bLu6|YB  
   return new Integer (i.intValue()); '4HwS$mW3  
  } R9S7p)B  
} Z i7(lG  
sPkT>q  
,2H5CFX/  
OD>-^W t;%  
  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。 ; {I{X}b  
rVQ:7\=Z  
u9mMkzgSkP  
/CKkT.Le  
  方法getValue()应该被写为: xkUsZ*X8B  
a+\ Gz  
~<v`&Gm?"  
M%&`&{  
public synchronized Integer getValue(){ }kL% l  
// ’i’ is immutable, so it is safe to return it instead of a copy. q7 Uu 8JXF  
return i; ?Dd2k%o  
} hpWAQ#%oHm  
H W.S~eLw*  
qK|r+}g|&  
A!iH g__/t  
  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括: gADt%K2 #Z  
$6fHY\i#R  
L=Dx$#|  
MrOW&7  
  ?Boolean .&r] ?O  
   ?Byte n0Ze9W+<  
   ?Character e"^1- U\  
   ?Class @!%<JZEz3  
   ?Double e yTYg  
   ?Float Gjy'30IF  
   ?Integer Duptles  
   ?Long 'O\K Wj{  
   ?Short Dvd.Q/f  
   ?String ^Po\:x%o  
   ?大部分的Exception的子类 k qwS/s  
IeN!nK-  
:Oq!.uO  
{j:{wW.  
 四、常见错误4# :自编代码来拷贝数组 gvA&F |4  
Htsa<t F  
(CZRX9TT1  
lzS"NHs<g(  
  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成: kf"cd 1  
Vx* =  
cO(|>&tJ  
J=4S\0Z*  
public class Example{ *WX6C("M  
  private int[] copy; +#&2*nY  
  /*** Save a copy of ’data’. ’data’ cannot be null.*/ )}WG`  
  public void saveCopy (int[] data){ wy) Frg  
   copy = new int[data.length]; %HYC-TF#  
   for (int i = 0; i < copy.length; ++i) I &{dan2  
    copy = data; ZP%^.wxC  
  } 5^* d4[&+  
} X/gh>MJJ<  
",Q\A I  
p' /$)klt  
>2VB.f  
  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是: CUxSmN2[  
6"_FjS3Sl  
o`RTvG Xk  
l[\[)X3$  
void saveCopy (int[] data){ 0dIJgKanGP  
  try{ |&RdOjw$u  
   copy = (int[])data.clone(); ,3fw"P$  
  }catch (CloneNotSupportedException e){ mGL%<4R,  
   // Can’t get here. 0JNG\ARC  
  } d6hWmZVC  
} P\N`E?lJL  
g-*@I`k[  
3QV|@5L`[  
II~D66 bF  
  如果你经常克隆数组,编写如下的一个工具方法会是个好主意: sF|<m)Kt{W  
zhN'@Wj'_  
Iupk+x>  
yRvq3>mU  
static int[] cloneArray (int[] data){ OSkZW  
  try{ (#Y2H  
   return(int[])data.clone(); R_@yj]%H=  
  }catch(CloneNotSupportedException e){ (5G^"Srw  
   // Can’t get here. %f{kT<XHu  
  } +;cw<9%0  
} Yj0Ss{Ep  
H3a}`3}U  
{ Ja#pt  
aNXu"US+Sp  
  这样的话,我们的saveCopy看起来就更简洁了: %X[|7D-  
_Dk;U*2  
zD)2af  
hsfVKlw-  
void saveCopy (int[] data){ 1RcaE!\p  
  copy = cloneArray ( data); ?"sk"{  
} rvr Ok  
dnNc,l&g  
E}1[&  
u.!Pda  
  五、常见错误5#:拷贝错误的数据 B~~rLo:a  
IS!B$  
-{L[Wt{1  
)cOm\^,  
  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差: k# &y  
0c:CA>F  
%C)U F  
;o8cfD.z  
import java.awt.Dimension; Wigm`A=,r  
/*** Example class. The height and width values should never * be 6dg[   
negative. */ jRq>Sz{8  
public class Example{ QiwZk<rb  
  static final public int TOTAL_VALUES = 10; 0Y"==g+ >f  
  private Dimension[] d = new Dimension[TOTAL_VALUES]; y4envjl 0  
  public Example (){ } Ye1P5+W(  
b5H[~8mf  
  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */ *M KVm)Iv  
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{ jkPXkysm  
   if (height < 0 || width < 0) nGyY`wt&Rg  
    throw new IllegalArgumentException(); |4-Ey! P  
    if (d[index] == null) .>H7i`1D`  
     d[index] = new Dimension(); [ 9$>N  
     d[index].height = height; )DoY*'Cl  
     d[index].width = width; f#5mX&j  
  } ."=Bx2  
  public synchronized Dimension[] getValues() ^ *m;![$[  
   throws CloneNotSupportedException{ m4kmJaM  
    return (Dimension[])d.clone(); eBBh/=Zc  
  } hS<x+|'l  
} LR`/pet  
9fe~Q%x=u  
/~AajLxu3W  
3 vE;s"/  
  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为: ^yBx.GrQc  
dyu~T{  
AMtFOXx%I  
a:@Eg;aN*O  
public synchronized Dimension[] getValues() throws CloneNotSupportedException{ H#Vs3*VK  
  Dimension[] copy = (Dimension[])d.clone(); HgG"9WBe%  
  for (int i = 0; i < copy.length; ++i){ <4Ujk8Zj  
   // NOTE: Dimension isn’t cloneable. hX[hR  
   if (d != null) ;.m"y-  
    copy = new Dimension (d.height, d.width); =FdS'<GM  
  } VE{t]>*-u  
  return copy; /A{znE  
} ]Ub?Wo7F?  
O^`Y>>a  
=1LrU$\  
ucB<  
  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示: i) :Q{[D  
1uS _]59=  
?;}2 Z)  
NlWIb2,  
public void store (int[] data) throws CloneNotSupportedException{ BcA:M\dK%  
  this.data = (int[])data.clone(); V =1Y&y  
  // OK et)A$'Q  
} n?:s/6tP  
?$8OVq.w,  
pGUrYik4  
o- GHAQ  
  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法: .}]5y4UQ.  
N{oD1%  
opdi5 e)jK  
lZ E x0  
public void wrongStore (int[][] data) throws CloneNotSupportedException{ dp-8,Seu  
  this.data = (int[][])data.clone(); // Not OK! 8'/vW~f  
} >_tn7Z0 L  
public void rightStore (int[][] data){ WEWNFTI  
  // OK! CCQ<.iCU  
  this.data = (int[][])data.clone(); @K2q*d  
  for (int i = 0; i < data.length; ++i){ F&I^bkvh  
   if (data != null) 6fQQKM@a|  
    this.data = (int[])data.clone(); Smg,1,=  
  } M%yT?R+  
} )1&[uE#L  
* UBU?  
8 w^i  
dN;C-XF3s  
62a{Ggs{  
   六、常见错误6#:检查new 操作的结果是否为null Tig`4d-%  
yv]/A<gP+  
mI9~\k&9  
EXoT$Wt{$  
  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为: jSpmE  
57D /"  
8T7[/"hi\  
_Su$oOy(Ea  
Integer i = new Integer (400); n1!0KOu/N  
if (i == null) "J3@Z,qW  
throw new NullPointerException(); ~S#Le  
2["bS++?  
Z[Uz~W6M]  
*)H?d  
  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。 qAHQZKk  
>>F E?@  
$x#Y\dpS  
&mp=jGR  
  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。  b-yfBO  
YN.rj-;^+  
 七、常见错误7#:用== 替代.equals ~bg?V0  
+Ae4LeVzc  
  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示: gb( a`  
2LEf"FH0~  
J *5 )g  
yM=% a3  
int x = 4; K;k&w; j  
int y = 5; q% 2cx@c  
if (x == y) \l"1Io=  
  System.out.println ("Hi"); x?y)a9&Hm  
// This ’if’ test won’t compile. -5\hZ!!J2  
if (x.equals (y)) bb}|"m .  
  System.out.println ("Hi"); Uf 1i "VY  
v#8{pr  
IlN9IF\9L  
H?m9HBDpn  
  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。 PB(mUD2"r  
#kR8v[Z  
/YKg.DA|  
5k)/SAU0  
  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。 [>U'P1@ql  
[D=ba=r0X  
dmgoVF_qR  
iOYC1QFi?  
  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。 < HlS0J9  
ifHQ2Ug 9  
)! Jo7SR  
@A GM=v  
  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。 ds}:t.3}6  
\7 n ;c   
ABuK`(f.  
z/dpnGX  
  八、常见错误8#: 混淆原子操作和非原子操作 7_xQa$U[  
[K1RP.  
3x@t7B  
b= amd*  
  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的: h mC. 5mY  
cv})^E$x  
X0wvOs:  
TmZ sC5  
public class Example{ Lq : !?)I  
  private int value; // More code here... l|TiUjs  
  public void set (int x){ C^oj/} ^  
   // NOTE: No synchronized keyword 4HG;v|Cp  
   this.value = x; r {R879  
  } 9f1,E98w_  
} "jS @ug  
g-)izPX  
wl2P^Pj  
82d~>i%T  
  不过,这个保证仅限于读和写,下面的代码不是线程安全的: h \dq]yOl  
+||y/}1  
mu`:@7+Yp  
Vcjmj  
public void increment (){ PggjuPPh  
  // This is effectively two or three instructions: CCx_|>  
  // 1) Read current setting of ’value’. O\0]o!  
  // 2) Increment that setting. noI>Fw<V  
  // 3) Write the new setting back. UaA1HZ1  
  ++this.value; ?D[9-K4Vn  
} %?  87#|  
eI99itDQ  
f0Wbc\L[  
rx[l7F q  
  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码: DD$YMM  
'Z ;8-1M?O  
dZMf5=tb  
PqUjBP\  
public synchronized void increment (){ Uphme8SX  
  ++this.value; my1FW,3  
} *n]f)Jc  
+hiskV@v  
yn`P:[v  
#^FDG1=  
  九、常见错误9#:在catch 块中作清除工作 |-+IF,j  
m+m2<|%x  
Pk{eGG<F$  
76 ] X  
  一段在catch块中作清除工作的代码如下所示: $f>h_8cla  
H<N$z 3k  
{~*^jS']5  
Sao4MkSz[]  
OutputStream os = null; <A~a|A-QFR  
try{ GycW3tc]_&  
  os = new OutputStream (); |J:|56kVZq  
  // Do something with os here. =5l20 Um  
  os.close(); &d,!^9  
}catch (Exception e){ !{S& "  
  if (os != null) qz_'v{uAj  
  os.close(); Sj(5xa[  
} Wxeg(L}E  
:)*+ aS"  
$:l>g)c  
(8Ptuh6\\2  
  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题: .m gm1zz  
k.5(d.*(  
`>1XL2  
Bz+zEXBC  
  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。 v}`{OE:-J  
<@4 48,9&  
_Jt  
V9:Jz Q=?`  
  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。 -xVp}RLT  
qdCcMcGt  
$S6%a9m   
Y?'Krw `  
  3.close()可能会抛出异常。 uyqu n@q  
wJAJ /  
fHV%.25  
o)]mJb~XG-  
  上面代码的一个更优版本为: w\}@+w3b~  
d8C44q+ds  
`qs[a}%'>"  
eJ ;a}{ 4%  
OutputStream os = null; 63PSYj(y  
try{ $p;<1+!  
  os = new OutputStream (); G4;5$YGG  
  // Do something with os here. &l8eljg  
}finally{ -_?U/k(Hi  
  if (os != null) N|5fkx<d^  
   os.close(); o|cx?  
} |L<p90  
(ebC80M  
^:cc3wt'3[  
fMm.V=/+  
  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。 t6C2DHh7$  
Fm#`}K_  
e0:[,aF`  
ZBuh(be  
  十、常见错误10#: 增加不必要的catch 块 SNOML7pd  
dJ(<zz+;b  
oAprM Z 7Y  
Y a/+|mv  
  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。 IY"+hHt  
L/c`t7  
= 9!|%j  
/g)(  
  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。 (Go1@;5I  
zL!}YR@&u"  
S Rb-eDk'  
wLa8&E[  
  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出: %'w?fqk  
-T=sY/O  
OPuty/^!Gw  
<~teD[1k"  
try{ 6Z#$(oC  
  // Nifty code here JKTn  
}catch(Exception e){ 6PyW(i(bs  
  throw e; 3lsfT-|Wt&  
}finally{ tjg?zlj  
  // Cleanup code here 83!{?EPE  
} ('z:XW96  
6]|-%  
3|Y2BA d  
:|%1i>O  
  不必要的catch块被删除后,上面的代码就缩短为: v\2- %  
Y[0mTL4IO  
0.kC|  
1|dXbyUd  
try{ 7 MS-Gs|  
  // Nifty code here e<$s~ UXv  
}finally{ Aa+<4 R  
  // Cleanup code here ] ^53Qbrv  
} 5t,X;  
zJ30ZY:  
_0|@B8!J?  
* hmoi  
  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法 ~e _  
i ?%_P u  
O>"T*   
BA8g[T A7K  
  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。 F~sUfqiJ'  
vA2>&YDFX  
SUDvKP  
lhX4 MB"  
  小结 Qp9QS yMs}  
LP'~7FG  
eD|"?@cE  
~t1?oJ  
  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。 $ {e5Ka  
'cqY-64CJZ  
 
评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
描述
快速回复

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