Java 寫碼標準

Java 寫碼標準

整理:蔡煥麟
日期:Oct-4-2003


1.0 簡介

1.1 資料來源

這份文件中所建議的寫碼標準主要參考自下列文件:

大部分規則都相同,僅有極少部分的修改,例如:縮排使用 2 個空白字元。

1.2 為何需要寫碼標準?

  • 軟體生命週期中,維護就佔了 80%。
  • 幾乎沒有任何一個軟體從頭到尾(整個生命週期)都是由原始作者在維護。
  • 寫碼慣例提高了程式的可讀性,讓程式設計師能更快地了解別人的程式碼。
  • 如果程式碼是產品的一部分,你得確保它跟其他產品有相同的品質。

2.0 檔案名稱

Java 原始碼:*.java

Java bytecode:*.class

3.0 檔案組織

通用慣例:
  • 一個檔案裡面若可區分成數個段落,各段落之間應以一個空白行隔開,並視需要加上註解。
  • 檔案的內容若超過 2000 行,就應予以切割。

3.1 Java 原始碼

Java 原始碼檔案的組成分子,依照出現的順序排列為:

  • 起始註解。
  • package 和 import 敘述(先寫 package,再寫 import)。
  • 類別或介面宣告。
3.1.1 起始註解

每個 Java 程式碼檔案的開頭都要有 C-style 的註解,參考下列範例:

英文範例:

/*
 * Description
 *
 *   A brief description of the class/interface.
 *
 * History
 *
 *   yyyy-mm-dd Author        
 *              What has been changed.
 *
 * Copyright notice
 */
中文範例:
/*
 * 說明
 *
 *   簡短描述這個類別/介面。
 *
 * 修改歷史
 *
 *   yyyy-mm-dd 作者姓名
 *              改了哪些東西。
 *
 * 版權聲明
 */
3.1.2 類別和介面宣告
類別和介面各個組成分子,依照應該出現的順序排列如下:

順序項目備註
1類別/介面的註解
2class 或 interface 敘述
3類別或介面的實作註解(有需要才寫)
4類別變數(宣告為 static 的變數)依照 public,protected, 和 private 的順序宣告。
5Instance 變數依照 public,protected, 和 private 的順序宣告。
6建構函式
7finalize()
8方法相關功能的方法應該擺在一起,而不是以 public 或 private 存取範圍來區分。這樣安排的目的是為了增加程式的可讀性。

參考 10.0 Java 程式碼範例

4.0 縮排(Indentation)

每次縮排為 2 個空格,並且使用 Tab 字元來縮排,亦即修改編輯器的設定,將 Tab 字元以 2 個空白字元來顯示(大部分的文書編輯器都有提供這類功能),注意不是按 Tab 鍵時輸入 2 個空白字元。

4.1 邊界

每道敘述不要超過 80 個字元,範例程式則建議在 70 個字元以下。

4.2 折行

程式碼若太長,會超出邊界時,可採取下列方式折行:

  • 逗號之後折行。
  • 運算元之前折行。
  • 被折下去的那行敘述,跟前一道敘述中相同階層的表示式對齊。
  • 如果上面的方式會造成程式碼太逼近右邊界(過度深層的縮排),可單純以 8 個空白字元縮排。
  • 盡量在外層折行。

範例:

// 折行後,跟前一道敘述中同階層的表示式對齊。
var = someMethod1(longExpression1,
                  someMethod2(longExpression2,
                              longExpression3));

// 折行後以縮排兩次,以避免過度深層的縮排
var = someMethod(longExpression1, longExpression2, longExpression3,
    longExpression4, longExpression5);  

// 在外層折行,正確。
longName1 = longName2 * (longName3 + longName4 - longName5)
            + 4 * longname6;

// 在內層折行,錯誤!應盡量避免
longName1 = longName2 * (longName3 + longName4
            - longName5) + 4 * longname6;

if ... else 的折行規則也是用兩次縮排,若只用一次縮排,if 敘述折行後會跟 if 區塊內的敘述對齊,使得不易辨識區塊主體,例如:

// 錯誤示範
if ((condition1 && condition2)
  || (condition3 && condition4)
  ||!(condition5 && condition6)) {   
  doSomethingAboutIt();              
} 

// 正確寫法
if ((condition1 && condition2)
    || (condition3 && condition4)
    ||!(condition5 && condition6)) {
  doSomethingAboutIt();
} 

// 若只縮排一次,則條件式和區塊主體之間要空一行
if ((condition1 && condition2) 
  || (condition3 && condition4)
  ||!(condition5 && condition6)) {

  doSomethingAboutIt();
} 

5.0 註解

通用慣例:
  • 註解要能幫助人們閱讀及了解程式碼。
  • 如果你的程式不值得寫註解,那你的程式可能也沒多大用處。
  • 避免過度裝飾,例如:把註解加框,讓它看起來像一幅廣告。
  • 要簡潔有力。
  • 先寫註解,才寫 code。
  • 好的註解要說明 why(為什麼要寫這段,為什麼要這樣寫...等等),而不只是說明 what (這段程式碼在做什麼)而已。

Java 的註解主要分成兩種:

  1. 實作註解(implementation comments),寫法跟 C++ 的註解一樣,有 /*...*/ 多行註解和 // 單行註解這兩種型式。
  2. 文件註解(documentation comments),是 Java 特有的註解型式,寫法是 /**...*/,這類註解可以被 javadoc 工具讀取並產生 HTML 說明文件。

實作註解主要是用來說明程式碼,文件註解則用來描述程式碼的規格。

註:如果程式當中到處都是註解,可能意味著程式碼的品質較差,當然這並不是要你把註解拿掉或乾脆不寫註解,而是希望你注意到,當你覺得若不寫這麼多冗長的註解就很難讓人了解程式碼時,你可能要考慮用更好的方式改寫這段程式碼。

5.1 實作註解

5.1.1 區塊註解(多行註解)

區塊註解可以用在檔案標頭、每個函式之前、或程式碼中的任何地方。區塊註解的前面應該要有一列空白。例如:

someMethod();

/*
 * Here is a block comment.
 */
hello();

不要用很多 * 把註解框起來,像下面這樣:

/*********************
?*??????????????????????????????? *
?*? 這是個錯誤示範?????????? *
?*??????????????????????????????? *
?*********************/

5.1.2 單行註解

比較短的註解可以使用單行註解,單行註解可以用 /*...*/ 或 // 兩種寫法,建議使用後者,它也常被用來將一段程式碼註解掉(comment out),參考下面的範例:

if (foo > 1) {

  // Do a double-flip. 注意前面要空一行
  ...
}
else {
  return false;          // 寫在這裡的叫做 end-of-line comments
}
//if (bar > 1) {
//
//  // Do a triple-flip.
//  ...
//}
//else {
//  return false;
//}

5.2 文件註解

請參考 http://java.sun.com/j2se/javadoc/writingdoccomments/index.html

6.0 宣告

6.1 一行一個宣告

建議一行只寫一個宣告,這樣有利於撰寫註解,例如下面的寫法:

int level; // Indentation level
int size;  // Size of table

比這種寫法好:

int level, size;
最糟糕的是把不同用途的東西擺在同一行宣告,例如:
int foo, fooarray[];  // WRONG!

註:以上的寫法都是用一個空白字元來分隔型態和變數名稱,另一種可以接受的寫法是用 tab 字元,例如:

int  level;        // Indentation level
int  size;         // Size of table
Object  currentEntry; // Currently selected table entry

6.2 初始化

儘量在宣告變數時就設定初始值。

6.3 宣告的位置

儘管 Java 允許你在任何地方宣告變數,把宣告集中在程式區塊一開始的地方(由 {...} 包住的程式區塊)會比較清楚。參考下面的範例:

void myMethod() {
  int int1 = 0;       // 正確

  if (condition) {
    int int2 = 0;   // 正確
    ...
  }

  int int3 = 0;       // 錯誤,應該跟 int1 的宣告在一起
}

唯一的例外是 for 迴圈的索引變數,它可以直接宣告在 for 敘述裡面,例如:

for (int i = 0; i < maxLoops; i++) {
  ... 
}

注意不要讓區域變數蓋過外層的變數,例如:

int count;
...
myMethod() {
  if (condition) {
    int count; // 錯誤!容易讓人混淆,不易除錯
    ...
  }
  ...
}

6.4 類別和介面的宣告

在撰寫類別和介面時,應遵守下列規則:

  • 方法名稱和左括號 '(' 之間不要有空白。
  • 左大括號 '{' 出現在同一行程式敘述的後面,而不是獨立一行。
  • 右大括號 '}' 應該是單獨一行,而且要跟它對應的程式區塊對齊。唯一的例外是,若程式區塊中沒有任何敘述(空的區塊),'}' 應該緊跟在 '{' 後面。
  • 方法和方法之間要空一行。

範例:

class Sample extends Object {
  int ivar1;
  int ivar2;
  Sample(int i, int j) {
    ivar1 = i;
    ivar2 = j;
  }
  int emptyMethod() {}
  ...
}

7.0 敘述(statements)

7.1 簡單敘述

一行只能寫一個敘述,例如:

argv++; // 正確
argc++; // 正確
argv++; argc--; // 錯誤!

7.2 複合敘述

所謂的複合敘述,指的就是以大括號 '{...}' 包住的敘述,通用的規則是:

  • 左大括號 '{' 出現在同一行程式敘述的後面,而不是獨立一行。
  • 右大括號 '}' 應該是單獨一行,而且要跟它對應的程式區塊對齊。
  • 當你在寫 if-else, for, while...等敘述時,不管其中的程式只有一行還是多行,一律都要用大括號包住,以免日後加入新的程式碼時忘了加括號。

7.3 return 敘述

單純的傳回值不要加括號,例如:

return;
return myDisk.size();

若傳回值使用了較複雜的敘述,可以使用括號,例如:

return (size ? size : defaultSize);

7.4 if, if-else, if else-if else 敘述

if-else 敘述應依照下列格式撰寫:

if ( condition) {
  statements;
}
if ( condition) {
  statements;
} else {
  statements;
}
if ( condition) {
  statements;
} 
else if ( condition) { // else 也可以自己獨立一行
  statements;
} 
else {   
  statements;
}

注意:不管單行還是多行敘述,都要用使用大括號。

7.5 for 敘述

for 敘述應依照下列格式撰寫 :

for (initialization; condition; update) {
  statements;
}

空的 for 敘述:

for (initialization; condition; update);

initialization 裡面用到的變數不要超過三個。

7.6 while 敘述

while 敘述應依照下列格式撰寫 :

while (condition) {
  statements;
}

空的 while 敘述:

while (condition);

7.7 do-while 敘述

do-while 敘述應依照下列格式撰寫 :

do {
  statements;
} while (conditions);

7.8 switch 敘述

switch 敘述應依照下列格式撰寫 :

switch (condition) {
case ABC:  // case 不需要縮排
  statements;
  // 往下繼續執行
case DEF:
  statements;
  break;
case XYZ:
  statements;
  break;
default:
  statements;
  break;  // 若 default 是最後一個 case,這裡的 break 可不寫。
}

當一個 case 裡面沒有寫 break 時,程式會繼續往下執行,這時必須在這個 case 往下執行的地方加上註解,就像上面的範例一樣,這樣可以避免粗心的程式設計師誤解程式流程。

7.9 try-catch 敘述

try-catch 敘述應依照下列格式撰寫 :

try {
  statements;
} 
catch (ExceptionClass e) {
  statements;
}
try {
  statements;
} 
catch (ExceptionClass e) {
  statements;
} 
finally {
  statements;
}

8.0 空白

8.1 空白行

適當地加入空白行可以區分程式段落,提高可讀性。以下情況應該加入一行空白行:

  • 方法和方法之間。
  • 區域變數和第一行程式敘述之間。
  • 程式區塊之前。
  • 單行註解之前。
  • 在一個方法裡面,可邏輯切割的程式段落之間(增進可讀性)。

以下情況應該加入兩行空白行:

  • 類別/介面之間。
  • 在一個原始碼檔案裡面,可邏輯切割的程式段落之間。

8.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)
  • 轉型時,例如:
myMethod((byte) aNum, (Object) x);
myMethod((int) (cp + 5), ((int) (i + 3)) + 1);

9.0 命名慣例

9.1 Java 命名慣例

通用慣例:
  • 使用完整的、能夠望文生義的英文。
  • 使用跟該領域相關的術語。
  • 大小寫混合使用,以增加可讀性。
  • 名稱盡量不要超過 15 個字元。
  • 不要只用大小寫的差異來辨別兩個變數名稱,這樣容易造成混淆。

項目命名慣例範例
Parameters
參數
使用完整的英文來描述傳遞的變數/物件,可以在前面冠上 'a' 或 'an',不管用哪種方式,最重要的是定下來以後就要遵守它。customer, account 或
aCustomer, anAccount
Fields/properties
欄位/屬性?
使用完整的英文,第一個單字(word)的第一個字母小寫,其餘的單字的第一個字母大寫。firstName, lastName
傳回布林型態的 getter 成員函式開頭一律冠上 'is'。isDead, isString, isFailed
Classes
類別
使用完整的英文,類別名稱的每個單字的第一個字母大寫,其餘字母為小寫。Customer, SavingAccount
Compilation unit files
檔案
以類別名稱作為主檔名,附屬檔名為小寫的'.java'。Customer.java, SavingAccount.java
Exceptions
異常
通常以小寫字母 'e' 代表異常物件。e
Final static fields (consants)
常數
全部使用大寫英文字母,每個單字以底線分隔。MIN_BALANCE,
DEFAULT_DATE
Getter 成員函式把要存取的欄位名稱前面冠上 'get'。getFirstName(),
getLastName()
Interfaces
介面
跟類別的命名規則相同,使用完整的英文,介面名稱的每個單字的第一個字母都大寫,其餘小寫。如果可能的話,盡量使用形容詞,也就是 'able','ible' 等字尾的名稱(不是絕對必要)。Runnable, Contactable, Singleton
Local variables
區域變數
使用完整的英文,第一個單字(word)的第一個字母小寫,其餘的單字的第一個字母大寫。注意:應避免和類別的欄位名稱相同,以免產生混淆,例如,你有一個欄位名稱是 'firstName',那麼在成員函式中的區域變數就不要取相同的名稱。grandTotal, customer, newAccount
Loop counters
迴圈計數器
通常以 'i', 'j', 'k' 或 'counter' 來命名。i, j, k, cnt, counter
Packages
套件
使用完整的英文,通常全都小寫。為了避免和其他套件名稱衝突,一般會將公司或組織的 internet 網域名稱順序顛倒過來,再加上套件名稱,不用把網域名稱的每個部分都加進去,用一個或兩個就行了。java.awt,
com.company.utility
com.apple.quicktime.v2
Member functions
成員函式
使用完整的英文,第一個單字(word)的第一個字母小寫,其餘的單字的第一個字母大寫,盡可能以動詞開頭openFile(), addAccount()
Setter 成員函式把要存取的欄位名稱前面冠上 'set'。setFirstName(),
setLastName()

區域變數的命名可以稍有彈性,對於比較不重要的變數,可以採取下列簡短的命名方式,但請特別注意,不可濫用,原則上仍應該盡量以完整的英文來命名:

變數類型建議的命名慣例
offsetoff
lengthlen
charc 或 ch
floatf
Objecto 或 obj
Strings 或 str

10.0 Java 程式碼範例

/*
 * @(#)Blah.java        1.82 99/03/18
 *
 * Copyright (c) 1994-1999 Sun Microsystems, Inc.
 * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of Sun
 * Microsystems, Inc. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Sun.
 */


package java.blah;

import java.blah.blahdy.BlahBlah;

/**
 * Class description goes here.
 *
 * @version  1.82 18 Mar 1999
 * @author  Firstname Lastname
 */
public class Blah extends SomeClass {
  /* A class implementation comment can go here. */

  /** classVar1 documentation comment */
  public static int classVar1;

  /** 
   * classVar2 documentation comment that happens to be
   * more than one line long
   */
  private static Object classVar2;

  /** instanceVar1 documentation comment */
  public Object instanceVar1;

  /** instanceVar2 documentation comment */
  protected int instanceVar2;

  /** instanceVar3 documentation comment */
  private Object[] instanceVar3;

  /** 
   * ...constructor Blah documentation comment...
   */
  public Blah() {
    // ...implementation goes here...
  }

  /**
   * ...method doSomething documentation comment...
   */
  public void doSomething() {
    // ...implementation goes here... 
  }

  /**
   * ...method doSomethingElse documentation comment...
   * @param someParam description
   */
  public void doSomethingElse(Object someParam) {
    // ...implementation goes here... 
  }
}

11.0 其他建議

  • 程式中如果要用到很多次字串相加,應改用 StringBuffer,以增加程式執行的效能,因為字串相加會需要額外建立 String 物件的時間和記憶體資源。
  • 隨時注意可能會引發 null pointer assignment 異常的程式碼,考慮物件參考為 null 時應如何處理。
  • 要使用 String.equals() 來比較某個 String 物件和字串常數時,將字串常數寫在左邊,這樣可以避免 String 物件為 null 時所引發的錯誤,例如:
if ("Michael".equals(s)) 
  • 盡量捕捉明確的異常類別,例如:FileNotFoundException,不要全都使用 Exception。
  • 不要用物件參考存取類別的靜態成員。
  • 有特殊意義的數值應定義成常數,不要把數值寫死在程式裡。你可以用 final static 來定義常數。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值