Java代碼規範

Java代碼規範

本Java代碼規範以SUN的標準Java代碼規範爲基礎,爲適應我們公司的實際需要,可能會做一些修改。本文檔中沒有說明的地方,請參看SUN Java標準代碼規範。如果兩邊有衝突,以本文檔爲準。

1. 標識符命名規範

1.1 概述

標識符的命名力求做到統一、達意和簡潔。

1.1.1 統一

統一是指,對於同一個概念,在程序中用同一種表示方法,比如對於供應商,既可以用supplier,也可以用provider,但是我們只能選定一個使用,至少在一個Java項目中保持統一。統一是作爲重要的,如果對同一概念有不同的表示方法,會使代碼混亂難以理解。即使不能取得好的名稱,但是隻要統一,閱讀起來也不會太困難,因爲閱讀者只要理解一次。

1.1.2 達意

達意是指,標識符能準確的表達出它所代表的意義,比如: newSupplier, OrderPaymentGatewayService等;而 supplier1, service2,idtts等則不是好的命名方式。準確有兩成含義,一是正確,而是豐富。如果給一個代表供應商的變量起名是 order,顯然沒有正確表達。同樣的,supplier1, 遠沒有targetSupplier意義豐富。

1.1.3 簡潔

簡潔是指,在統一和達意的前提下,用盡量少的標識符。如果不能達意,寧願不要簡潔。比如:theOrderNameOfTheTargetSupplierWhichIsTransfered 太長, transferedTargetSupplierOrderName則較好,但是transTgtSplOrdNm就不好了。省略元音的縮寫方式不要使用,我們的英語往往還沒有好到看得懂奇怪的縮寫。

1.1.4 駱駝法則

Java中,除了包名,靜態常量等特殊情況,大部分情況下標識符使用駱駝法則,即單詞之間不使用特殊符號分割,而是通過首字母大寫來分割。比如: SupplierName, addNewContract,而不是 supplier_name, add_new_contract。

1.1.5 英文 vs 拼音

儘量使用通俗易懂的英文單詞,如果不會可以向隊友求助,實在不行則使用漢語拼音,避免拼音與英文混用。比如表示歸檔,用archive比較好, 用pigeonhole則不好,用guiDang尚可接受。

1.2 包名

使用小寫字母如 com.xxx.settlment,不要 com.xxx.Settlement
單詞間不要用字符隔開,比如 com.xxx.settlment.jsfutil,而不要com.xxx.settlement.jsf_util

1.3 類名

1.3.1 首字母大寫

類名要首字母大寫,比如 SupplierService, PaymentOrderAction;不要 supplierService, paymentOrderAction.

1.3.2 後綴

類名往往用不同的後綴表達額外的意思,如下表:

後綴名 意義 舉例
Service 表明這個類是個服務類,裏面包含了給其他類提同業務服務的方法 PaymentOrderService
Impl 這個類是一個實現類,而不是接口 PaymentOrderServiceImpl
Inter 這個類是一個接口 LifeCycleInter
Dao 這個類封裝了數據訪問方法 PaymentOrderDao
Action 直接處理頁面請求,管理頁面邏輯了類 UpdateOrderListAction
Listener 響應某種事件的類 PaymentSuccessListener
Event 這個類代表了某種事件 PaymentSuccessEvent
Servlet 一個Servlet PaymentCallbackServlet
Factory 生成某種對象工廠的類 PaymentOrderFactory
Adapter 用來連接某種以前不被支持的對象的類 DatabaseLogAdapter
Job 某種按時間運行的任務 PaymentOrderCancelJob
Wrapper 這是一個包裝類,爲了給某個類提供沒有的能力 SelectableOrderListWrapper
Bean 這是一個POJO MenuStateBean

1.4 方法名

首字母小寫,如 addOrder() 不要 AddOrder()
動詞在前,如 addOrder(),不要orderAdd()
動詞前綴往往表達特定的含義,如下表:

前綴名 意義 舉例
create 創建 createOrder()
delete 刪除 deleteOrder()
add 創建,暗示新創建的對象屬於某個集合 addPaidOrder()
remove 刪除 removeOrder()
init或則initialize 初始化,暗示會做些諸如獲取資源等特殊動作 initializeObjectPool
destroy 銷燬,暗示會做些諸如釋放資源的特殊動作 destroyObjectPool
open 打開 openConnection()
close 關閉 closeConnection()<
read 讀取 readUserName()
write 寫入 writeUserName()
get 獲得 getName()
set 設置 setName()
prepare 準備 prepareOrderList()
copy 複製 copyCustomerList()
modity 修改 modifyActualTotalAmount()
calculate 數值計算 calculateCommission()
do 執行某個過程或流程 doOrderCancelJob()
dispatch 判斷程序流程轉向 dispatchUserRequest()
start 開始 startOrderProcessing()
stop 結束 stopOrderProcessing()
send 發送某個消息或事件 sendOrderPaidMessage()
receive 接受消息或時間 receiveOrderPaidMessgae()
respond 響應用戶動作 responseOrderListItemClicked()
find 查找對象 findNewSupplier()
update 更新對象 updateCommission()

find方法在業務層儘量表達業務含義,比如 findUnsettledOrders(),查詢未結算訂單,而不要findOrdersByStatus()。 數據訪問層,find,update等方法可以表達要執行的sql,比如findByStatusAndSupplierIdOrderByName(Status.PAID, 345)

1.5 域(field)名

1.5.1 靜態常量

全大寫用下劃線分割,如


public static find String ORDER_PAID_EVENT = "ORDER_PAID_EVENT";

1.5.2 枚舉

全大寫,用下劃線分割,如

public enum Events {
ORDER_PAID,
ORDER_CREATED
}

1.5.3 其他

首字母小寫,駱駝法則,如:

public String orderName;

1.6 局部變量名

參數和局部變量名首字母小寫,駱駝法則。儘量不要和域衝突,儘量表達這個變量在方法中的意義。

2. 代碼格式

用空格字符縮進源代碼,不要用tab,每個縮進4個空格。

2.1 源文件編碼

源文件使用utf-8編碼,結尾用unix n 分格。

2.2 行寬

行寬度不要超過130。

2.3 包的導入

刪除不用的導入,儘量不要使用整個包的導入。在eclipse下經常使用快捷鍵 ctrl+shift+o 修正導入。

2.4 類格式

2.5 域格式

每行只能聲明一個域。
域的聲明用空行隔開。

2.5 方法格式

2.6 代碼塊格式

2.6.1 縮進風格

大括號的開始在代碼塊開始的行尾,閉合在和代碼塊同一縮進的行首,例如:

package com.test;

public class TestStyle extends SomeClass implements AppleInter, BananaInter {
public static final String THIS_IS_CONST = "CONST VALUE";

private static void main(String[] args) {
int localVariable = 0;
}

public void compute(String arg) {
if (arg.length() > 0) {
System.out.println(arg);
}

for (int i = 0; i < 10; i++) {
System.out.println(arg);
}

while (condition) {

}

do {
otherMethod();
} while (condition);

switch (i) {
case 0:
callFunction();
break;
case 1:
callFunctionb();
break;
default:
break;
}
}
}

2.6.2 空格的使用

2.6.2.1 表示分割時用一個空格

不能這樣:

if ( a > b ) {
//do something here
};

2.6.2.2 二元三元運算符兩邊用一個空格隔開

如下:

a + b = c;
b - d = e;
return a == b ? 1 : 0;

不能如下:


a+b=c;
b-d=e;
return a==b?1:0;

2.6.2.3 逗號語句後如不還行,緊跟一個空格

如下:

call(a, b, c);

不能如下:

call(a,b,c);

2.6.3 空行的使用

空行可以表達代碼在語義上的分割,註釋的作用範圍,等等。將類似操作,或一組操作放在一起不用空行隔開,而用空行隔開不同組的代碼, 如圖:

order = orderDao.findOrderById(id);

//update properties
order.setUserName(userName);
order.setPrice(456);
order.setStatus(PAID);

orderService.updateTotalAmount(order);

session.saveOrUpdate(order);

上例中的空行,使註釋的作用域很明顯.

  • 連續兩行的空行代表更大的語義分割。
  • 方法之間用空行分割
  • 域之間用空行分割
  • 超過十行的代碼如果還不用空行分割,就會增加閱讀困難

3. 註釋規範

3.1 註釋 vs 代碼

  • 註釋宜少二精,不宜多而濫,更不能誤導
  • 命名達意,結構清晰, 類和方法等責任明確,往往不需要,或者只需要很少註釋,就可以讓人讀懂;相反,代碼混亂,再多的註釋都不能彌補。所以,應當先在代碼本身下功夫。
  • 不能正確表達代碼意義的註釋,只會損害代碼的可讀性。
  • 過於詳細的註釋,對顯而易見的代碼添加的註釋,羅嗦的註釋,還不如不寫

  • 註釋要和代碼同步,過多的註釋會成爲開發的負擔
  • 註釋不是用來管理代碼版本的,如果有代碼不要了,直接刪除,svn會有記錄的,不要註釋掉,否則以後沒人知道那段註釋掉的代碼該不該刪除。

3.2 Java Doc

表明類、域和方法等的意義和用法等的註釋,要以javadoc的方式來寫。Java Doc是個類的使用者來看的,主要介紹 是什麼,怎麼用等信息。凡是類的使用者需要知道,都要用Java Doc 來寫。非Java Doc的註釋,往往是個代碼的維護者看的,着重告述讀者爲什麼這樣寫,如何修改,注意什麼問題等。 如下:

/**
* This is a class comment
*/
public class TestClass {
/**
* This is a field comment
*/
public String name;

/**
* This is a method comment
*/
public void call() {

}
}

3.3 塊級別註釋

3.3.1 塊級別註釋,單行時用 //, 多行時用 /* .. */。

3.3.2 較短的代碼塊用空行表示註釋作用域

3.3.3 較長的代碼塊要用


/*------ start: ------*/

/*-------- end: -------*/

包圍
如:

/*----------start: 訂單處理 ------- */
//取得dao
OrderDao dao = Factory.getDao("OrderDao");

/* 查詢訂單 */
Order order = dao.findById(456);

//更新訂單
order.setUserName("uu");
order.setPassword("pass");
order.setPrice("ddd");

orderDao.save(order);
/*----------end: 訂單處理 ------- */

3.3.4 可以考慮使用大括號來表示註釋範圍

使用大括號表示註釋作用範圍的例子:

/*----------訂單處理 ------- */
{
//取得dao
OrderDao dao = Factory.getDao("OrderDao");

/* 查詢訂單 */
Order order = dao.findById(456);

//更新訂單
order.setUserName("uu");
order.setPassword("pass");
order.setPrice("ddd");

orderDao.save(order);
}

3.4 行內註釋

行內註釋用 // 寫在行尾

4 最佳實踐和禁忌

4.1 每次保存的時候,都讓你的代碼是最美的

程序員都是懶惰的,不要想着等我完成了功能,再來優化代碼的格式和結構,等真的把功能完成,很少有人會再願意回頭調整代碼。

4.2 使用log而不是System.out.println()

log可以設定級別,可以控制輸出到哪裏,容易區分是在代碼的什麼地方打印的,而System.out.print則不行。而且,System.out.print的速度很慢。所以,除非是有意的,否則,都要用log。至少在提交到svn之前把System.out.print換成log。

4.3 每個if while for等語句,都不要省略大括號{}

看下面的代碼:

if (a > b)
a++;

如果在以後維護的時候,需要在a > b 時,把b++,一步小心就會寫成:

if (a > b)
a++;
b++;

這樣就錯了,因爲無論a和b是什麼關係,b++都會執行。 如果一開始就這樣寫:

if (a > b) {
a++;
}

相信沒有哪個笨蛋會把b++添加錯的。而且,這個大括號使作用範圍更明顯,尤其是後面那行很長要折行時。

4.4 善用TODO:

在代碼中加入 //TODO: ,大部分的ide都會幫你提示,讓你知道你還有什麼事沒有做。比如:

if (order.isPaid()) {
//TODO: 更新訂單
}

4.5 在需要留空的地方放一個空語句或註釋,告述讀者,你是故意的

比如:

if (!exists(order)) {
;
}

或:


if (!exists(order)) {
//nothing to do
}

4.6 不要再對boolean值做true false判斷

比如:

if (order.isPaid() == true) {
// Do something here
}

不如寫成:

if (order.isPaid()) {
//Do something here
}

後者讀起來就很是 if order is paid, .... 要比 if order's isPaid method returns true, … 更容易理解

4.7 減少代碼嵌套層次

代碼嵌套層次達3層以上時,一般人理解起來都會困難。下面的代碼是一個簡單的例子:

public void demo(int a, int b, int c) {
if (a > b) {
if (b > c) {
doJobA();
} else if (b < c) {
doJobB()
}
} else {
if (b > c) {
if (a < c) {
doJobC();
}
}
}
}

減少嵌套的方法有很多:

  • 合併條件
  • 利用 return 以省略後面的else
  • 利用子方法

比如上例,合併條件後成爲:

public void demo(int a, int b, int c) {
if (a > b && b > c) {
doJobA();
}
if (a > b && c > b) {
doJobB();
}
if (a <= b && c < b && a < c) {
doJobC();
}
}

如果利用return 則成爲:

public void demo(int a, int b, int c) {
if (a > b) {
if (b > c) {
doJobA();
return;
}
doJobB()
return;
}

if (b > c) {
if (a < c) {
doJobC();
}
}
}

利用子方法,就是將嵌套的程序提取出來放到另外的方法裏。

4.8 程序職責單一

關注點分離是軟件開發的真理。人類自所以能夠完成複雜的工作,就是因爲人類能夠將工作分解到較小級別的任務上,在做每個任務時關注更少的東西。讓程序單元的職責單一,可以使你在編寫這段程序時關注更少的東西,從而降低難度,減少出錯。

4.9 變量的聲明,初始化和被使用盡量放到一起

比方說如下代碼:

int orderNum= getOrderNum();

//do something withou orderNum here

call(orderNum);

上例中的註釋處代表了一段和orderNum不相關的代碼。orderNum的聲明和初始化離被使用的地方相隔了很多行的代碼,這樣做不好,不如這樣:

//do something withou orderNum here

int orderNum= getOrderNum();
call(orderNum);

4.10 縮小變量的作用域

能用局部變量的,不要使用實例變量,能用實例變量的,不要使用類變量。變量的生存期越短,以爲着它被誤用的機會越小,同一時刻程序員要關注的變量的狀態越少。實例變量和類變量默認都不是線程安全的,局部變量是線程安全的。比如如下代碼:

public class OrderPayAction{
private Order order;

public void doAction() {
order = orderDao.findOrder();
doJob1();
doJob2();
}

private void doJob1() {
doSomething(order);
}

private void doJob2() {
doOtherThing(order);
}
}

上例中order只不過擔當了在方法間傳遞參數之用,用下面的方法更好:

public class OrderPayAction{

public void doAction() {
order = orderDao.findOrder();
doJob1(order);
doJob2(order);
}

private void doJob1(Order order) {
doSomething(order);
}

private void doJob2(Order order) {
doOtherThing(order);
}
}

4.11 儘量不要用參數來帶回方法運算結果

比如:

public void calculate(Order order) {
int result = 0;
//do lots of computing and store it in the result

order.setResult(result);
}

public void action() {
order = orderDao.findOrder();
calculate(order);

// do lots of things about order
}

例子中calculate方法通過傳入的order對象來存儲結果, 不如如下寫:

public int calculate(Order order) {
int result = 0;
//do lots of computing and store it in the result

return result;
}

public void action() {
order = orderDao.findOrder();
order.setResult(calculate(order));

// do lots of things about order
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章