1 Java編程規(guī)范
1.1 命名約定
1.1.1 包的命名
JDK 的標(biāo)準(zhǔn)是——包名稱的所有字母均小寫,如:
package myownpackage;
1.1.2 類的命名
JDK 的標(biāo)準(zhǔn)是——大寫一個類名的首字母,若類名由幾個單詞構(gòu)成,那么把它們緊靠到一起(也就是說,不要用下劃線來分隔名字)。此外,每個嵌入單詞的首字母都要采用大寫形式。類名稱應(yīng)該是一個名詞短語。如:
class ZipFileAccesser
1.1.3 變量的命名
1. ?變量命名一般原則(參照匈牙利命名法)(必須)
2. 能清楚表示變量的類型
3. 能簡明表達(dá)變量的涵義
4. ?常量的命名(必須)
5. 常量名稱全部大寫,如:
public final int PORT = 2000;
6. 主要基本類型變量的命名
主要類型名稱縮寫格式及規(guī)則:由類型全稱取一個字母組成 + 實(shí)際名稱,如:
String strTemp;
對幾種主要的基本類型變量命名前綴的規(guī)定(參見附錄A:基本命名參考)
7. 靜態(tài)變量的命名
靜態(tài)變量名稱以字符串“STATIC”開頭,如:
static int STATICintNumber;
8. 臨時變量的命名(推薦)
臨時性變量格式為(類型名稱縮寫 + Temp),如:
String strTemp=”asdf”;
9. 對象命名規(guī)則:
第一原則:當(dāng)類名是由多單詞組成,取每個單詞的首字母,將其小寫形式組合而成變量名稱的前綴,當(dāng)對象是由單個單詞組成時,取整個單詞的小寫形成作為變量名的前綴,如:
PrintWriter pwTemp;
Socket socketTemp;
第二原則:如果這樣命名時產(chǎn)生了重復(fù)現(xiàn)象,則和類型名稱縮寫提取原則類似,取三個小寫輔音字母或習(xí)慣性的用法,總之以能明顯區(qū)別類名稱為原則。如:ServletResponse 和ServletRequest 如果按照第一原則,兩個對象都可命名為srTemp,產(chǎn)生了重復(fù),此時就應(yīng)按照第二原則,命名為srpTemp 和srqTemp
10. 成員變量的命名
成員變量的命名原則與臨時變量相同,但在可能與臨時變量名稱重復(fù)的調(diào)用場合,應(yīng)加上this.以示區(qū)別。推薦:對大多數(shù)成員變量調(diào)用的場合加上this.以利區(qū)別,如:
public String strUserName;
this.strUserName = strUserName;
1.1.4 方法的命名
方法名的第一個字母采用小寫,其他嵌入單詞首字母大寫。方法名應(yīng)該是一個動賓結(jié)構(gòu)的短語,能夠簡明恰當(dāng)?shù)谋磉_(dá)出方法完成的操作。如:
public String getStudentName(…);
1.1.5 其它說明:(推薦)
一般不允許使用數(shù)字和下劃線。如:
socket1,socket_num;
1.2 注釋
注釋采用javadoc的注釋方法,所有注釋放置于所注釋目標(biāo)的上方。
1.2.1 類/接口的頭注釋
/**********************************************************
* 類描述信息
* @author 作者信息
* @version 版本信息,時間
* @since 創(chuàng)自信息
**********************************************************/
1.2.2 屬性的注釋
/**
* 注釋內(nèi)容
*/
1.2.3 方法的頭注釋
/**
* @param
* @return
* @throws
*/
注:每個參數(shù)名后必須有相應(yīng)的說明,格式如下:
[參數(shù)名 參數(shù)說明]
如:
…
* @param intCounter 計(jì)數(shù)器
* @param strName 變量名稱
* @param blnFlag 標(biāo)志位
…
返回值不必寫類型,如果沒有返回值或者返回值是void 就不寫@return,如:
* @return 當(dāng)前的計(jì)數(shù)值
異常要逐個分行寫出,如:
* @throws IOException 如果發(fā)生I/O 錯誤
* @throws ZIPException 如果發(fā)生ZIP 文件錯誤
1.2.4 代碼內(nèi)的注釋
直接在代碼內(nèi)出現(xiàn)的注釋通常使得代碼易于理解。通過在前面加上一個空行和一個注釋行(或更多行)來概括大的操作。在需要時用每行后面的注釋或者更多的注釋來解釋較奇特的代碼。不止一行的注釋通常用/*…*/,而不是//,但//在行尾注釋中更好用。單行的注釋用兩種方法都可以,但推薦使用//。
1.2.5 未完成代碼的提醒注釋
/**
* @todo 注釋內(nèi)容
*/
1.3 代碼分隔
import …
空一行
package …
空一行
class …
1. 屬性與屬性間空一行,方法與方法之間空一行,類與類之間空一行。先寫公有屬性,后寫保護(hù)屬性,最后寫私有屬性。
2. 在方法內(nèi)部,幾行關(guān)系緊密的代碼之間應(yīng)該不空行,但兩個相對獨(dú)立的代碼塊之間應(yīng)空一行。一般,在方法內(nèi)部每隔3~5 行就應(yīng)有一個空行。兩個相鄰空行之間的一段代碼為一個代碼塊,
3. 每隔代碼塊前面應(yīng)有行注釋。
4. 構(gòu)造子必須放在其他所有方法前
5. 運(yùn)算符兩邊要加一個空格,如:
intTotal = intAvg * intNum;
6. 括號兩邊內(nèi)側(cè)要各加一個空格,如:
void main ( String[ ] args );
7. 一行代碼只允許有一條語句,但這樣簡單的形式除外:
int ix, iy;
8. 減少public 域的定義,盡量通過方法而不是通過對象的實(shí)例直接訪問對象的域,并且所有的成員域必須加以說明。如:
String strUserName; //用戶名稱
1.4 代碼縮進(jìn)原則
1. 類名、類體、方法體依次縮進(jìn)一個單位長
2. 一個單位長是四個空格(可設(shè)為一個TAB 長)
1.5 雜項(xiàng)格式化約定
1. 在代碼塊之間使用空行,通過概括注釋來分隔。
2. 使行的長度限制在80個字符內(nèi)。
3. 用tab來縮進(jìn)、每個tab長度為4個空格。
4. 左花括號與方法名處同一行,間隔一個空格。
5. 跳轉(zhuǎn)語句的標(biāo)號與switch關(guān)鍵字對齊。
6. 像在英文中那樣使用空格(關(guān)鍵字和逗號后面加,括號后面不加,許多操作符之間加)。
2 附錄A:基本命名參考
2.1 構(gòu)造名稱
w 一般的匈牙利名稱是由三部分組成:一個或多個前綴,一個基本標(biāo)簽和一個qualifier。
w 基本標(biāo)簽表示了變量的類型(例如,“co”表示一種顏色)。
w 前綴修飾這個類型(例如,“rg”表示體格數(shù)組,所以“rgco”表示一個顏色數(shù)組)。
w qualifier描述了這種特定類型的具體用途(例如,“rgcoGray”表示一個作為灰色使用的顏色數(shù)組)。
2.2 標(biāo)準(zhǔn)基本標(biāo)簽
i
|
int
|
f
|
float
|
d
|
double
|
b
|
boolean
|
ch
|
char
|
str
|
字符串String
|
bt
|
byte類型。
|
l
|
long類型 (有符號32位的值)。
|
date
|
日期型
|
enum
|
枚舉型
|
2.3 標(biāo)準(zhǔn)前綴
rg/a
|
數(shù)組(來自于“range”)。例如,“rgch”表示一個字符數(shù)組。
|
c
|
某個基類條目的計(jì)數(shù)。例如,“cch”表示字符的計(jì)數(shù)。
|
d
|
某種基類的值之間的差值或delta。例如,“dx”表示兩個x類型的值之間的差值。
|
此外,以下是我們在合適的時候可以在任何匈牙利前綴之前加入的特殊前綴:
m_
|
C++類的數(shù)據(jù)成員。
|
s_
|
C++類的靜態(tài)數(shù)據(jù)成員。
|
2.4 標(biāo)準(zhǔn)qualifier
First
|
某集合中的第一個條目,或所關(guān)心的第一個條目(例如pchFirst)。
|
Last
|
某集合的最后一個條目,或所關(guān)心的最后一個條目(例如,pchLast)。當(dāng)作為下標(biāo)使用時,Last表示最后一個有效的/希望的值,所以一個循環(huán)可能是:
for (ich = ichFirst; ich <= ichLast; ich++)
|
Lim
|
某集合的元素個數(shù)上限。與Last不同,Lim不代表一個有效的值,Lim代表最后一個有效值后面的一個,所以一個循環(huán)可能是:
for (ich = ichFirst; ich < ichLim; ich++)
|
Min
|
某集合的最小元素。與First相似,但通常表示第一個有效的值,而不是要處理的第一個值。
|
Max
|
某集合的元素個數(shù)上限(與Lim相同)。不幸的是,標(biāo)準(zhǔn)的英文中的“Max”通常表示最后一個有效的值,但Max qualifier不是一個有效的值,它是最后一個有效值后面的一個。和Lim一樣,典型的用法應(yīng)該是:
for (ich = ichMin; ich < ichMax; ich++)
對此要特別小心。
|
Mac
|
與Max相似,但有時可用在“當(dāng)前”最大值可能隨時間變化的情況下。注意Mac也是代表“最后的”有效值之后的一個。
|
Mic
|
與Min相似,但有時可用在“當(dāng)前”最大值可能隨時間變化的情況下。
|
T
|
臨時的值。這個qualifier可能會被過度使用,而不能為某些量起一個好名字,但有時少數(shù)的臨時值是可以的,比如在一個典型的交換操作中。
|
TT, T3, etc.
|
這是在需要更確切的名字的時候?qū)?/span>T的進(jìn)一步濫用。這些用法應(yīng)當(dāng)被避免。
|
Sav
|
用于保存一個值的臨時量,使該值可以在以后被恢復(fù)。例如:hwndSav = hwnd; ...; hwnd = hwndSav;
|
Null
|
特殊的0值,總是等價于0,但用于特定的目的(例如 hwndNull)。
|
Nil
|
特殊的無效值,不一定與0等價(可能是-1,或任何其它值)。為避免混淆,最好對同一類型避免既使用Null又使用Nil。
|
Src
|
某項(xiàng)操作的源,典型的用法是與表示目標(biāo)的Dest匹配使用,如下:
*pchDest = *pchSrc
|
Dest
|
目標(biāo)。參見Src。
|
Sum
|
總和。
|
3 附錄B:標(biāo)準(zhǔn)代碼樣本
import com.sun.tools.doclets.*;
import com.sun.javadoc.*;
import java.io.*;
import java.lang.*;
import java.util.*;
/***********************************************************************
* Generate the package index page "overview-summary.html" for the right-hand
* frame. A click on the package name on this page will update the same frame
* with the "pacakge-summary.html" file for the clicked package.
*
* @author Atul M Dambalkar
* @version 1.0
* @since JDK 1.4
***********************************************************************/
public class PackageIndexWriter
extends AbstractPackageIndexWriter {
/**
* Root of the program structure. Used for "overview" documentation.
*/
private RootDoc root;
/**
* Map representing the group of packages as specified on the command line.
*
* @see Group
*/
private Map groupPackageMap;
/**
* List to store the order groups as specified on the command line.
*/
private List groupList;
/**
* Construct the PackageIndexWriter. Also constructs the grouping
* information as provided on the command line by "-group" option. Stores
* the order of groups specified by the user.
*
* @see Group
*/
public PackageIndexWriter(String filename, RootDoc root) throws IOException {
super(filename);
this.root = root;
groupPackageMap = Group.groupPackages(packages);
groupList = Group.getGroupList();
}
/**
* Generate the package index page for the right-hand frame.
*
* @param root the root of the doc tree.
*/
public static void generate(RootDoc root) throws DocletAbortException {
PackageIndexWriter packgen;
String filename = "overview-summary.html";
try {
packgen = new PackageIndexWriter(filename, root);
packgen.generatePackageIndexFile();
packgen.close();
}
catch (IOException exc) {
Standard.configuration().standardmessage.error(
"doclet.exception_encountered",
exc.toString(), filename);
throw new DocletAbortException();
}
}
/**
* Print each package in separate rows in the index table. Generate link
* to each package.
*
* @param packagedoc Package to which link is to be generated.
*/
protected void printIndexRow(PackageDoc packagedoc) {
trBgcolorStyle("white", "TableRowColor");
summaryRow(20);
bold();
printPackageLink(packagedoc);
boldEnd();
summaryRowEnd();
summaryRow(0);
printSummaryComment(packagedoc);
summaryRowEnd();
trEnd();
}
/**
* Depending upon the grouping information and their titles, generate
* separate table indices for each package group.
*/
protected void generateIndex() {
for (int i = 0; i < groupList.size(); i++) {
String groupname = (String) groupList.get(i);
List list = (List) groupPackageMap.get(groupname);
if (list != null && list.size() > 0) {
printIndexContents( (PackageDoc[]) list.
toArray(new PackageDoc[list.size()]),
groupname);
}
}
}
/**
* Print the overview summary comment for this documentation. Print one line
* summary at the top of the page and generate a link to the description,
* which is generated at the end of this page.
*/
protected void printOverviewHeader() {
if (root.inlineTags().length > 0) {
printSummaryComment(root);
p();
bold(getText("doclet.See"));
br();
printNbsps();
printHyperLink("", "overview_description",
getText("doclet.Description"), true);
p();
}
}
/**
* Print Html tags for the table for this package index.
*/
protected void printIndexHeader(String text) {
tableIndexSummary();
tableHeaderStart("#CCCCFF");
bold(text);
tableHeaderEnd();
}
/**
* Print Html closing tags for the table for this package index.
*/
protected void printIndexFooter() {
tableEnd();
p();
space();
}
/**
* Print the overview comment as provided in the file specified by the
* "-overview" option on the command line.
*/
protected void printOverviewComment() {
if (root.inlineTags().length > 0) {
anchor("overview_description");
p();
printInlineComment(root);
p();
}
}
/**
* Call {@link #printOverviewComment()} and then genrate the tag information
* as provided in the file specified by the "-overview" option on the
* command line.
*/
protected void printOverview() throws IOException {
printOverviewComment();
generateTagInfo(root, null); //new
}
/**
* Print the header for navigation bar. Also print the "-title" specified
* on command line, at the top of page.
*/
protected void printNavigationBarHeader() {
navLinks(true);
hr();
printConfigurationTitle();
}
/**
* Print the footer fornavigation bar. Also print the "-bottom" specified
* on command line, at the top of page.
*/
protected void printNavigationBarFooter() {
hr();
navLinks(false);
printBottom();
}
}
4 附錄C:更多Java編程規(guī)則
選自《Think in java》,包含了大量有用的建議,幫助大家進(jìn)行低級程序設(shè)計(jì),并提供了代碼編寫的一般性指導(dǎo):
1. 類名首字母應(yīng)該大寫。字段、方法以及對象的首字母應(yīng)小寫。對于所有標(biāo)識符,其中包含的所有單詞都應(yīng)緊靠在一起,而且大寫中間單詞的首字母。例如:
ThisIsAClassName
thisIsMethodOrFieldName
若在定義中出現(xiàn)了常數(shù)初始化字符,則大寫static final基本類型標(biāo)識符中的所有字母。這樣便可標(biāo)志出它們屬于編譯期的常數(shù)。
Java包(Package)屬于一種特殊情況:它們?nèi)际切懽帜?,即便中間的單詞亦是如此。對于域名擴(kuò)展名稱,如com,org,net或者edu等,全部都應(yīng)小寫(這也是Java 1.1和Java 1.2的區(qū)別之一)。
2. 為了常規(guī)用途而創(chuàng)建一個類時,請采取“經(jīng)典形式”,并包含對下述元素的定義:
equals()
hashCode()
toString()
clone()(implement Cloneable)
implement Serializable
3. 對于自己創(chuàng)建的每一個類,都考慮置入一個main(),其中包含了用于測試那個類的代碼。為使用一個項(xiàng)目中的類,我們沒必要刪除測試代碼。若進(jìn)行了任何形式的改動,可方便地返回測試。這些代碼也可作為如何使用類的一個示例使用。
4. 應(yīng)將方法設(shè)計(jì)成簡要的、功能性單元,用它描述和實(shí)現(xiàn)一個不連續(xù)的類接口部分。理想情況下,方法應(yīng)簡明扼要。若長度很大,可考慮通過某種方式將其分割成較短的幾個方法。這樣做也便于類內(nèi)代碼的重復(fù)使用(有些時候,方法必須非常大,但它們?nèi)詰?yīng)只做同樣的一件事情)。
5. 設(shè)計(jì)一個類時,請?jiān)O(shè)身處地為客戶程序員考慮一下(類的使用方法應(yīng)該是非常明確的)。然后,再設(shè)身處地為管理代碼的人考慮一下(預(yù)計(jì)有可能進(jìn)行哪些形式的修改,想想用什么方法可把它們變得更簡單)。
6. 使類盡可能短小精悍,而且只解決一個特定的問題。下面是對類設(shè)計(jì)的一些建議:
(1) 數(shù)量眾多的方法涉及到類型差別極大的操作:考慮用幾個類來分別實(shí)現(xiàn)
(2) 許多成員變量在特征上有很大的差別:考慮使用幾個類
7. 讓一切東西都盡可能地“私有”——private。在多線程環(huán)境中,隱私是特別重要的一個因素——只有private字段才能在非同步使用的情況下受到保護(hù)。
8. 謹(jǐn)惕“巨大對象綜合癥”。對一些習(xí)慣于順序編程思維、且初涉OOP領(lǐng)域的新手,往往喜歡先寫一個順序執(zhí)行的程序,再把它嵌入一個或兩個巨大的對象里。根據(jù)編程原理,對象表達(dá)的應(yīng)該是應(yīng)用程序的概念,而非應(yīng)用程序本身。
9. 若不得已進(jìn)行一些不太雅觀的編程,至少應(yīng)該把那些代碼置于一個類的內(nèi)部。
10. 任何時候只要發(fā)現(xiàn)類與類之間結(jié)合得非常緊密,就需要考慮是否采用內(nèi)部類,從而改善編碼及維護(hù)工作。
11. 盡可能細(xì)致地加上注釋,并用javadoc注釋文檔語法生成自己的程序文檔。
12. 避免使用“魔術(shù)數(shù)字”,這些數(shù)字很難與代碼很好地配合。如以后需要修改它,無疑會成為一場噩夢,因?yàn)楦静恢?#8220;100”到底是指“數(shù)組大小”還是“其他全然不同的東西”。所以,我們應(yīng)創(chuàng)建一個常數(shù),并為其使用具有說服力的描述性名稱,并在整個程序中都采用常數(shù)標(biāo)識符。這樣可使程序更易理解以及更易維護(hù)。
13. 當(dāng)客戶程序員用完對象以后,若你的類要求進(jìn)行任何清除工作,可考慮將清除代碼置于一個良好定義的方法里,采用類似于cleanup()這樣的名字,明確表明自己的用途。除此以外,可在類內(nèi)放置一個boolean(布爾)標(biāo)記,指出對象是否已被清除。在類的finalize()方法里,請確定對象已被清除。在采取象這樣的方案之前,請確定finalize()能夠在自己的系統(tǒng)中工作(可能需要調(diào)用System.runFinalizersOnExit(true),從而確保這一行為)。
14. 在一個特定的作用域內(nèi),若一個對象必須清除(非由垃圾收集機(jī)制處理),請采用下述方法:初始化對象;若成功,則立即進(jìn)入一個含有finally從句的try塊,開始清除工作。
15. 若在初始化過程中需要覆蓋(取消)finalize(),請記住調(diào)用super.finalize()(若Object屬于我們的直接超類,則無此必要)。在對finalize()進(jìn)行覆蓋的過程中,對super.finalize()的調(diào)用應(yīng)屬于最后一個行動,而不應(yīng)是第一個行動,這樣可確保在需要基礎(chǔ)類組件的時候它們依然有效。
16. 創(chuàng)建大小固定的對象集合時,請將它們傳輸至一個數(shù)組(若準(zhǔn)備從一個方法里返回這個集合,更應(yīng)如此操作)。這樣一來,我們就可享受到數(shù)組在編譯期進(jìn)行類型檢查的好處。
17. 盡量使用interfaces,不要使用abstract類。若已知某樣?xùn)|西準(zhǔn)備成為一個基礎(chǔ)類,那么第一個選擇應(yīng)是將其變成一個interface(接口)。只有在不得不使用方法定義或者成員變量的時候,才需要將其變成一個abstract(抽象)類。接口主要描述了客戶希望做什么事情,而一個類則致力于(或允許)具體的實(shí)施細(xì)節(jié)。
18. 在構(gòu)建函數(shù)內(nèi)部,只進(jìn)行那些將對象設(shè)為正確狀態(tài)所需的工作。盡可能地避免調(diào)用其他方法,因?yàn)槟切┓椒赡鼙黄渌烁采w或取消,從而在構(gòu)建過程中產(chǎn)生不可預(yù)知的結(jié)果。
19. 對象不應(yīng)只是簡單地容納一些數(shù)據(jù);它們的行為也應(yīng)得到良好的定義。
20. 警惕“過早優(yōu)化”。首先讓它運(yùn)行起來,再考慮變得更快——但只有在自己必須這樣做、而且經(jīng)證實(shí)在某部分代碼中的確存在一個性能瓶頸的時候,才應(yīng)進(jìn)行優(yōu)化。除非用專門的工具分析瓶頸,否則很有可能是在浪費(fèi)自己的時間。性能提升的隱含代價是自己的代碼變得難于理解,而且難于維護(hù)。
21. 請記住,閱讀代碼的時間比寫代碼的時間多得多。思路清晰的設(shè)計(jì)可獲得易于理解的程序,但注釋、細(xì)致的解釋以及一些示例往往具有不可估量的價值。無論對你自己,還是對后來的人,它們都是相當(dāng)重要的。如對此仍有懷疑,那么請?jiān)囅胱约涸噲D從聯(lián)機(jī)Java文檔里找出有用信息時碰到的挫折,這樣或許能將你說服。
22. 如認(rèn)為自己已進(jìn)行了良好的分析、設(shè)計(jì)或者實(shí)施,那么請稍微更換一下思維角度。試試邀請一些外來人士——并不一定是專家,但可以是來自本公司其他部門的人。請他們用完全新鮮的眼光考察你的工作,看看是否能找出你一度熟視無睹的問題。采取這種方式,往往能在最適合修改的階段找出一些關(guān)鍵性的問題,避免產(chǎn)品發(fā)行后再解決問題而造成的金錢及精力方面的損失。
23. 良好的設(shè)計(jì)能帶來最大的回報。簡言之,對于一個特定的問題,通常會花較長的時間才能找到一種最恰當(dāng)?shù)慕鉀Q方案。但一旦找到了正確的方法,以后的工作就輕松多了,再也不用經(jīng)歷數(shù)小時、數(shù)天或者數(shù)月的痛苦掙扎。我們的努力工作會帶來最大的回報(甚至無可估量)。而且由于自己傾注了大量心血,最終獲得一個出色的設(shè)計(jì)方案,成功的快感也是令人心動的。堅(jiān)持抵制草草完工的誘惑——那樣做往往得不償失。