編寫高效的JAVA程式--編程規範

來源:互聯網
上載者:User
基本篇

 

本文介紹的JAVA規則的說明分為5個層級,層級1是最基本也是最重要的層級,在今後將陸續寫出其他的規則。遵守了這些規則可以提高程式的效率、使代碼有更好的可讀性等。


 

(1) 避免使用NEW關鍵字來建立String對象。
把一個String常量copy到String 對象中通常是多餘、浪費時間的
Public class test{
Public void method(){
System.out.print (str);
}
private String str = new String ("1"); //這裡建立對象是完全沒有必要的
private String str2=”2” //正確的應該如此
}
參考:Joshua Bloch: "Effective Java - Programming Language Guide"

(2) 避免使用不必要的嵌套。
過多的嵌套會使你的代碼複雜化,減弱可讀性。
Public class test {
String add (){
Int c=(a=a+b)+b; //過於複雜
Return c
}

}

參考:http://java.sun.com/docs/codeconv/html/CodeConventions.doc9.html#177

(3) 避免在同一行聲明不同類型的多個變數
這樣可以使程式更加清晰,避免混亂
private int index, index1[];
正確的應該如此:
private int index;
private int index1[];

參考:http://java.sun.com/docs/codeconv/html/CodeConventions.doc5.html#2992

(4) 在每一行裡寫一條語句
這條規則不包括for語句:比如:'for (int i = 0; i < 10; i++) x--;’可以增加代碼的可讀性。
public class OSPL {
int method (int a, int b) {
int i = a + b; return i; // 可讀性不強
}
正確的:
public class OSPLFixed {
int method (int a, int b) {
int i = a + b;
return i;
}
}
參考:Section 7.1 of http://java.sun.com/docs/codeconv/html/CodeConventions.doc6.html#431

(5)經常從finalize ()中調用super.finalize ()
這裡的finalize ()是java在進行垃圾收集的時候調用的,和finally不一樣。如果你的父類沒有定義finally()的話,你也應該調用。這裡有兩個原因:(1)在不改變代碼的情況下能夠將父類的finally方法加到你的類中。 (2)以後你會養成習慣調用父類的finally方法,即使父類沒有定義finally方法的時候。
正確的方法應該如此:
public class parentFinalize {
protected void finalize () throws Throwable {
super.finalize(); // FIXED
}

參考:"The Java Programming Language" by Ken Arnold and James Gosling, page 49.

(6) 不要在finalize ()中登出listeners
不要再finalize ()方法中中登出listeners,finalize ()只有再沒有對象引用的時候調用,如果listeners從finalize()方法中去除了,被finalize的對象將不會在垃圾收集中去除。
public void finalize () throws Throwable {
bButton.removeActionListener (act);
}

(7) 不要顯式的調用finalize ()方法
雖然顯式的調用這個方法可以使你確保你的調用,但是當這個方法收集了以後垃圾收集會再收集一次。

public class T7 { public void finalize() throws Throwable { close_resources (); super.finalize (); } public void close_resources() {}}class Test { void cleanup () throws Throwable { t71.finalize(); // 調用 t71 = null; } private t71 = new T7 ();}

對於這樣的調用我們應該自己建立一個釋放的方法,做最初finalize ()所作的事情,當你每次想顯式的調用finalize ()的時候實際上調用了釋放方法。然後再使用一個判斷欄位來確保這個方法只執行一次,以後再調用就沒關係了。

public class T7 { public synchronized void release () throws Throwable{ if (!_released) { close_resources (); // do what the old 'finalize ()' did _released = true; } } public void finalize () throws Throwable { release (); super.finalize (); } public void close_resources() {} private boolean _released = false;}class TestFixed { void closeTest () throws Throwable { t71 .release (); // FIXED t71 = null; } private T7 t71 = new T7 ();}

參考:Nigel Warren, Philip Bishop: "Java in Practice - Design Styles and Idioms
for Effective Java". Addison-Wesley, 1999. pp.110-111

(8)不要使用不推薦的API
盡量使用JDK1.3推薦的API。在類和方法或者java組件裡有很多方法是陳舊的或者是可以選擇的。有一些方法SUN用了"deprecated“標記。最好不要使用例如:
private List t_list = new List ();
t_list.addItem (str);
如果查一下javadoc的話,會發現建議用add()來代替addItem()。
參考:http://java.sun.com/j2se/1.3/docs/api/index.html

(9)為所有序列化的類建立一個'serialVersionUID'
可以避免從你各種不同的類破壞序列的相容性。如果你不特別制訂一個UID的話,那麼系統為自動產生一個UID(根據類的內容)。如果UID在你新版本的類中改變了,即使那個被序列化的類沒改變,你也不能還原序列化老的版本了。

public class DUID implements java.io.Serializable { public void method () {}}
在裡面加一個UID,當這個類的序列化形式改變的時候,你也改變這個UID就可以了。

public class DUIDFixed implements java.io.Serializable { public void method () {} private static final long serialVersionUID = 1; }

參考:Joshua Bloch: "Effective Java - Programming Language Guide"
Addison Wesley, 2001, pp. 223

(10)對於private常量的定義
比較好的做法是對於這樣的常量,加上final標記,這樣的常量從初始化到最後結束值都不會改變。
private int size = 5;
改變後的做法是:
private final int size = 5;

(11)避免把方法本地變數和參數定義成和類變數相同的名字。
這樣容易引起混擾,建議把任何的變數字都定義成唯一的。這樣看來,SCJP裡的那些題目在現實中就用不到了:)

public void method (int j) { final int i = 5; // VIOLATION } private int j = 2;

建議:
public void method (int j1) { final int i = 5; // VIOLATION } private int j = 2;

中級篇

 

本文介紹的JAVA規則的說明分為3個主要層級,中級是平時開發用的比較多的層級,在今後將陸續寫出其他的規則。遵守了這些規則可以提高程式的效率、使代碼又更好的可讀性等。
(1) 在finally方法裡關掉input或者output 資源
再方法體裡面定義了input或者output流的話,需要在finally裡面把它關掉。
以下這幾種調用不需要遵守這條規則,因為colse()方法不起作用:)
java.io.StringWriter java.io.ByteArrayOutputStream java.io.ByteArrayInputStream
如果再方法返回的時候沒有調用close()方法來釋放input()和output()的資源的話,會導致一個系統資源泄漏。而且在任何情況下都要確定在返回全調用了close() 方法,包括出現異常的時候。所以需要在finally方法裡面加入這個方法。這樣就保證了在任何情況下都會關閉資源。
錯誤樣本:
public class CIO {
public void method (java.io.File f) {
java.io.FileInputStream fis = null;
try {
fis = new java.io.FileInputStream (f);
fis.read ();
fis.close ();
} catch (java.io.FileNotFoundException e1) {
System.out.println("File not found");
} catch (java.io.IOException e2) {
System.out.println("I/O Exception");
}
// 如果出現異常,這裡就不能保證關閉資源。
}
}
修正後的代碼:
public class CIOFixed {
public void method (java.io.File f) {
java.io.FileInputStream fis = null;
try {
fis = new java.io.FileInputStream (f);
fis.read ();
} catch (java.io.FileNotFoundException e1) {
System.out.println("File not found");
} catch (java.io.IOException e2) {
System.out.println("I/O Exception");
} finally {
if (fis != null) {
try {
fis.close ();
} catch (java.io.IOException e) {
System.out.println("I/O Exception");
}
}
}
}
}


 

(2) else的注意問題.
一般總認為如果if語句只有一句的話,那麼{}就是可要可不要的了。可是如果if有else嵌套的話,就不一樣了,{}是必需的
錯誤樣本:
if (i < 5)
if (i < 2)
i++;
else
i--;
修改後:
if (i < 5) {
if (i < 2)
i++;
}
else {
i--;
}

(3) 不要再catch()塊裡什麼代碼也不放
在catch()塊裡面放入一些錯誤處理代碼是一個好的習慣。但是如果catch()裡面有有關javadoc 的代碼,那也是可以的。
錯誤樣本:
try {
System.in.read ();
} catch (java.io.IOException e) {
// 錯誤
}

正確:
try {
System.in.read ();
} catch (java.io.IOException e) {
System.out.println("Descriptive error");
}
參考:Joshua Bloch: "Effective Java - Programming Language Guide".
Addison-Wesley, 2001, pp. 187

(4) 不要在if條件裡面附值
如果這樣做的話,系統會報告錯誤。在java的很多條件聲明裡面用附值是很不明智的,而且系統也會報告錯誤。很容易引起異常。遵守這條規者能夠使維護簡單,避免不一致。
錯誤樣本:
if (b = true)
正確的:
if (b == true)
參考:Section 10.4 of http://java.sun.com/docs/codeconv/html/CodeConventions.doc9.html#547

(5) for語句需要迴圈體。
如果沒有{}的話,for語句只會執行一次!
錯誤樣本:
for (i = 0; i < 10; i++) ;
System.out.println (i);
這裡print() 只會執行一次。
正確:
for (i = 0; i < 10; i++) { // FIXED
System.out.println (i);
}

(5) 不要把方法定義成main().
在java裡,main()方法是一個特別的方法。所以在自己定義方法的時候不要定義這樣的名字,以免引起混擾。

(6)不要直接或者間接的定義'Error'和'Throwable'的子類
'java.lang.Error'只在JVM出現反常的時候覆蓋這個方法,如果你定義了直接或者不直接的類繼承了類'Error',也就指出了這個錯誤是JVM內部的,而不是這個類的。所以對於java編譯器來說是不可見的,這樣就不能檢查錯誤的異常處理了。
'java.lang.Throwable'是'java.lang.Exception'和'java.lang.Error'的上級類,使用者如果象定義異常類的話應該繼承'java.lang.Exception'。
錯誤樣本:public class ABC extends Error
正確:public class ABC extends Exception

(7)有關"switch"語句裡面的"case"問題
最好在每一個 “case”裡都定義一個”return”或者“break”來控制不要走到下面的 “case”裡去。如果一個”case”語句在代碼的最後沒有一個”break”或者”return”句,程式就會走到下一個”case”。如果這個”case”是最後一個的話,那就沒什麼問題,如果後面還有”case” 的話,看起來就不太安全了。
錯誤樣本:
switch (i) {
case 1:
x = 10;
break;
case 2:
x = 20;
default:
a = 40;
break;
正確:
switch (i) {
case 1:
x = 10;
break;
case 2: // VIOLATION
x = 20;
break;
default:
x = 40;
break;

(8)建議不要使用'System.getenv ()'
不建議使用'System.getenv ()',這個方法看起來很好用,不過並不是所有的系統都有環境變數的。不用這個方法也可能帶來一些不方便。
錯誤樣本:
void method (String name) {
System.getenv (name); // 可以用其他方法來代替
}
如果不用這個方法,我們可以用其它的方法來代替。比如:'System.getProperty ()’,'getTypeName ()'等,這也可以找到java的系統屬性。
參考:David Flanagan: "Java in a Nutshell". O'Reilly
November, 1999: Third Edition, pp.190-192

(9)不要使用’/n’或者'/r'來分行
這兩個標記看來很普遍,特別是’/n’。我們經常用來作為分行用。但是不同的系統用不同的分行字元,所以這些字元在某些意義上違背了java的平台無關性。
錯誤樣本:
System.out.println("Hello/n" + name);
我們可以用其它的一些方法來代替,比如println(),這個方法在不同的系統平台上都起到相同的作用。後者推薦大家用這個方法:System.getProperty("line.separator")
參考:David Flanagan: "Java in a Nutshell". O'Reilly,
November 1999: Third Edition, pp. 191-192

(10) 使所有的內部類"private".
Java允許一個類包含另外一個類,帶是Java byte code沒有這個概念。類被編譯器解釋成package-private類。從更深的程度來說,包含類的任何內部私人對象能被內部類訪問的也能被同一個包內的其他類訪問。
錯誤樣本:
public class INNER {
class INNER_Class {
void setValue(int i) {
_value = i; // 現在包就可以訪問了
}
}
private int _value;
}
所以需要加上private class INNER_Class
參考:Statically Scanning Java Code: Finding Security Vulnerabilities.
John Viega, Gary McGraw, Tom Mutdosch, and Edward W. Felten
IEEE SOFTWARE September/October 2000

(11)不要使介面序列化
如果一個位元組數組包含了一個被序列化的對象。攻擊者就能讀到這個對象的內部狀態合欄位(包括private的)。
錯誤樣本:
public interface sample extends java.io.Serializable

開發篇

本文介紹的JAVA規則的說明分為3個主要層級,本篇拋棄了平時開發中很少遇到的情況,那些用得比較少的以後再進階篇裡面出現。並有六個有用的國際軟體開發重要注意的有關String的問題,遵守了這些規則可以提高程式的效率、使代碼又更好的可讀性等。
(1) 如果有JDBC串連沒有關掉的話,需要在"finally"方法中關掉
如果資料庫連接失敗或者是沒有釋放串連,看上去無關緊要。但是其他的使用者就需要用更長的時間等待串連,這樣資料庫利用效率就會下降。確保你的代碼在任何情況下,包括出錯或者程式異常終止的情況下都釋放資料庫連接。在"finally"方法中關掉串連,就可以確保這一點。
錯誤樣本:
try {
Statement stmt = con.createStatement();
} catch(SQLException e) {
e.printStackTrace();
}
正確樣本:
try {
Statement stmt = con.createStatement();
} finally {
if (con != null && !con.isClosed()) {
con.close();
}
}

 

 

(2) 盡量避免使用'Thread.resume ()', 'Thread.stop ()', 'Thread.suspend ()'和 'Runtime.runFinalizersOnExit ()' 方法。
這些方法在平時的開發或者是教科書裡面也有用到過,但是這些方法會導致四鎖的傾向。一下有充足的資料來說明為什麼不建議用上述方法。
參考:1."java.lang.Thread" in the JDK API documentation
2.http://java.sun.com/j2se/1.3/docs/guide/misc/threadPrimitiveDeprecation.html
3.Paul Hyde: "Java Thread Programming"
Sams, ISBN: 0-672-31585-8 pp. 270

(3) 在表示長整常量的時候,用L來代替l.
因為l很容易和1混一起。
錯誤樣本:
long temp = 23434l;
正確樣本:
long temp = 23434L;
參考:Ken Arnold, James Gosling: "The Java Programming Language Second Edition"Addison Wesley, 1997, pp.108

(4) 最好在jsp開頭寫一條注釋
在 jsp檔案頭上面寫一條注釋,這樣可以協助別人來理解你的代碼。這條規則不僅適用於jsp,更是用於任何開發的文檔。
正確樣本:<%-- JSP comment --%>

(5)明確的初始化一個構造類裡面的所有的欄位
因為沒有初始化的欄位會是一個潛在的bug,所以最好初始化類裡面的所有的欄位。特別是靜態欄位,最好在一開始就分配一個初始值
錯誤樣本:
public class CSI {
public CSI () {
this (12);
k = 0;
}

public CSI (int val) {
j = val;
}

private int i = 5;
private int j;
private int k;
}

正確樣本:
public class CSIFixed {
public CSIFixed () {
this (12);
}

public CSIFixed (int val) {
j = val;
k = 0;
}

private int i = 5;
private int j;
private int k;
}
參考:http://www.ambysoft.com/javaCodingStandards.pdf

(5) 國際化開發建議:邏輯操作符不要再一個單個的字元的前面或者後面
一個單個字元的前後不要用邏輯操作符,如果代碼要在一個國家環境中啟動並執行話。我們可以使用字元比較方法,這些方法使用統一字元比較標準來定義字元的屬性的。
錯誤樣本:public class CLO {
public boolean isLetter (char ch) {
boolean _isLetter = ( ch >= 'a' && ch <= 'z') //錯誤
|| (ch >= 'A' && ch <= 'Z');
return _isLetter;
}
}

正確樣本:
public class CLOFixed {
public boolean isLetter (char ch) {
boolean _isLetter = Character.isLetter(ch);
return _isLetter;
}
}
參考: http://java.sun.com/docs/books/tutorial/i18n/intro/checklist.html
更多的字元比較方法請參考:http://java.sun.com/docs/books/tutorial/i18n/text/charintro.html

(6) 國際化開發建議:不要對日期對象使用'Date.toString ()'
不要使用'Date.toString ()'方法,日期格式對於地區和語言不同的國家來說是不一樣的,務必不要使用。
錯誤樣本:'DateFormat'類提供了一個預定義的格式類型來指定本地的格式。
public void printToday () {
Date today = new Date ();
String todayStr = today.toString ();
System.out.println (todayStr);
}
正確樣本:
public void printToday () {
Locale currentLocale = Locale.getDefault ();
DateFormat dateFormatter = DateFormat.getDateInstance (
DateFormat.DEFAULT, currentLocale);
Date today = new Date ();
String todayStr = dateFormatter.format (today);
System.out.println (todayStr);
}
參考:http://java.sun.com/docs/books/tutorial/i18n/intro/checklist.html
http://java.sun.com/docs/books/tutorial/i18n/format/dateFormat.html

(7) 國際化開發建議:不要對數字變數使用'toString ()'方法
在全球化的開發中,不要對數字變數使用'toString ()'方法,對於java.lang.Number的任何子類都適用。包括:BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, and Short.對於這樣的情況,java裡也與定義了"NumberFormat"方法來格式化。
錯誤樣本:
public class NTS {
public void method (Double amount) {
String amountStr = amount.toString ();
System.out.println (amountStr);
}
}
正確樣本:
public class NTSFixed {
public void method (Double amount) {
Locale currentLocale = Locale.getDefault ();
NumberFormat numberFormatter =
NumberFormat.getNumberInstance (currentLocale);
String amountStr = numberFormatter.format (amount); //
System.out.println (amountStr + ' ' + currentLocale.toString ());
}
}
參考:http://java.sun.com/docs/books/tutorial/i18n/intro/checklist.html
http://java.sun.com/docs/books/tutorial/i18n/format/numberFormat.html

(8) 國際化開發建議:不要使用'String.equals ()'方法
建議不要使用'String.equals ()'方法,因為在統一字元比較標準中不一定按照相關的順序來比較。'Collator'提供的預定義整理規則來排序string,Collator類調用'getInstance ()'方法,一般來說,可以為預設的本地建立一個Collator。例如:Collator myCollator = Collator.getInstance ();建立Collator的時候你也可以指定一個特殊的locale。例如:Collator myFrenchCollator = Collator.getInstance (Locale.FRENCH);然後就可以調用'Collator.compare ()'來執行一個本地的字元比較myCollator.compare (s1,s2);從這裡可以瞭解更多的有關Collator類的資訊:http://java.sun.com/docs/books/tutorial/i18n/text/collationintro.html

 

錯誤樣本:
public class SE {
public boolean compstr (String s1, String s2) {
boolean b = (s1.equals (s2));
return b;
}
}
正確樣本:
public class SEFixed {
public boolean compstr (String s1, String s2) {
Collator myCollator = Collator.getInstance ();
boolean b = (myCollator.compare(s1,s2) == 0);
return b;
}
}

參考:http://java.sun.com/docs/books/tutorial/i18n/intro/checklist.html
http://java.sun.com/docs/books/tutorial/i18n/text/locale.html

(9) 國際化開發建議:不要使用'StringTokenizer()'方法
錯誤樣本:StringTokenizer st = new StringTokenizer(str);
可以從這裡得到更多的資訊:‘
參考:http://java.sun.com/docs/books/tutorial/i18n/intro/checklist.html

(10) 國際化開發建議:不要使用'Time.toString ()'方法
因為時間的格式各個國家也不一樣。如果你使用日期格式類,你的應用就能夠在世界上各個地方正確的顯示時間和日期了。首先,用'getTimeInstance ()'方法建立一個formatter。然後,調用'format ()'方法。
錯誤樣本:
public class TTS {
public void printTime (Time t1) {
String timeStr = t1.toString ();
System.out.println (timeStr);
}
}
正確樣本:
import java.sql.Time;
import java.text.DateFormat;
import java.util.Locale;

public class TTSFixed {
public void printTime (Time t1) {
DateFormat timeFormatter = DateFormat.getTimeInstance(
DateFormat.DEFAULT, Locale.getDefault ());
String timeStr = timeFormatter.format(t1);
System.out.println (timeStr);
}
}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.