【轉帖】Java項目開發規範

來源:互聯網
上載者:User

一、目的   

  對於代碼,首要要求是它必須正確,能夠按照程式員的真實思想去運行;第二個的要求是代碼必須清晰易懂,使別的程式員能夠容易理解代碼所進行的實際工作。在軟體工程領域,來源程式的風格統一標誌著可維護性、可讀性,是軟體項目的一個重要組成部分。而目前還沒有成文的編碼風格文檔,以致於很多時候,程式員沒有一個共同的標準可以遵守,編碼風格各異,程式可維護性差、可讀性也很差。通過建立代碼編寫規範,形成開發小組編碼約定,提高程式的可靠性、可讀性、可修改性、可維護性、可繼承性和一致性,可以保證程式碼的品質,繼承軟體開發成果,充分利用資源,使開發人員之間的工作成果可以共用。

    本文在參考業界已有的編碼風格的基礎上,描述了一個基於 JBuilder 的項目風格,力求一種統一的編程風格,並從整體編碼風格、代碼檔案風格、函數編寫風格、變數風格、注釋風格等幾個方面進行闡述。(這些規範並不是一定要絕對遵守,但是一定要讓程式有良好的可讀性)

二、整體編碼風格

1、縮排

    縮排建議以4個空格為單位。建議在 Tools/Editor Options 中設定 Editor 頁面的Block ident為4,Tab Size 為8。預先處理語句、全域資料、標題、附加說明、函數說明、標號等均頂格書寫。語句塊的"{"、"}"配對對齊,並與其前一行對齊,語句塊類的語句縮排建議每個"{"、"}"單獨佔一行,便於匹對。JBuilder 中的預設是開始的"{"不是單獨一行,建議更改成上述格式(在 Project/Default Project Properties 中設定 Code Style 中選擇 Braces 為 Next line)。

2、空格

    原則上變數、類、常量資料和函數在其類型,修飾名稱之間適當空格並據情況對齊。關鍵字原則上空一格,如:if ( ... ) 等。運算子的空格規定如下:"::"、"->;"、"["、"]"、"++"、"--"、"~"、"!"、"+"、"-"(指加號或減號)、"&"(引用)等幾個運算子兩邊不加空格(其中單目運算子系指與運算元相連的一邊),其它運算子(包括大多數二目運算子和三目運算子"?:"兩邊均加一空格,在作函數定義時還可據情況多空或不空格來對齊,但在函數實現時可以不用。","運算子只在其後空一格,需對齊時也可不空或多空格。不論是否有括弧,對語句行後加的注釋應用適當空格與語句隔開並儘可能對齊。個人認為此項可以依照個人習慣決定遵循與否。

3、對齊

    原則上關係密切的行應對齊,對齊包括類型、修飾、名稱、參數等各部分對齊。另每一行的長度不應超過螢幕太多,必要時適當換行,換行時儘可能在","處或運算子處,換行後最好以運算子打頭,並且以下各行均以該語句首行縮排,但該語句仍以首行的縮排為準,即如其下一行為“{”應與首行對齊。

    變數定義最好通過添加空格形成對齊,同一類型的變數最好放在一起。如下例所示:
int        Value;
int        Result;
int        Length;
DWORD      Size;
DWORD      BufSize;

 個人認為此項可以依照個人習慣決定遵循與否。

4、空行

 不得存在無規則的空行,比如說連續十個空行。程式檔案結構各部分之間空兩行,若不必要也可只空一行,各函數實現之間一般空兩行,由於每個函數還要有函數說明注釋,故通常只需空一行或不空,但對於沒有函數說明的情況至少應再空一行。對自己寫的函數,建議也加上“//------”做分隔。函數內部資料與代碼之間應空至少一行,代碼中適當處應以空行空開,建議在代碼中出現變數聲明時,在其前空一行。類中四個“p”之間至少空一行,在其中的資料與函數之間也應空行。

5、注釋

 注釋是軟體可讀性的具體體現。程式注釋量一般占程式編碼量的20%,軟體工程要求不少於20%。程式注釋不能用抽象的語言,類似於"處理"、"迴圈"這樣的電腦抽象語言,要精確表達出程式的處理說明。例如:"計算淨需求"、"計算第一道工序的加工工時"等。避免每行程式都使用注釋,可以在一段程式的前面加一段注釋,具有明確的處理邏輯。

 注釋必不可少,但也不應過多,不要被動的為寫注釋而寫注釋。以下是四種必要的注釋:
 
A. 標題、附加說明。

B. 函數、類等的說明。對幾乎每個函數都應有適當的說明,通常加在函數實現之前,在沒有函數實現部分的情況下則加在函數原型前,其內容主要是函數的功能、目的、演算法等說明,參數說明、傳回值說明等,必要時還要有一些如特別的軟硬體要求等說明。公用函數、公用類的聲明必須由註解說明其使用方法和設計思路,當然選擇恰當的命名格式能夠協助你把事情解釋得更清楚。

C. 在代碼不明晰或不可移植處必須有一定的說明。

D. 及少量的其它注釋,如自訂變數的注釋、代碼書寫時間等。

  注釋有塊注釋和行注釋兩種,分別是指:"/**/"和"//"建議對A用塊注釋,D用行注釋,B、C則視情況而定,但應統一,至少在一個單元中B類注釋形式應統一。具體對不同檔案、結構的注釋會在後面詳細說明。

6、代碼長度

 對於每一個函數建議儘可能控制其代碼長度為53行左右,超過53行的代碼要重新考慮將其拆分為兩個或兩個以上的函數。函數拆分規則應該一不破壞原有演算法為基礎,同時拆分出來的部分應該是可以重複利用的。對於在多個模組或者表單中都要用到的重複性代碼,完全可以將起獨立成為一個具備公用性質的函數,放置於一個公用模組中。

7、頁寬

 頁寬應該設定為80字元。原始碼一般不會超過這個寬度, 並導致無法完整顯示, 但這一設定也可以靈活調整. 在任何情況下, 超長的語句應該在一個逗號或者一個操作符後折行. 一條語句折行後, 應該比原來的語句再縮排2個字元.

8、行數

 一般的整合編程環境下,每屏大概只能顯示不超過50行的程式,所以這個函數大概要5-6屏顯示,在某些環境下要8屏左右才能顯示完。這樣一來,無論是讀程式還是修改程式,都會有困難。因此建議把完成比較獨立功能的程式塊抽出,單獨成為一個函數。把完成相同或相近功能的程式塊抽出,獨立為一個子函數。可以發現,越是上層的函數越簡單,就是調用幾個子函數,越是底層的函數完成的越是具體的工作。這是好程式的一個標誌。這樣,我們就可以在較上層函數裡容易控制整個程式的邏輯,而在底層的函數裡專註於某方面的功能的實現了。

三、代碼檔案風格

所有的 Java(*.java) 檔案都必須遵守如下的樣式規則:

. 檔案產生

對於規範的 JAVA 衍生類別,盡量用 JBuilder 的 Object Gallery 工具來組建檔案格式,避免用手工製作的標頭檔/實現檔案。
 
. package/import

package 行要在 import 行之前,import 中標準的包名要在本地的包名之前,而且按照字母順序排列。如果 import 行中包含了同一個包中的不同子目錄,則應該用 * 來處理。

package hotlava.net.stats;

import java.io.*;
import java.util.Observable;
import hotlava.util.Application; 
 
這裡 java.io.* 使用來代替InputStream and OutputStream 的。

. 檔案頭部注釋

檔案頭部注釋主要是表明該檔案的一些資訊,是程式的總體說明,可以增強程式的可讀性和可維護性。檔案頭部注釋一般位於 package/imports 語句之後,Class 描述之前。要求至少寫出檔案名稱、建立者、建立時間和內容描述。JBuilder 的 Object Gallery 工具產生的程式碼中會在類、工程檔案中等自動添加註釋,我們也要添加一些注釋,其格式應該盡量約束如下:

/**
 * Title:  確定滑鼠位置類
 * Description: 確定滑鼠當前在哪個作業欄位中並返回作業號
 * @Copyright: Copyright (c) 2002
 * @Company: HIT
 * @author: rivershan
 * @version: 1.0
 * @time: 2002.10.30
 */
 
. Class

接下來的是類的注釋,一般是用來解釋類的。

/**
 * A class representing a set of packet and byte counters
 * It is observable to allow it to be watched, but only
 * reports changes when the current set is complete
 */
 
接下來是類定義,包含了在不同的行的 extends 和 implements

public class CounterSet
  extends Observable
  implements Cloneable

.Class Fields

接下來是類的成員變數:

/**
 * Packet counters
 */
 
protected int[] packets;
 
public 的成員變數必鬚生成文檔(JavaDoc)。proceted、private和 package 定義的成員變數如果名字含義明確的話,可以沒有注釋。

. 存取方法
 
接下來是類變數的存取的方法。它只是簡單的用來將類的變數賦值擷取值的話,可以簡單的寫在一行上。(個人認為盡量分行寫)

/**
 * Get the counters
 * @return an array containing the statistical data.  This array has been
 * freshly allocated and can be modified by the caller.
 */
 
public int[] getPackets()
{
  return copyArray(packets, offset);
}

public int[] getBytes()
{
 return copyArray(bytes, offset);
}

public int[] getPackets()
{
 return packets;
}

public void setPackets(int[] packets)
{
 this.packets = packets;
}
 
其它的方法不要寫在一行上

. 建構函式

接下來是建構函式,它應該用遞增的方式寫(比如:參數多的寫在後面)。

訪問類型("public","private" 等.)和任何"static","final"或"synchronized"應該在一行中,並且方法和參數另寫一行,這樣可以使方法和參數更易讀。

public
CounterSet(int size)
{
   this.size = size;
}

. 複製方法
 
如果這個類是可以被複製的,那麼下一步就是 clone 方法:

public
Object clone()
{
 try
   {
     CounterSet obj = (CounterSet)super.clone();
     obj.packets = (int[])packets.clone();
     obj.size = size;
     return obj;
   } 
   catch(CloneNotSupportedException e)
   {
    throw new InternalError("Unexpected CloneNotSUpportedException: "
          + e.getMessage());
   }
}

. 類方法

下面開始寫類的方法:

/**
 * Set the packet counters
 * (such as when restoring from a database)
 */
protected final
void setArray(int[] r1, int[] r2, int[] r3, int[] r4)
  throws IllegalArgumentException
{
 //
   // Ensure the arrays are of equal size
   //
   if (r1.length != r2.length || r1.length != r3.length || r1.length != r4.length)
  throw new IllegalArgumentException("Arrays must be of the same size");
   System.arraycopy(r1, 0, r3, 0, r1.length);
   System.arraycopy(r2, 0, r4, 0, r1.length);
}

. toString 方法

無論如何,每一個類都應該定義 toString 方法:

public
String toString()
{
 String retval = "CounterSet: ";
    for (int i = 0; i < data.length(); i++)
    {
       retval += data.bytes.toString();
       retval += data.packets.toString();
    }
    return retval;
}

. main 方法

如果main(String[]) 方法已經定義了, 那麼它應該寫在類的底部.

四、函數編寫風格

. 函數的命名

通常,函數的命名也是以能表達函數的動作意義為原則的,一般是由動詞打頭,然後跟上表示動作對象的名詞,各單詞的首字母應該大寫。另外,還有一些函數命名的通用規則。如取數,則用Get打頭,然後跟上要取的對象的名字;設定數,則用Set打頭,然後跟上要設的對象的名字;而對象中為了響應訊息進行動作的函數,可以命名為On打頭,然後是相應的訊息的名稱;進行主動動作的函數,可以命名為Do打頭,然後是相應的動作名稱。類似的規則還有很多,需要程式員多讀優秀的程式,逐漸積累經驗,才能作出好的函數命名。

. 函數注釋

系統自動產生的函數,如滑鼠動作響應函數等,不必太多的注釋和解釋;

對於自行編寫的函數,若是系統關鍵函數,則必須在函數實現部分的上方標明該函數的資訊,格式如下:

/**
* 函數名:
* 編寫者:
* 參考資料:
* 功  能:
* 輸入參數:
* 輸出參數:
* 備  註:
*/

希望盡量遵循以上格式。

五、符號風格

. 總體要求

對於各種符號的定義,都有一個共通點,就是應該使用有實際意義的英文單詞或英文單詞的縮寫,不要使用簡單但沒有意義的字串,儘可能不使用阿拉伯數字,更切忌使用中文拼音的首字母。如這樣的名稱是不提倡的:Value1,Value2,Value3,Value4 …。

例如:
file(檔案),code(編號),data(資料),pagepoint(頁面指標), faxcode(傳真號) ,address(地址),bank(開戶銀行),……

. 變數名稱

變數命名由(首碼+修飾語)構成。現在比較流行的是一套由微軟的一個匈牙利軟體工程師首先使用,並且在微軟推廣開來,現在被稱之為匈牙利命名法的命名規則。匈牙利命名法規定,使用表示標識符所對應的變數類型的英文小寫縮寫作為標識符的首碼,後面在使用表示變數意義的英文單詞或縮寫進行命名。下面是匈牙利命名法中的一些命名方式:

(1)生存期修飾:用l(local)表示局域變數,p(public)表示全域變數,s(send)表示參數變數

(2)類型修飾:用s(AnsiString)表示字串,c(char)表示字元,n(number)數值,i(intger)表示整數,d(double)表示雙精確度,f(float)浮點型,b(bool)布爾型,d(date)表示日期型.

例如:
li_length表示整形的局域變數,是用來標識長度的.ls_code表示字元型的局域變數,用來標識代碼.

. 控制項名稱

控制項命名由(首碼+修飾語)構成。首碼即為控制項的名稱。

按鈕變數  Button+Xxxxxxx    例如:ButtonSave,ButtonExit,ButtonPrint等
題標變數  Label+Xxxxxxxx    例如:LabelName,LabelSex等
資料表變數 Table+Xxxxxx      例如:TableFile,TableCount等
查詢變數  Query+Xxxxxx      例如:QueryFile,QueryCeneter等
資料來源變數 DataSource+Xxx      例如:DataSourceFile,DataSourceCenter等
。。。。。。。。。。。。。。。。
(註:對於與表有關的控制項“修飾語”部分最好直接用表名。)

. Package 的命名

Package 的名字應該都是由一個小寫單片語成。

. Class 的命名

Class 的名字必須由一個或數個能表達該類的意思的大寫字母開頭而其它字母都小寫單詞或縮寫組成,這樣能使這個 Class 的名稱能更容易被理解。

. Class 變數的命名

變數的名字必須用一個小寫字母開頭。後面的單詞用大寫字母開頭。對於類的成員變數,在對其標識符命名時,要加上代表member(成員)的首碼m_。例如一個標識符為m_dwFlag,則它表示的變數是一個類型為雙字的成員變數,它是代表一個標誌。

. Static Final 變數的命名

Static Final 變數的名字應該都大寫,並且指出完整含義。

. 參數的命名

參數的名字必須和變數的命名規範一致。

. 數組的命名

數組應該總是用下面的方式來命名:
byte[] buffer; 
 
而不是:
byte buffer[];

. 方法的參數
 
使用有意義的參數命名,如果可能的話,使用和要賦值的欄位一樣的名字:

SetCounter(int size)
{
 this.size = size;
}

. 神秘的數

首先要說什麼是神秘的數。我們在程式裡經常會用到一些量,它是有特定的含義的。例如,現在我們寫一個薪金統計程式,公司員工有50人,我們在程式裡就會用50這個數去進行各種各樣的運算。在這裡,50就是&quot;神秘的數&quot;。為什麼稱它為神秘呢?因為別的程式員在程式裡看到50這個數,不知道它的含義,只能靠猜了。

在程式裡出現&quot;神秘的數&quot;會降低程式的可讀性,應該盡量避免。避免的方法是把神秘的數定義為一個常量。注意這個常量的命名應該能表達該數的意義,並且應該全部大寫,以與對應於變數的標識符區別開來。例如上面50這個數,我們可以定義為一個名為NUMOFEMPLOYEES的常量來代替。這樣,別的程式員在讀程式的時候就可以容易理解了。

六、程式編寫風格

. exit()

exit 除了在 main 中可以被調用外,其他的地方不應該調用。因為這樣做不給任何代碼代碼機會來截獲退出。一個類似後台服務地程式不應該因為某一個庫模組決定了要退出就退出。

. 異常

申明的錯誤應該拋出一個RuntimeException或者派生的異常。
頂層的main()函數應該截獲所有的異常,並且列印(或者記錄在日誌中)在螢幕上。

. 垃圾收集

JAVA使用成熟的後台垃圾收集技術來代替引用計數。但是這樣會導致一個問題:你必須在使用完對象的執行個體以後進行清場工作。比如一個prel的程式員可能這麼寫:

 ...
 {
  FileOutputStream fos = new FileOutputStream(projectFile);
  project.save(fos, &quot;IDE Project File&quot;);
 }
 ...
 
除非輸出資料流一出範圍就關閉,非引用計數的程式語言,比如JAVA,是不能自動完成變數的清場工作的。必須象下面一樣寫:

 FileOutputStream fos = new FileOutputStream(projectFile);
 project.save(fos, &quot;IDE Project File&quot;);
 fos.close();

. Clone

下面是一種有用的方法:
implements Cloneable

public
Object clone()
{
 try
 {
  ThisClass obj = (ThisClass)super.clone();
  obj.field1 = (int[])field1.clone();
  obj.field2 = field2;
  return obj;
 }
 catch(CloneNotSupportedException e)
 {
  throw new InternalError(&quot;Unexpected CloneNotSUpportedException: &quot; + e.getMessage());
 }
}

. final 類

絕對不要因為效能的原因將類定義為 final 的(除非程式的架構要求)
如果一個類還沒有準備好被繼承,最好在類文檔中註明,而不要將她定義為 final 的。這是因為沒有人可以保證會不會由於什麼原因需要繼承她。
 
. 訪問類的成員變數
 
大部分的類成員變數應該定義為 protected 的來防止繼承類使用他們。
注意,要用&quot;int[] packets&quot;,而不是&quot;int packets[]&quot;,後一種永遠也不要用。

public void setPackets(int[] packets)
{
 this.packets = packets;
}
CounterSet(int size)
{
 this.size = size;
}

. byte 數群組轉換到 characters

為了將 byte 數群組轉換到 characters,你可以這麼做:

&quot;Hello world!&quot;.getBytes();

. Utility 類

Utility 類(僅僅提供方法的類)應該被申明為抽象的來防止被繼承或被初始化。

. 初始化
 
下面的代碼是一種很好的初始化數組的方法:

objectArguments = new Object[]
{
 arguments
};

. 枚舉類型
 
JAVA 對枚舉的支援不好,但是下面的代碼是一種很有用的模板:

class Colour
{
   public static final Colour BLACK = new Colour(0, 0, 0);
   public static final Colour RED = new Colour(0xFF, 0, 0);
   public static final Colour GREEN = new Colour(0, 0xFF, 0);
   public static final Colour BLUE = new Colour(0, 0, 0xFF);
   public static final Colour WHITE = new Colour(0xFF, 0xFF, 0xFF);
}
 
這種技術實現了RED, GREEN, BLUE 等可以象其他語言的枚舉類型一樣使用的常量。 他們可以用 '==' 操作符來比較。
但是這樣使用有一個缺陷:如果一個使用者用這樣的方法來建立顏色 BLACK

new Colour(0,0,0)

那麼這就是另外一個對象,'=='操作符就會產生錯誤。她的 equal() 方法仍然有效。由於這個原因,這個技術的缺陷最好註明在文檔中,或者只在自己的包中使用。

. 混合使用 AWT 和 Swing 組件

如果要將 AWT 組件和 Swing 組件混合起來使用的話,請小心使用。實際上,盡量不要將他們混合起來使用。

. 滾動的 AWT 組件

AWT 組件絕對不要用 JscrollPane 類來實現滾動。滾動 AWT 組件的時候一定要用 AWT ScrollPane 組件來實現。

. 避免在 InternalFrame 組件中使用 AWT 組件
 
盡量不要這麼做,要不然會出現不可預料的後果。

. Z-Order 問題

AWT 組件總是顯示在 Swing 組件之上。當使用包含 AWT 組件的 POP-UP 菜單的時候要小心,盡量不要這樣使用。

八、效能

 在寫代碼的時候,從頭至尾都應該考慮效能問題。這不是說時間都應該浪費在最佳化代碼上,而是我們時刻應該提醒自己要注意代碼的效率。比如:如果沒有時間來實現一個高效的演算法,那麼我們應該在文檔中記錄下來,以便在以後有空的時候再來實現她。

 不是所有的人都同意在寫代碼的時候應該最佳化效能這個觀點的,他們認為效能最佳化的問題應該在項目的後期再去考慮,也就是在程式的輪廓已經實現了以後。

. 不必要的物件建構

不要在迴圈中構造和釋放對象

. 使用 StringBuffer 對象

在處理 String 的時候要盡量使用 StringBuffer 類,StringBuffer 類是構成 String 類的基礎。String 類將 StringBuffer 類封裝了起來,(以花費更多時間為代價)為開發人員提供了一個安全的介面。當我們在構造字串的時候,我們應該用 StringBuffer 來實現大部分的工作,當工作完成後將 StringBuffer 對象再轉換為需要的 String 對象。比如:如果有一個字串必須不斷地在其後添加許多字元來完成構造,那麼我們應該使用 StringBuffer 對象和她的 append() 方法。如果我們用 String 對象代替 StringBuffer 對象的話,會花費許多不必要的建立和釋放對象的 CPU 時間。

. 避免太多的使用 synchronized 關鍵字

避免不必要的使用關鍵字 synchronized,應該在必要的時候再使用她,這是一個避免死結的好方法。

                                                                                  rivershan 原創

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.