代碼編程規範之所以重要是因爲:
- 軟件生命週期中的80%時間是軟件維護期;
- 幾乎沒有任何軟件在其整個生命週期中一直由它的原作者來負責維護;
- 代碼編程規範能夠增加軟件的可讀性,使得軟件工程師更快更準確地理解新代碼
- 編程規範能提高軟件的封裝性;
後面文中將深入討論結構良好的程序文檔的規則和要求。一個結構良好的文檔應首先做到以下幾點。
- 文檔註釋應能增加代碼的可讀性
編寫註釋的目的是使你、你的開發夥伴以及後續的開發人員更好地理解你的代碼。SUN公司JAVA開發組的Nagle曾說過“如果程序不值得寫文檔的話,意味着代碼也不值得運行”。
- 避免文檔的過度修飾
六七十年代的COBOL語言開發着有一種註釋習慣,通常用星號畫一個方盒子來填寫註釋。這樣做的確很好看,但對於最終產品來講沒有太多的益處。應當注意文檔中註釋和代碼的比例,代碼的註釋應當簡潔、明瞭。
- 在編碼前就開始寫文檔
原則上講,我們看一段代碼總能發現它在做什麼。例如,下面這段代碼中,我們能分析出代碼的行爲,但不能真正看出它的確切語義。
if ( grandTotal >= 1000.00)
{
grandTotal = grandTotal*0.95;
}
在註釋文檔中應說明代碼所表達的語義和編碼設計思想,而不應只是就事論事的講述代碼的行爲。
- 爲代碼寫註釋文檔
- 將代碼從邏輯上分段
- 合理的使用空行
- 遵守“30秒規則”,提高代碼的可讀性
- 書寫較短的代碼行
一個JAVA源文件是由用空白行和註釋分隔開的很多部分組成的,通常文件長度應該小於2000行。
每一個Java源文件都包括一個公共的類或接口。當私有類或接口與公共類相關聯的時候,通常可以將其與公共類放在同一個源文件當中。公共類應是文件中的第一個類或接口。
Java源文件的組織結構如下:
- 文件開始的註釋段
- Package和Import的聲明
- 類和接口聲明
所有源文件的開始部分都要有一個C語言風格的註釋段,列出本程序的類名、版本信息、日期、版權聲明。如下所示。
/*
* Classname
*
* Version information
*
* Date
*
* Copyright notice
*/
-
- Package和Import聲明
通常Java源文件中第一個非註釋行是一個Package聲明,然後是Import聲明。如下所示。
package java.awt;
import java.awt.peer.CanvasPeer;
下表描述了類和接口聲明中的一些部分,按照其出現順序列出:
類和接口說明 |
說明 |
Class/Interface 文檔型註釋 |
/** * */ |
Class或Interface聲明 |
|
Class或Interface的實現聲明 |
該段註釋中應該包括任何與類和接口有關的,但又不適於放在文檔註釋區中的註釋說明 |
類靜態變量 |
變量出現的順序爲,首先公共變量,然後protected變量,然後是包package一級的變量,然後是私有變量 |
變量實例 |
首先是公共變量,然後protected變量,然後是包package一級的變量,然後是私有變量 |
構造函數(Constructors) |
|
方法 |
方法應該按照功能而不是範圍和可訪問性來分類。例如,一個私有的類方法可以在兩個公共的Instance方法的中間。這樣做的目的是提高代碼的可讀性。 |
文檔中的縮進基本單位是4個空格。Tab鍵應當設置爲4個空格鍵。
代碼行的長度應小於80個字符,否則不能被編輯和處理工具很好的處理。通常文檔中代碼例子的長度不超過70個字符。
當一個表達式因太長等原因不適於單行放置的時候,可以根據下面的規則來進行分割:
- 在逗號後面分割;
- 在操作符前分割;
- 應選擇高一級的分割而不是低一級的分割;
- 將換行後的表達式的開始部分與前一行的同一級表達式對齊;
- 如果上述規則使得代碼難讀或代碼集中在行的右側,應4空格的縮進規則;
下面是一些方法調用行分割的例子
someMethod(longExpression1, longExpression2, longExpression3,
longExpression4, longExpression5);
Type var =
someMethod1(longExpression1,
someMethod2(longExpression2,longExpression3))
以上是好的格式,以下就不是很好:
Type var = someMethod1(longExpression1
someMethod2(longExpression2, longExpression3))
下面是兩個算術表達式的換行分割的例子。
LongName1 =
longName2*( longName3 + longName4 – longName5)
+ 4 * longName6; // 較好的分割方式
LongName1 =
longName2*( longName3 + longName4
– longName5) + 4 * longName6; // 儘量避免這種分割方式
其中,第一方式的較好,因爲換行分割點位於括號的外邊,同第二種分割相比是分割的級別較高。
當方法中參數很多時,提倡如下的書寫:
public void someMethod(Type longExpression1,
Type longExpression2,
Type longExpression3,
Type longExpression4,
Type longExpression5){
...
}
同樣,當IF語句或其他循環語句較長時,提倡如下的書寫:
if ((condition1 && condition2 )
|| (condition3 && condition4)
|| !(condition5 && condition6)) {
doSomethingAboutIt();
}
下面是3個可接受的表達式的書寫方式:
alpha = (alongBooleanExpression) ? beta : gamma;
alpha = (alongBooleanExpression) ? beta
: gamma;
alpha = (alongBooleanExpression)
? beta
: gamma;
用空行將代碼段按照邏輯進行分割有助於提高文件的可讀性。空2行的方法應在如下情況中採用:
- 在一個源文件中的各個代碼段之間用;
- 在類定義和接口定義之間使用。
空1行的分割方法應該在下列情況中採用:
- 在各個方法之間;
- 在一個方法中的局部變量和它的首次聲明之間;
- 在塊註釋和單行註釋之前
- 在一個方法中用空行將代碼段從邏輯上劃分,以提高文檔的可讀性
空格應該在下列情況下使用:
- 一個系統保留字和他後面的括號應該用空格分割。例如,
while (true) {
...
}
這樣做有助於把系統保留字和方法調用區分開來,因爲方法調用與其後的調用參數括號()之間是沒有空格的。
- 在逗號後面用於分隔的多個參數;
- 除了“.”外的所有二元運算符都應用空格與其相應的操作數隔開。空格不能用在一元操作符,如一元加++或一元減-- ,與其相應的操作數之間。例如,
a += c + d;
a = (a + b) / (c * d);
while (d++ = s++) {
n++;
}
prints(“size is ” + foo + “\n”);
- for 循環表達式應該用空格分開。例如,
for (expr1; expr2; expr3);
- 強制類型轉換(cast)後面應該添加空格。例如,
myMethod((byte) aNum, (Object) x);
myMethod((int) (cp + 5), ((int) (i + 3))
+ 1);
JAVA有三種註釋方式。如下表所示。
註釋方式 |
使用場合 |
例 子 |
文檔型(Document)注 釋
|
這種註釋放在程序代碼中的,接口函數、類、成員函數和域的聲明定義之前。 文檔型註釋由javadoc來處理。如右圖所示,爲一個類創建一個外部說明文檔。 |
/** Customer- A customer is any person or organization that we sell services and products to. @author S.W. Ambler */
|
C 風格的註釋 |
一般來說,用C風格的註釋來說明一段代碼是不合適的。 這種方式主要用在維護和修改代碼時,或者調試中臨時關閉一段代碼時使用。
|
/* This code was commented out By J.T.Kirk on Dec 9,1997 because it was replaced by the prededing code. Delete it after two years if it is still not applicable.
… … (the source code) */
|
單行註釋 |
一般在成員函數的內部採用單行註釋,主要用來說明事務邏輯(business logic)、代碼段、臨時變量的聲明定義。 |
//Apply a 5% discount to all invoices //over $1000 as defined by the Sarek // generosity compaign started in // Feb. Of 1995 |
下表說明了各個部分註釋的主要內容要求:
註釋項 |
註釋的內容 |
Arguments/parameters |
|
Fields/properties |
|
classes |
|
Compilation unite |
|
Interfaces |
|
局部變量local variables |
|
成員函數的文件註釋 documentation comments |
|
成員函數的內部註釋 internal comments |
|
包packages |
|
JAVA程序有兩類註釋:一類是實現性註釋(implementation comments),另一類是文檔型註釋(documentation comments)。其中,Implementation 註釋有C和C++兩種格式,/*...*/ 和//。文檔型註釋是JAVA程序所獨有的,在/**...*/中限定。文檔型註釋能夠被javadoc文件工具提取到HTML文件中。
Implementation註釋用於註釋代碼或者註釋特殊的實現。文檔型註釋主要是描述代碼段的說明,從一種非面向實現的角度來寫給開發人員閱讀讀,這些開發人員可能手邊並沒有源代碼。
通常註釋應該是給代碼一個總的描述或者提供從代碼本身不太容易看出的附加性信息。註釋的內容應只與讀代碼和理解代碼有關。例如,關於相應的包(package)是如何構建,以及存放在什麼目錄中,不應該包括在註釋中。對代碼中不太明顯的設計意圖進行說明是應該的,但也應避免對一些明顯的信息進行重複說明,儘量避免那些隨着代碼的維護會過時的註釋。
注意:
- 過於頻繁的註釋常常意味着代碼質量較低。但你覺得必須頻繁添加註釋時,應考慮重寫該段代碼了。
- 註釋內容不應封在星號(*)或者其它字母圍成的矩形框中;註釋中不應帶有特殊註釋字符如製表符(form-feed)和退格符(backspace)。
- Implementation註釋的格式
JAVA程序段中可以有4種形式的實現註釋:塊註釋(block)、單行註釋、後註釋(trailing)、行尾註釋(end-of-line)。
塊註釋用來提供文件、方法、數據結構和算法的描述。塊註釋主要用在每個文件和方法的開頭部分,有時也可用在方法的中間。在一個函數和方法中的塊註釋應該遵照縮進規則排列到說明對象的同一級深度。
通常塊註釋前用一個空行和其它的代碼段分開。
/*
* Here is a block comment.
*/
另外,用/* - 開始的塊註釋能夠被indent(1)識別出,作爲塊註釋的開始。例如,
/*-
* Here is a block comment with some very special
* formatting that I want indent(1) to ignore.
*
*/
注意: 如果你自己不用indent(1),你沒有必要用/* -註釋開始。
單行註釋應與其註釋的代碼處在同一縮進級別中。如果一個註釋能寫成單行,那麼沒有必要寫成塊註釋。單行註釋前面應有一個空格。例如:
if (condition) {
/* Handle the condition. */
...
}
一些非常短的註釋可以加在代碼的同一行內,但是應該把註釋和代碼分開足夠大的間隔,以增加可讀性。如果在一個小代碼段中有多個後註釋,那麼應將其用TAB縮進對齊。
If (a == 2) {
Return TRUE; /* special case */
} else {
return isPrime(a); /*work only for odd a */
}
注:不提倡使用
“//”註釋符能夠將整行或部分行註釋掉,它不能用在連續的多行文本註釋中。它可以用在將小段連續的代碼註釋掉。所有的3種風格如下所示:
If (foo > 1){
// Do a double-flip
...
}
else{
return false; //Explain why here(不提倡使用)
}
// if (bar > 1) {
//
// // Do a triple-flip.
// ...
//}
//else{
// return false;
//}
JAVA SDK本身提供了較強的代碼文檔生成功能,只要代碼中的註釋符合一定的規則就可利用javadoc工具自動生成代碼文檔。Javadoc 是一種工具,它用於對一組源文件中的聲明和文檔註釋進行語法分析,並生成一組 HTML(HyperText Markup Language,超文本標記語言)頁,描述類、內部類、接口、構造函數、方法和域。文檔型註釋與javadoc緊密相關,可通過靈活運用文檔型註釋讓javadoc自動生成豐富詳實的代碼文檔。Javadoc可從*.java源文件、包說明文件、概述說明文件,以及其它文件中提取信息生成文檔。
文檔型註釋(documentation)主要描述JAVA類、接口、構造器、方法和域。每一個文件註釋的內容在/**...*/, 一個類和接口一個註釋段。註釋應該緊接着類聲明的前面。文檔型註釋的格式是/**及其之後的空格都被javadoc忽略,後續行中的第一個*也被忽略。
/**
* The Example class provides ...
*/
public class Example { ...
注意:
最上一級的類和接口是不縮進的,第一行是(/**)。
其它的成員函數和構造模板要用4空格的縮進。
如果需要對類、接口進行註釋但是又不適於採用文檔型註釋,可以用塊註釋或者單行註釋。例如,一個類的詳細實現信息可以通過在類聲明後的塊註釋來做。文檔性註釋不能放在一個方法和構造之內。
源代碼的文檔型註釋放在任何實體(類、接口、方法、屬性、構造函數)的開頭。
注意:
- 只有緊挨着類、接口、方法、屬性、構造函數聲明的那塊註釋才被生成javadoc文檔,每個聲明語句前只有一塊註釋可被javadoc識別。
- 函數內部的註釋不會生成javadoc文檔。
例如下面例子中的註釋因爲放在了import語句之前,而無法被生成文檔:
/**
* This is the class comment for the class Whatever.
*/
import com.sun; // MISTAKE - Important not to put import statement here
public class Whatever {
}
每塊文檔型註釋的第一句話應該是一個概括型語句,可簡潔但完整地描述本塊註釋將要表達的意思。Javadoc將使用該句文字作爲成員方法的概述。
文檔型註釋遵循HTML語法規則,可以在註釋中插入任何想要的HTML標籤以對註釋進行格式化。從註釋生成的文檔中缺省是沒有換行的,如需要在最終文檔中體現換行,需要在註釋中增加換行標籤:
/**
* 查詢指定條件上的線路配置集。<br>
* 該方法是靜態方法,無需實例化一個本類的對象即可調用本方法。
* @param neName 網元的名字
* @param cpn 表明需要查詢哪個機框、板、端口上的信息。注意本方法不按機架
* 進行查詢,因爲一個機架上會有兩個網元,所以CPN中的rack設爲-1。按從細到粗的
* 順序,CPN中的其它字段也可設置爲-1,表示查詢僅精確到上一級單位。
* @return 返回AdslLineConfProfileTable對象組成的Vector。
**/
如需加粗某些文字:
/**
* This is a <b>doc</b> comment.
* @see java.lang.Object
*/
javadoc自身的標籤
javadoc提供了若干種自定義標籤,充分運用這些標籤可使生成的文檔結構更合理,實用性更強。Javadoc標籤以@開頭,且是大小寫敏感的。包括以下幾種標籤:@author 、{@docRoot}、@deprecated、@exception、{@link}、@param、@return、@see、@serial、@serialData、@serialField、@since、@throws、@version。下面簡要介紹幾種很常用且比較重要的標籤。
@author name-text
將代碼作者的名字生成到文檔中。
@param parameter-name description
描述方法的參數
@return description
描述返回值
@see reference
增加一個See Also的鏈接。
-
-
- javadoc標籤的使用
-
位置 |
標籤 |
概述文檔,一般是overview.html |
@see、{@link}、@since |
包文檔 |
@see、{@link}、@since、@deprecated |
類和接口文檔 |
@see、{@link}、@since、@deprecated、@author、@version |
屬性文檔 |
@see、{@link}、@since、@deprecated、@serial、@serialField |
構造函數或方法文檔 |
@see、{@link}、@since、@deprecated、@param、@return、@throws (@exception)、@serialData |
儘量不要使用括號中的標籤,如:@exception;因爲在JDK1.2中已經用括號前面的替代了。
對於較複雜的類或方法,最好在其註釋文檔中增加一段如何使用本類(方法)的例子代碼。由於javadoc生成的文檔會忽略空格和換行,應在樣例代碼段前後加上<pre></pre>標籤:
/**
* A class representing a window on the screen.
* For example:
* <pre>
* Window win = new Window(parent);
* win.show();
* </pre>
*
* @author Sami Shaio
* @version %I%, %G%
* @see java.awt.BaseWindow
* @see java.awt.Button
*/
class Window extends BaseWindow {
...
}
下面給出一個簡單的批處理文件用於生成javadoc文檔:
del filelist.txt
dir /b /s ..\ems5100\src\*.java > filelist.txt
dir /b /s ..\emsbase\src\*.java >> filelist.txt
javadoc -windowtitle IPMS源程序文檔(JavaDOC) -private @filelist.txt
另外,Javadoc 有許多有用的選項,有些相對其他更爲常用。下面是實際中我們用來在 Java 平臺 API 上運行 javadoc 的命令,它使用了 makefile 變量(除了未列出所有要建文檔的包之外)。
javadoc -sourcepath /jdk/src/share/classes /* 源文件路徑 */
-d /jdk/build/api /* 目的目錄 */
-use /* 添加“用法”文件 */
-splitIndex /* 分割索引 A-Z */
-windowtitle $(WINDOWTITLE) /* 添加窗口標題 */
-doctitle $(DOCTITLE) /* 添加文檔標題 */
-header $(HEADER) /* 添加運行頁眉文本 */
-bottom $(BOTTOM) /* 添加底部文本 */
-group $(GROUPCORE) /* 概述頁的核心標題 */
-group $(GROUPEXT) /* 概述頁的擴展標題 */
-overview overview-core.html /* 概述文本 */
-J-Xmx180m /* 180MB 內存 */
java.lang java.lang.reflect /* 要建立其文檔的包 */
java.util java.io java.net
java.applet
WINDOWTITLE = 'Java 平臺 1.2 最終 API 規範'
DOCTITLE = 'Java<sup><font size="-2">TM</font></sup> Platform 1.2 Final API Specification'
HEADER = '<b>Java Platform 1.2</b><br><font size="-1">Final</font>'
BOTTOM = '<font size="-1"><a href="http://java.sun.com/cgi-bin/bugreport.cgi">
提交 bug 或功能 </a><br><br>Java 是 Sun Microsystems , Inc 在美國和其他國家的商標或註冊商標。<br>Copyright 1993-1998 Sun Microsystems, Inc. 901 San Antonio Road,<br>Palo Alto, California, 94303, U.S.A.保留所有權利。</font>'
GROUPCORE = '"核心包" "java.*:com.sun.java.*:org.omg.*"
GROUPEXT = '"擴展包" "javax.*"'
如果省略 -windowtitle 選項,則 javadoc 將文檔標題複製到窗口標題。-windowtitle 選項是沒有必要的,除非文檔標題包含 HTML 標記。
通常JAVA代碼中一行只聲明一個變量:
int level; // indentation level
int size; // size of table
對比:
int level, size;
應在一個變量聲明的時就對它進行初始化工作。
變量聲明只應該放在代碼段的開始部分。代碼段是指花括號{}所包括的區域。嚴禁到使用時才聲明變量。
void myMethod(){
int int1 = 0; //beginning of method block
if (condition) {
int int2 = 0; // beginning of “if” block
...
}
}
唯一的例外是for循環中的循環變量,如
for (int i = 0; i < maxLoops; i++){ ... }
爲了避免局部變量被高一級的變量所覆蓋,應避免在局部代碼塊中採用同樣的變量名稱。例如,
int count;
...
myMethod() {
if (condition) {
int count; // 應避免這種情況
...
}
...
}
當編寫JAVA的類和接口是應該遵循下列格式規則:
- 在方法名稱和其後的括號()之間沒有空格;
- 聲明類和接口時,表示代碼塊開始的花括號”{“處在聲明的同一行的末尾;
- 花括號”}“應該與相應的花括號”{“所在的行縮進對齊;
- 方法與方法之間用空行隔開。
class Sample extends Object {
int ivar1;
int ivar2;
Sample(int i, int j) {
ivar1 = i;
ivar2 = j;
}
// 空行
int emptyMethod()
};
每一行應該只包括一個語句,例如:
argv++; //正確
argc++; //正確
argv++; argc--; //錯誤
複合語句是指用一對花括號包圍起來的任意數量的簡單語句{語句}。
- 括起來的語句應該比複合語句縮進至少一層;
- 開始括號應該在複合語句末尾;結束括號應該在新的一行並且和複合語句括號的開始行對齊;
- 當它們作爲控制流程的一部分的時候,應該用括號把所有的複合語句括起來,即使只有一句簡單語句,比如if-else或者for語句。這樣可以更方便的加入語句而不會引起由於忘掉加括號而引起的偶然性的錯誤。
帶值的返回語句不需要用圓括號,除非有時不得不用括號使返回結構更加明顯。例如:
return;
return myDisk.size( );
return (size ? size : defaultSize);
-
- if,if-else,if else-if else語句
if-else類語句有如下幾種形式:
if (condition) {
statements;
}
if (condition) {
statements;
} else {
statements;
}
if (condition) {
statements;
} else if (condition) {
statements;
} else {
statements;
}
注意:if語句一定要用花括號{};要避免以下混淆形式:
if (condition) //要避免這種對花括號的忽略!
statement;
-
- for語句
for語句形式如下:
for (initialization; condition; update) {
statements;
}
一個空的for循環語句的形式如下:
for (initianlization; condition; update);
當for循環的initianlization部分和update部分中有多個用逗號隔開的變量時,通常變量的數量不應該超過3個。如果必要的話,可以在for循環的之前(initianlization部分前)或者在循環的尾部(update部分)使用單獨的語句來說明。
-
- while 循環語句
一個while語句結構如下:
while (condition) {
tatements;
}
下面是一個空的while循環語句:
while (condition);
-
- do-while 語句
一個do-while 循環語句的形式如下:
do {
statements;
} while (condition);
-
- switch語句
一個switch語句的結構如下:
switch (conditon) {
case ABC:
statements;
/* falls through */
case DEF:
statements;
break;
case XYZ:
statements;
break;
default:
statements;
break;
}
每一個switch語句中應該包括一個default case語句。
-
- try-catch 語句
try-catch語句有以下格式:
try {
statements;
} catch (ExceptionClass e) {
statements;
}
try-catch語句也可以跟隨finally,它總是被執行,而不受異常是否拋出的影響。
try {
statements;
} catch (ExceptionClass e) {
statements;
} finally {
statements;
}
命名規範的目的是提高程序的可讀性,使程序易於理解。
基本命名原則:
- 使用完整的英文描述符準確描述變量variable/域field/類class
較好的命名應該象firstName、grandTotal、CorporateCustomer, 而諸如x1、y1等命名反映任何命名含義,而且造成代碼難以理解、維護和改進。
- 採用應用領域相關的術語來命名
如果軟件開發人員應注意軟件用戶的一些約定術語,不應當隨意的創造術語。這會降低軟件的易用性。
- 採用大小寫混合的方式提高命名的可讀性
一般情況下應該用小寫字母來命名,其中類(class)和接口(interface)名稱的首字母用大寫。
- 謹慎使用縮寫
設計命名中應該慎用縮寫命名。如要採用,則應採用統一的縮略規則,並且在文中的相應部分統一採用縮寫。例如,採用num作爲number的縮寫,那麼在整個文檔中應該始終使用該縮寫。
- 避免太長的命名
命名的長度應小於15個字母(但是可以使用更長的形式,以便更好的表達意思)。
- 避免採用僅有大小寫不同命名
命名時應避免採用幾乎相同的名稱。例如,變量名稱persistentObject和persistentObjects不應當同時運用;anSqlDatabase和anSQLDatabase也不應同時使用。
- 將標準縮略詞的首字母進行大小寫變化
有時名稱中會含有固定的縮略詞,例如SQL代表Standard Query Language. 而在命名時sqlDatabase和SqlDatabase就比sQLDatabase和SQLDatabase易於閱讀。
成員函數名稱應採用完整的英文單詞組成,除第一個單詞外其餘的成員單詞的首字母用大寫。成員函數取名採用動賓結構,名稱中的第一個詞爲動詞。例如,
openAccount();
printMailingLabel();
save();
delete();
成員函數的命名應能夠準確、簡潔地表示其功能。雖然函數名稍微有點長,但還是值得的,因爲它能夠增加代碼的可讀性。
對(fields/property)賦值的訪問性函數,通常的命名方式是用get/set+“訪問對象名”。當對象爲布爾型時,用is+“訪問對象名”構成,其中對象名稱用大小寫混合的方式,首字母大寫。這些命名約定是JDK標準和JAVA Beans開發時所要求的。
get型函數
Get型函數返回一個域(field)的值。在命名時將“get”作爲第一個單詞;如果是boolean型的域,那麼將“is”作爲函數名稱的第一個單詞。如:
getFirstName();
getAccountNumber();
getLostEh();
isPersistent();
isAtEnd();
set型函數
這些函數主要用來對一個域(field)來賦值,命名時將“set”作爲第一個單詞。如:
setFirstName(string aName);
setAccountNumber(int anAccountNumber);
setReasonableGoals(Vector newGoals);
setAtEnd(boolean isAtEnd);
構造函數Constructor
構造函數是成員函數的一種,當一個對象初次創建的時候負責對象初始化工作。構造函數的名稱總是與其創建的類完全相同。如:
Customer();
SavingAccount();
PresistenceBroker();
通常應該用完整的英文單詞來命名一個域,使其名稱能夠很好的反映其代表的值。如:
firstName;
zipCode;
unitPrice;
discountRate;
orderItems;
組件接口的命名應該採用全英文單詞,後綴爲該組件的類型,如botton、list等。命名應能夠較好表達該組件的用途,並使得該組件易於在applet或application中查找。組件應避免取諸如botton1、botton2等抽象的名稱。如:
okButton;
customerList;
fileMenu;
newFileMenuItem;
常量的命名常用全英文單詞來說明,所有的字母採用大寫方式,單詞之間用下劃線連接。
如:
MINMUM_BALANCE;
MAX_VALUE;
DEFAULT_START_DATE;
數組(array)和vector的命名方法是,用array和vector所存對象名稱的複數表示,名稱爲全英文單詞組成,非開頭單詞的首字母採用大寫。例如,
customers;
orderItems;
aliases;
局部變量的命名規範與前面提到的field命名原則類似,命名用全英文單詞,其中非開頭單詞的首字母大寫。
-
-
- 流(Streams)的命名
-
當在一個成員函數中打開一個輸入輸出流的時候,SUN公司的命名約定如下:
- 輸入流
命名用in +“流名稱”;
- 輸出流
命名用out +“流名稱”;
- 輸入/輸出流
命名用inOut +“流名稱”;
循環變量的命名一般用i、j、k來表示,也可以用更有意義的單詞來表示。對於嵌套循環,一般外層循環用i, 第二層用j, 依此類推。
-
-
- 異常(Exception)處理中的命名
-
異常處理在JAVA代碼編程中是十分常用的。通常用小寫ex 來作異常對象(exception objection)的對象。例如,
public LocaLize(String FileName) {
try {
filename = FileName;
jbInit();
}
catch(Exception ex) {
ex.printStackTrace();
}
}
下表是SUN公司的一些通用變量和對象的JAVA命名約定。
變量類型 |
命名前綴 |
offset |
Off |
length |
Len |
byte |
B |
char |
C |
double |
D |
float |
F |
long |
L |
Object |
O |
String |
S |
Arbitrary value |
V |
下表總結了類、接口、包的命名規則。
定義類型 |
命名規則 |
例子 |
Packages |
包名稱的前綴總是用小寫的ASCII字母來表示的,並且總是採用一級域名,如com,edu,gov,mil.net,ort,或者是ISO 3166標準中規定的國家名的2字母縮寫。 一個包名稱後面的組件名根據一個組織的各個名稱約定來取。
|
com.sun.eng com.apple.quicktime.v2 edu.cmu.cs.bovik.cheese com.zte.resmaster.util |
Class |
類名稱應該是名詞,且其內部的每一個詞的首字母應大寫。類名應簡單而有意義。組成類名的單詞不應用縮寫。除非這些縮寫已被廣泛理解,如URL和HTML。 類名儘量使用名詞,或者名詞+動詞的形式。 |
Class Raster; Class ImageSprite; |
Inerfaces |
接口名稱的大寫規則與類名相同。 接口名儘量使用形容詞,如:Runnable。或者名詞,並且名詞最好是帶er,如:RequestHandler。 |
Interface RasterDelegate interface Sroring |
Methods |
方法的取名應該是動詞。它是大小寫混合的。成員單詞的首字母小寫,且中間單詞的首字母大寫 |
Run(); RunFast(); GetBackground( |
Variables |
除了變量外,所有的instance,calss,和類常量是大小寫混合的。成員單詞的首字母小寫,且中間單詞的首字母大寫。另外,變量名稱不應該用下劃線“_”和“$”符號開始。
變量名稱應該簡短但有意義。除非是臨時使用的變量外,不應當用單個單詞來命名變量。
通常臨時變量命名時,i,j, k,m,n,來表示整型變量;c,d,e表示字符型變量
|
Int i; Char c; Float myWidth; |
Constants |
所有的常量名稱應該用大寫字母表示,各個成員單詞之間用下劃線“_”相連接。
|
Static final int MIN_WIDTH = 4;
Static final int MAX_WIDTH = 999 Static final int GET_THE_CPU = 1; |
-
- J2EE命名規範
在開發J2EE系統的過程中,隨着系統開發的深入,如何控制各種對象和資源的命名就成爲一個使系統成功的一個重要因素。任何隨意性都將導致系統在後期產生混亂,結構不清晰,導致更多的維護成本。
-
-
- EJB包結構
-
實體Bean
Bean :com.company.projectname.subsystem.ejbname.ejb.EjbnameEJB
Remote :com.company.projectname.subsystem.ejbname.ejb.EjbnameRemote
Home :com.company.projectname.subsystem.ejbname.ejb.EjbnameHome
例如:用戶對象
Bean : com.zte.resmaster.sysmanage.user.ejb.UserEJB
Remote : com.zte.resmaster.sysmanage.user.ejb.UserRemote
Home : com.zte.resmaster.sysmanage.user.ejb.UserHome
會話Bean
Bean : com.company.project.subsystem.ejbname.manager.EjbnameEJB
Remote : com.company.project.subsystem.ejbname.manager.EjbnameRemote
Home : com.company.project.subsystem.ejbname.manager.EjbnameHome
例如:用戶對象
Bean : com.zte.resmaster.sysmanage.user.manager.UserEJB
Remote : com.zte.resmaster.sysmanage.user.manager.UserRemote
Home : com.zte.resmaster.sysmanage.user.manager.UserHome
數據值對象(Data Value設計模式中採用)
com.company.projectname.subsystem.ejbname.model.EjbnameModel
例如:用戶對象
com.zte.resmaster.sysmanage.user.model.UserModel
DAO對象
DAO接口:com.company.projectname.subsystem.ejbname.dao.EjbnameDAO
DAO實現:com.company.projectname.subsystem.ejbname.dao.EjbnameDAOImpl
DAO數據庫實現:
com.company.projectname.subsystem.ejbname.dao.EjbnameDAODatabase
DAO工廠:com.company.projectname.subsystem.ejbname.dao.EjbnameDAOFactory
例如:用戶對象
DAO接口:com.zte.resmaster.sysmanage.user.dao.UserDAO
DAO實現:com.zte.resmaster.sysmanage.user.dao.UserDAOImpl
DAOOracle實現:com.zte.resmaster.sysmanage.user.dao.UserDAOOracle
DAOSybase實現:com.zte.resmaster.sysmanage.user.dao.UserDAOSybase
DAO工廠:com.zte.resmaster.sysmanage.user.dao.UserDAOFactory
異常
com.company.projectname.subsystem.ejbname.exceptions.XXXException
例如:用戶關鍵字已經存在的異常
com.zte.resmaster.sysmanage.user.exceptions.UserDupKeyException
工具類
com.company.projectname.subsystem.ejbname.util.Xxxx
例如:
com.zte.resmaster.sysmanage.user.util.UserEJBHelper
-
-
- Servlet包結構
-
Servlet類
com.company.projectname.subsystem.web.XxxServlet
例如:數據網子系統
com.zte.resmaster.datanet.web.MainServlet
Handler處理類
com.company.projectname.subsystem.web.handlers.ObjectHandler
例如:線路對象
com.zte.resmaster.datanet.web.handlers.LineHandler
Web工具類
com.company.projectname.subsystem.util.XXX
例如:
com.zte.resmaster.datanet.util.DataUtil
Web異常類
com.company.projectname.subsystem.exceptions.XXXException
例如:通用錯誤類
com.zte.resmaster.datanet.exceptions.GeneralFailureException
數據
com.company.projectname.util.data.XXX
例如:
com.zte.resmaster.util.data.SystemProperties
跟蹤調試
com.company.projectname.util.tracer.XXX
例如:
com.zte.resmaster.util.trace.Debug
UI(用戶界面)
com.company.projectname.util.ui.XXX
例如:字體選擇面板
com.zte.resmaster.util.ui.FontChooserPanel
Theme(界面樣式)
com.company.projectname.util.theme.XXX
例如:IzPack的菜單UI樣式
com.zte.resmaster.util.theme.IzPackMenuUI
-
-
- JNDI名稱
-
EJB註冊(可以不用projectname)
Ejb/projectname/ejbtype/EjbnameEJB
例如:
ejb/resmaster/entity/UserEJB
ejb/resmaster/session/SecurityEJB
還可以加入子系統名稱,以便管理。如:
ejb/resmaster/baseres/session/QuyuEJB。
可以把ejbtype去掉,如:
ejb/resmaster/baseres/QuyuEJB。
引用EJB
ejb/EjbnameEJB
例如:
ejb/UserEJB
引用數據源(可以使用DatabaseType)
jdbc/DatabaseType/DataSources
例如:jdbc/oracle/ResTxDataSource
一般不要隨意的將變量和實例的訪問屬性設置爲public。
不要用一個對象去直接訪問類變量或者方法,而是應該用它的類名。例如,
classMethod(); //OK
Aclass.classMethod(); //OK
AnObject.classMethod(); //避免採用
編碼中不應直接使用數字常量,除了在for循環中用的-1,0,1等計數常量。
數字常量又叫Magic Sum,有時你自己都不知道它的值究竟是什麼。
避免在一條語句中將一個數值賦給多個變量,這樣不易閱讀。例如,
fooBar.fChar = barFoo.lchar = ‘c’; //應當避免
不要在語句中容易和“相等”混淆的地方放置操作符。例如,
if (c++ = d++) { //JAVA不允許的
...
}
應當改爲,
if ((c++ = d++) != 0) {
...
}
要警惕IF判斷語句中的賦值,上面的形式Java編譯器拒絕,但是下面的接受:
boolean flag = false;
if (flag = booleanexpression){
...
}
所以,你要清楚你究竟要幹什麼,不清楚時,寫成多行語句。
另外,不要把一個語句嵌入在另外一個語句中。例如,
d = (a = b + c) + r // 避免使用
應該寫成,
a = b + c;
d = a + r;
- Field應該設爲私有
- 不要直接訪問屬性,應該定義存取成員函數
- 不要用常量靜態屬性(final static fields), 應使用存取成員函數
- 不要重名
- 靜態屬性要初始化
- 減少publice和protected 接口的數量
- 在編碼之前爲類定義公共接口
- 按照如下順序聲明類屬性和成員函數
- constructors
- finalize()
- public member functions
- protected member functions
- private member functions
- private field
- 不要命名重複
- 每行只能聲明一個變量
- 局部變量採用行內註釋
- 緊跟在使用之前定義局部變量
- 先進行總體說明註釋(document comment)
- 將代碼安邏輯關係分段
- 合理使用空行
- 好的成員函數應能夠在30秒鐘內被其他人理解
- 寫單行的短註釋
- 儘可能控制對成員函數內部成員的訪問
- 說明操作順序
由於Java中有內存垃圾收集的機制,並且沒有指針類型,開發者就可以從C/C++的內存噩夢中解脫出來。正因爲如此,很多開發者就不再關注內存的使用問題。小心,這裏存在陷阱。
請注意,Java的這個機制是內存垃圾收集而非內存收集!所以,你需要把使用的對象變成JVM可以識別的垃圾。一般而言,在方法中聲明使用的變量在方法外面時就會成爲垃圾。但是有些卻不是,例如:
private HashMap dialogMap = new HashMap();
...
{
JDialog dialog = new JDialog(“XXX”);
...
dialogMap.put(dialog.getTitle(),dialog);
}
...
如果你在整個代碼期間都沒有調用如下語句:
Object ref =dialogMap.remove(key);
...(釋放資源,如:((JDialog)ref).dispose())
ref = null;
或者
dialogMap.clear()(有時,僅僅這個語句還不行)
那麼你的代碼就存在內存泄漏問題!
幾乎所有存儲對象的結構都要引起注意,看看是否有資源沒有釋放,沒有成爲垃圾而無法回收。下面的對象要嚴格釋放:
java.sql.Connection;
java.sql.Statement;
java.sql.PreparedStatement
java.sql.CallableStatement
java.sql.ResultSet
這些對象如果不釋放其資源,不僅僅是內存問題;還將引起數據庫問題。如果不釋放Connection,那麼很快就用盡連接或是數據庫巨慢(數據庫連接數目還受到Licence的限制)。如果不釋放後面的對象資源,那麼很快就會用盡數據庫遊標,因爲每打開一次後面的資源就要使用一個遊標,即使語句中沒有使用遊標(其實每個DDL語句都使用一個缺省遊標)。而數據庫的遊標一般是有限制的,Oracle8.1.6中缺省爲100,Oracle8.1.7中缺省爲300。
千萬注意!
可能在一段時間內永遠不會碰到內存不足的問題,是不是就不用警惕上面提到的內容呢?
不!
JVM中垃圾內存的收集還受到內存容量的影響。當還有可用內存時,常常不會主動去垃圾收集。這就是爲什麼常常沒有碰到內存不足的問題,因爲你機器的內存足夠大(256M)。
爲了Java程序有序的運行,你可以爲它設定一個最大最小內存使用量。就像Weblogic中的一樣,如:
-hotspot -ms64m -mx64m。
這樣有利於更好的利用垃圾收集機制。
妨礙程序國際化的一個常見的設計失誤是:把在界面上顯示的內容在源代碼中進行硬編碼,這使得了application或者applet不能被很方便地進行本地化處理。
爲了解決這個問題,可以在完成代碼的編寫和測試以後,把用戶界面中的硬編碼字符串進行資源化處理,另外的一個較好辦法是,在界面設計過程中就對可視的字符串進行資源化處理。
如果使用Jbuilder,就可以使用它所提供的兩種方法來完成工作,這兩種方法分別是:資源字符串嚮導(Resource Strings wizard)和本地化屬性設置對話框(Localizable Property Setting dialog box)。
如果不使用Jbuilder工具,也可以模仿它所生成的代碼,自己進行相應處理。
-
-
-
- 使用Resource Strings wizard
-
-
Resource Strings wizard能夠快速、方便地掃描源代碼,把硬編碼的字符串放到Java的ResourceBundle類中。
該向導不僅能夠處理Jbulider生成的源文件,對於其它來源的Java文件也能處理。
ResourceBundles資源包,是包含有可翻譯字符串集合的特殊的文件,
ResourceBundle有兩種:PropertyResourceBundle和ListResourceBundle。
PropertyResourceBundles是帶有.properties擴展名的文本文件,和源代碼生成的class文件在一個目錄下。
對於PropertyResourceBundles,在對應用程序的資源包進行修改或者添加時,無需重新編譯。
而ListResourceBundles 是以Java源代碼文件形式存在的。 由於是以Java源碼形式實現的,所以任何新建或者修改的ListResourceBundles,都需要被重新編譯才能發佈。與PropertyResourceBundles相比,ListResourceBundles的效率要高的多,所以Jbuilder把其作爲缺省都選擇。
該屬性設置框允許你在創建或者定製用戶界面的組件時,就可對可視化的字符串進行資源化處理。
在對象查看器中,右鍵單擊任何text屬性字段(比如按鈕控件的標籤屬性)。
然後選擇ResourceBundle命令,顯示本地化屬性設置對話框。
該對話框顯示的屬性和資源字符串嚮導對話框中的類似,只是含有影響單個選定對象屬性的選項。
在對象查看器中進行資源化處理非常地快速和方便,你完全可以把這個過程看成是對應用程序地組件進行定製的完整步驟之一。
-
-
- 用戶界面設計的國際化
- 支持對於Unicode字符的處理
- 用戶界面設計的國際化
-
使用Unicode escape序列的16進制值來插入Unicode字符,另外,要用尖括號將其括起來。
比如,要把中文的字符“確定”作爲按鈕的標籤,就可以輸入<\u786e\u5b9a>。
-
-
-
- 使用動態佈局管理
-
-
當設計一個將要被本地化爲多種語言的用戶界面時,最爲重要的一個規則是:一定要使用動態佈局管理器(dynamic layout manager)。
這可以解決諸如讓按鈕大小隨着它的標籤文字的寬度而自動變化等問題。
在實際設計界面時,需要綜合考慮、使用Java平臺提供的五種最常使用的佈局管理方式:BorderLayout, GridLayout ,FlowLayout ,BoxLayout和 GridBagLayout。
編譯器對使用本地編碼(或者叫本地代碼頁)的源代碼進行編譯,這種源碼的存儲格式被絕大多數的文本編輯器所使用。
編譯器會自動根據操作系統環境選擇合適的本地代碼。也可以爲由不同的本地代碼寫成的源代碼文件指定任意的JDK編碼。
指定編碼的名稱,就可以控制編譯器解釋處理ASCII字符集以外的那些字符。這可以在工程項目範圍一級進行指定,也可以在命令行用encoding編譯選項來進行控制。如果該選擇沒有被指定,則會使用平臺缺省的本地代碼轉換器。
除了最簡單的關於界面顯示語言的國際化,還要保證自己的程序對於其它國家相關的特性提供支持,實際應用中的情況是非常複雜的。
其它一些需要考慮的因素包括:提示信息,GUI組件上的標籤,在線幫助,聲音,顏色,圖形,圖標,日期,貨幣符號,數字,小數值,分隔符。。。。。。
此外,一些具體處理過程和方式也會不同,例如排序方法,輸入處理等。
這些可以利用java.text包以及java.util包中的TimeZone、SimpleTimeZone和Calendar等類進行輔助處理。
通常建議在有多個操作符的表達式中藉助於括號來更加清楚地表達一條語句。這能幫助你和其他程序員更好的理解代碼。
if (a == b && c == d) // 避免
if ((a == b) && (c == d)) // 較好
寫代碼時應該儘量讓你的程序結構很好地體現意圖。如:
if (booleanExpression) {
return true;
} else (
return false;
)
應該改爲如下語句。
Return booleanExpression;
類似的,
if (condition) {
return x;
}
return y;
應該寫爲,
return (condition ? x : y);
如果在三重操作符“?:”中的“?”前面還有二元操作,那麼必須採用括號。如,
(x >= 0) ? x : -x;
- Amble S.W. (Feb 2000 ,v17.01d), Writing Robust Java Code, AmbySoft Inc v17.01d, New York ,U.S.A
- Perter King, (2001), java Code Conventions, Sun Microsystems, Inc. http://java.sun.com,
- 主要參考:張偉民、張立、餘勇,(2001-3),《JAVA代碼編程規範》;