每一個程序員在編寫代碼的過程中都免不了出現錯誤或是小的失誤,這些小的錯誤和失誤往往是的程序員還得返工.那麼,如何才能盡量避免這些錯誤的發生呢?筆者總結只有在日常的編寫代碼中總結出經驗,在這篇文章中,筆者列出了10個Java編程中常見的錯誤,你可以把這些錯誤添加到你的代碼審查的檢查列表中,這樣在經過代碼審查后,你可以確信你的代碼中不再存在這類錯誤了.
一、常見錯誤1:多次拷貝字元串
測試所不能發現的一個錯誤是生成不可變(immutable)對象的多份拷貝.不可變對象是不可改變的,因此不需要拷貝它.最常用的不可變對象是String.
如果你必須改變一個String對象的內容,你應該使用StringBuffer.下面的代碼會正常工作:
String s = new String (「Text here」);
但是,這段代碼性能差,沒有必要這麼複雜.你還可以用以下的方式來重寫上面的代碼:
String temp = 「Text here」; String s = new String (temp);
但是這段代碼包含額外的String,並非完全必要.更好的代碼為:
String s = 「Text here」; 二、常見錯誤2:沒有克隆(clone)返回的對象
封裝(encapsulation)是面向對象編程的重要概念.不幸的是,Java為不小心打破封裝提供了方便Java允許返回私有數據的引用(reference).下面的代碼揭示了這一點:
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類保證了它所存儲的border=「1」 Height和width值永遠非負數,試圖使用setValues()方法來設置負值會觸發異常.不幸的是,由於getValues()返回d的引用,而不是d的拷貝,你可以編寫如下的破壞性代碼:
Example ex = new Example();
Dimension d = ex.getValues();
d.border=「1」 height = -5; d. width = -10;
現在,Example對象擁有負值了!如果getValues() 的調用者永遠也不設置返回的Dimension對象的width 和border=「1」 Height值,那麼僅憑測試是不可能檢測到這類的錯誤.
不幸的是,隨著時間的推移,客戶代碼可能會改變返回的Dimension對象的值,這個時候,追尋錯誤的根源是件枯燥且費時的事情,尤其是在多線程環境中.
更好的方式是讓getValues()返回拷貝:
public synchronized Dimension getValues()…{
return new Dimension (d.x, d.y); }
現在,Example對象的內部狀態就安全了.調用者可以根據需要改變它所得到的拷貝的狀態,但是要修改Example對象的內部狀態,必須通過setValues()才可以. 三、常見錯誤3:不必要的克隆
我們現在知道了get方法應該返回內部數據對象的拷貝,而不是引用.但是,事情沒有絕對: /** *//*** 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());
} }
這段代碼是安全的,但是就象在錯誤1#那樣,又作了多餘的工作.Integer對象,就象String對象那樣,一旦被創建就是不可變的.因此,返回內部Integer對象,而不是它的拷貝,也是安全的.
方法getValue()應該被寫為:
public synchronized Integer getValue()…{
// 」i「 is immutable, so it is safe to return it instead of a copy.
return i; }
Java程序比C 程序包含更多的不可變對象.JDK 所提供的若干不可變類包括:
·Boolean
·Byte
·Character
·Class
·Double
·Float
·Integer
·Long
·Short
·String ·大部分的Exception的子類
四、常見錯誤4:自編代碼來拷貝數組
Java允許你克隆數組,但是開發者通常會錯誤地編寫如下的代碼,問題在於如下的循環用三行做的事情,如果採用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];
} }
這段代碼是正確的,但卻不必要地複雜.saveCopy()的一個更好的實現是:
void saveCopy (int[] data)…{
try…{
copy = (int[])data.clone();
}catch (CloneNotSupportedException e)…{
// Can」t get here.
} }
如果你經常克隆數組,編寫如下的一個工具方法會是個好主意:
static int[] cloneArray (int[] data)…{
try…{
return(int[])data.clone();
}catch(CloneNotSupportedException e)…{
// Can「t get here.
} }
這樣的話,我們的saveCopy看起來就更簡潔了:
void saveCopy (int[] data)…{
copy = cloneArray ( data); } 五、常見錯誤5:拷貝錯誤的數據
有時候程序員知道必須返回一個拷貝,但是卻不小心拷貝了錯誤的數據.由於僅僅做了部分的數據拷貝工作,下面的代碼與程序員的意圖有偏差:
import java.awt.Dimension;
/** *//*** Example class. The border=」1「 Height and width values should never * be
negative. */
public class Example…{
static final public int TOTAL_VALUES = 10;
private Dimension[] d = new Dimension[TOTAL_VALUES];
public Example ()…{ }
/** *//*** Set border=」1「 Height and width. Both border=」1「 Height and width must be nonnegative * or an exception will be thrown. */
public synchronized void setValues (int index, int border=」1「 Height, int width) throws IllegalArgumentException…{
if (border=」1「 Height 《0 || width 《0)
throw new IllegalArgumentException();
if (d[index] == null)
d[index] = new Dimension();
d[index].border=」1「 height = border=」1「 Height;
d[index]. width = width;
}
public synchronized Dimension[] getValues()
throws CloneNotSupportedException…{
return (Dimension[])d.clone();
} }
這兒的問題在於getValues()方法僅僅克隆了數組,而沒有克隆數組中包含的Dimension對象,因此,雖然調用者無法改變內部的數組使其元素指向不同的Dimension對象,但是調用者卻可以改變內部的數組元素(也就是Dimension對象)的內容.方法getValues()的更好版本為:
public synchronized Dimension[] getValues() throws CloneNotSupportedException…{
Dimension[] copy = (Dimension[])d.clone();
for (int i = 0; i
// NOTE: Dimension isn」t cloneable.
if (d != null)
copy[i] = new Dimension (d[i].border=「1」 Height, d[i].width);
}
return copy; }
在克隆原子類型數據的多維數組的時候,也會犯類似的錯誤.原子類型包括int,float等.簡單的克隆int型的一維數組是正確的,如下所示: public void store (int[] data) throws CloneNotSupportedException…{
this.data = (int[])data.clone();
// OK }
拷貝int型的二維數組更複雜些.Java沒有int型的二維數組,因此一個int型的二維數組實際上是一個這樣的一維數組:它的類型為int[].簡單的克隆int[][]型的數組會犯與上面例子中getValues()方法第一版本同樣的錯誤,因此應該避免這麼做.下面的例子演示了在克隆int型二維數組時錯誤的和正確的做法: public void wrongStore (int[][] data) throws CloneNotSupportedException…{
this.data = (int[][])data.clone(); // Not OK!
}
public void rightStore (int[][] data)…{
// OK!
this.data = (int[][])data.clone();
for (int i = 0; i
if (data != null)
this.data[i] = (int[])data[i].clone();
} }
[火星人 ] 2012年JAVA認證:Java代碼常見錯誤分析已經有336次圍觀