歡迎您光臨本站 註冊首頁

2012年JAVA認證:Java代碼常見錯誤分析

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

每一個程序員在編寫代碼的過程中都免不了出現錯誤或是小的失誤,這些小的錯誤和失誤往往是的程序員還得返工.那麼,如何才能盡量避免這些錯誤的發生呢?筆者總結只有在日常的編寫代碼中總結出經驗,在這篇文章中,筆者列出了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次圍觀

http://coctec.com/docs/java/show-post-59853.html