每一個(gè)程序員在編寫代碼的過程中都免不了出現(xiàn)錯(cuò)誤或是小的失誤,這些小的錯(cuò)誤和失誤往往使得程序員還得返工。那么,如何才能盡量避免這些錯(cuò)誤的發(fā)生呢?筆者總結(jié)只有在日常的編寫代碼中總結(jié)出經(jīng)驗(yàn),在這篇文章中,筆者列出了10個(gè)Java編程中常見的錯(cuò)誤,你可以把這些錯(cuò)誤添加到你的代碼審查的檢查列表中,這樣在經(jīng)過代碼審查后,你可以確信你的代碼中不再存在這類錯(cuò)誤了。
一、常見錯(cuò)誤1:多次拷貝字符串
測試所不能發(fā)現(xiàn)的一個(gè)錯(cuò)誤是生成不可變(immutable)對(duì)象的多份拷貝。不可變對(duì)象是不可改變的,因此不需要拷貝它。最常用的不可變對(duì)象是String。
如果你必須改變一個(gè)String對(duì)象的內(nèi)容,你應(yīng)該使用StringBuffer。下面的代碼會(huì)正常工作:
String s = new String (“Text here”);
但是,這段代碼性能差,而且沒有必要這么復(fù)雜。你還可以用以下的方式來重寫上面的代碼:
String temp = “Text here”; String s = new String (temp);
但是這段代碼包含額外的String,并非完全必要。更好的代碼為:
String s = “Text here”; 二、常見錯(cuò)誤2:沒有克?。╟lone)返回的對(duì)象
封裝(encapsulation)是面向?qū)ο缶幊痰闹匾拍睢2恍业氖牵?a class="channel_keylink" target="_blank">Java為不小心打破封裝提供了方便Java允許返回私有數(shù)據(jù)的引用(reference)。下面的代碼揭示了這一點(diǎn):
import java.awt.Dimension;
/** *//***Example class.The x and y values should never*be negative.*/
public class Example…{
private Dimension d = new Dimension (0, 0);
public Example ()…{ }
/** *//*** Set border=“1” Height and width. Both border=“1” Height and width must be nonnegative * or an exception is thrown.*/
public synchronized void setValues (int border=“1” Height,int width) throws IllegalArgumentException…{
if (border=“1” Height 《0 || width 《0)
throw new IllegalArgumentException();
d.border=“1” height = border=“1” Height;
d. width = width;
}
public synchronized Dimension getValues()…{
// Ooops! Breaks encapsulation
return d;
} }
Example類保證了它所存儲(chǔ)的border=“1” Height和width值永遠(yuǎn)非負(fù)數(shù),試圖使用setValues()方法來設(shè)置負(fù)值會(huì)觸發(fā)異常。不幸的是,由于getValues()返回d的引用,而不是d的拷貝,你可以編寫如下的破壞性代碼:
Example ex = new Example();
Dimension d = ex.getValues();
d.border=“1” height = -5; d. width = -10;
現(xiàn)在,Example對(duì)象擁有負(fù)值了!如果getValues() 的調(diào)用者永遠(yuǎn)也不設(shè)置返回的Dimension對(duì)象的width 和border=“1” Height值,那么僅憑測試是不可能檢測到這類的錯(cuò)誤。
不幸的是,隨著時(shí)間的推移,客戶代碼可能會(huì)改變返回的Dimension對(duì)象的值,這個(gè)時(shí)候,追尋錯(cuò)誤的根源是件枯燥且費(fèi)時(shí)的事情,尤其是在多線程環(huán)境中。
更好的方式是讓getValues()返回拷貝:
public synchronized Dimension getValues()…{
return new Dimension (d.x, d.y); }
現(xiàn)在,Example對(duì)象的內(nèi)部狀態(tài)就安全了。調(diào)用者可以根據(jù)需要改變它所得到的拷貝的狀態(tài),但是要修改Example對(duì)象的內(nèi)部狀態(tài),必須通過setValues()才可以?! ∪?、常見錯(cuò)誤3:不必要的克隆
我們現(xiàn)在知道了get方法應(yīng)該返回內(nèi)部數(shù)據(jù)對(duì)象的拷貝,而不是引用。但是,事情沒有絕對(duì): /** *//*** Example class.The value should never * be negative.*/
public class Example…{
private Integer i = new Integer (0);
public Example ()…{ }
/** *//*** Set x. x must be nonnegative* or an exception will be thrown*/
public synchronized void setValues (int x) throws IllegalArgumentException…{
if (x 《0)
throw new IllegalArgumentException();
i = new Integer (x);
}
public synchronized Integer getValue()…{
// We can“t clone Integers so we makea copy this way.
return new Integer (i.intValue());
} }
這段代碼是安全的,但是就象在錯(cuò)誤1#那樣,又作了多余的工作。Integer對(duì)象,就象String對(duì)象那樣,一旦被創(chuàng)建就是不可變的。因此,返回內(nèi)部Integer對(duì)象,而不是它的拷貝,也是安全的。
方法getValue()應(yīng)該被寫為:
public synchronized Integer getValue()…{
// ”i“ is immutable, so it is safe to return it instead of a copy.
return i; }
Java程序比C++程序包含更多的不可變對(duì)象。JDK 所提供的若干不可變類包括:
?Boolean
?Byte
?Character
?Class
?Double
?Float
?Integer
?Long
?Short
?String ?大部分的Exception的子類
四、常見錯(cuò)誤4:自編代碼來拷貝數(shù)組
Java允許你克隆數(shù)組,但是開發(fā)者通常會(huì)錯(cuò)誤地編寫如下的代碼,問題在于如下的循環(huán)用三行做的事情,如果采用Object的clone方法用一行就可以完成:
public class Example…{
private int[] copy;
/** *//*** Save a copy of ”data“。 ”data“ cannot be null.*/
public void saveCopy (int[] data)…{
copy = new int[data.length];
for (int i = 0; i
copy[i] = data[i];
} }
這段代碼是正確的,但卻不必要地復(fù)雜。saveCopy()的一個(gè)更好的實(shí)現(xiàn)是:
void saveCopy (int[] data)…{
try…{
copy = (int[])data.clone();
}catch (CloneNotSupportedException e)…{
// Can”t get here.
} }



