1命名規范 1.1 package (*) 包名全部由小寫的ASCII字母組成,用“.”分隔。 在此項目中,所有的包均以“com.prosten.ticket”開頭。 1.2 class (*) 類名應當是名詞,每個內部單詞的頭一個字母大寫。應當使你的類名簡單和具有說明性。用完整的英語單詞或約定俗成的簡寫命名類名。 【示例】public class UserManager 1.3 interface(*) 接口名應當是名詞,每個內部單詞的頭一個字母大寫。應當使你的接口名簡單和具有說明性。用完整的英語單詞或約定俗成的簡寫命名接口名。 【示例】interface TicketManagement 1.4 Class 成員屬性及變量的命名 (*) 變量名全部由字母組成,頭一個字母小寫,以后每個內部單詞的頭一個字母大寫。 變量名應該短而有意義。變量名的選擇應該易于記憶。 一個字符的變量名應避免,除非用于臨時變量。通常臨時變量名的命名規則為:i,j,k,m,n用于整數;c,d,e用于字符。 【示例】private String lastName; 1.5 常量的命名(*) Java 里的常量,是用static final 修飾的,應該用全大寫加下劃線命名,并且盡量指出完整含義。 【示例】static final String SMTH_BBS="bbs.tsinghua.edu.cn"; 1.6 數組的命名(*) 數組應該總是用下面的形式來命名: byte[] buffer; 1.7 方法的參數(*) 和變量的命名規范一致,且應使用有意義的參數命名,如果可能的話,使用和要賦值的字段一樣的名字。 【示例】setCounter(int size){ this.size = size; } 1.8 方法命名(*) 方法的命名應當使用動詞,頭一個字母小寫,以后每個內部單詞的頭一個字母大寫。 在方法名的選擇上應意義明確便于記憶。 對于屬性的存取方法,應使用getXXX()和setXXX()名稱,以isXXX(),hasXXX()來命名返回值為boolean 類型的方法。 1.9 一般命名注意事項 用有意義的名字命名變量 首先,用完整的英語單詞或約定俗成的簡寫命名變量。 【示例】firstName zipCode 用復數命名Collection 類變量。Collection 包括數組,Vector 等。命名時使用復數: 【示例】customers classmates 2 Java 源文件樣式 Java(*.java) 源文件應遵守如下的樣式規則: 2.1 Class 代碼布局: 版權聲明 Package和Import語句 Javadoc 注釋或者其它文件頭注釋 類或接口聲明 Fields 聲明 空行 構造函數 空行 克隆方法 空行 其它方法(不包括main) 空行 內部(Inner)類 空行 main()方法 2.2 版權聲明 所有的源文件都應該以一個c風格的注釋開始,以列出類名,版本信息,修改日期和版權聲明。 【示例】/* * Class name : * * Version information : * * Date : * * Copyright 2003 Prosten Technology Co.,Ltd. * */ 其他不需要出現在 javadoc 的信息也可以包含在這里。 2.3 Package/Imports(*) package 行要在 import 行之前,中間空一行。 將import 的classes 歸類,按順序羅列: a. Java 標準類(java.*) b. Java 擴充類(javax.*) c. 第三方類 d. 你的應用程序的類 注意在第三方類里進行注釋,說明它們的來源。如果 import 行中包含了同一個包中的多個類,則可以用 * 來處理。 【示例】package com.prosten.ticket.ticketmanagement; import java.io.*; import java.util.Observable; import java.util.Date; import javax.sql.*; //Apache Xerces import org.apache.xml.*; import org.apache.xerces.dom.*; //Application classes import com.prosten.util.*; 這里 java.io.* 使用來代替InputStream 和 OutputStream 的引入。 2.4 Javadoc 注釋 【示例】/** *Title: 類名
*Description:(說明用中文)/
* @author: * @date:(最后一次修改的提交時間) */ 2.5 Class Fields 類的成員變量: 【示例】protected int[] packets; public 的成員變量必須以生成文檔(JavaDoc) 的方式進行注釋(/** … */)。 proceted、private 和 package 定義的成員變量如果名字含義明確的話,可以沒有注釋。 Field 定義可遵從以下順序: a. public 常量 b. public 變量 c. protected 常量 d. protected 變量 e. package 常量 f. package 變量 g. private 常量 h. private 變量 2.6 存取方法(getter,setter) 接下來是類變量的存取的方法。 2.7 構造方法(*) 重載的構造方法應該用遞增的方式寫(參數多的寫在后面)。 【示例】public CounterSet(){ this(10); } public CounterSet(int size){ this.size = size; } 2.8 克隆方法 如果這個類是可以被克隆的,就應實現 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()); } } 2.9 類方法 下面開始寫類方法: 【示例】/** * 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 { if (r1.length != r2.length || r1.length != r3.length || r1.length != r4.length) { throw new IllegalArgumentException("Arrays must be he same size"); } System.arraycopy(r1, 0, r3, 0, r1.length); System.arraycopy(r2, 0, r4, 0, r1.length); } 2.10 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; } 2.11 main 方法(*) 如果類中包含main(String[]) 方法, 那么它應該寫在類的底部。 3 代碼編寫風格 3.1 語句 3.1.1 簡單語句 每一行包含至多一條語句。 3.1.2 復合語句 復合語句是指附加形如"{……}"封套結構的語句。 ? 封套內的語句要比復合語句多縮進一個層次。 ? 開頭的括號因該在起始復合語句同一行的末尾;結尾的括號應該新起一行并和起始的復合語句保持同樣縮進。 ? 應當對所有諸如if-else,while,for,try-catch結構的控制語句都使用大括號,即使是單個語句,只要它是控制結構的一部分。 【示例】if (condition) { statements; } else { statements; } //if-else 語句 3.1.3返回語句 有值返回的返回語句不應該使用括號,除非某些情況下為了使得返回值更加明顯。 【示例】return; return myDisk.size(); return (size ? size : defaultSize); 3.2 位置控制 3.2.1 縮進 應當用四個空格作為縮排的單位。不要在源文件中保存Tab 字符(!!)。以免在使用不同的源代碼管理工具時Tab 字符將因為用戶設置的不同而顯示為不同的寬度。 3.2.2 行的長度 避免行長超過80個字符,因為這樣不好被大多數終端顯示和工具處理。 3.2.3 折疊的行 當表達在一行放不下時,根據下面的一般原則打斷它: ? 在一個逗號后打斷。 ? 在運算符前打斷。 ? 高層次的打斷優于低層次的打斷。 ? 讓新起的行與上一行同一層次表達的開頭對齊。 ? 如果上面的方法導致代碼混亂或者代碼減少了合適的頁邊空白,那么使用縮進8個空格代替。 【示例】下面的例子打斷了方法調用: someMethod(longExpression1, longExpression2, longExpression3, longExpression4, longExpression5); var = someMethod1(longExpression1, someMethod2(longExpression2,longExpression3)); 【示例】 下面兩個例子打斷了算術表達式。第一個較好,因為打斷發生在較高層次上。 longName1 = longName2 * (longName3 + longName4 - longName5) + 4 * longname6; 【示例】 語句的行折疊通常用8空格,這是因為4空格會使主體部分看起來困難。 //USE THIS INDENTATION INSTEAD if ((condition1 && condition2) || (condition3 && condition4) ||!(condition5 && condition6)) { doSomethingAboutIt(); } 3.3 空白處理 3.3.1 空行 空行通過分開局部相關的代碼部分,增加了可讀性。 在下列情況下總是使用兩個空行: ? 源文件的不同部分之間 ? 和接口定義之間 在下列情況下總是使用一個空行: ? 在方法之間 ? 方法里面的局部變量聲明和它的第一個語句之間 ? 在塊注釋(參見4.1.1節)或單行注釋(參見4.1.2節)之前 ? 方法里面的邏輯部分之間,以提高可讀性 3.3.2 空格 應該在下列情況下使用空格: ? 關鍵字和后面的括號之間應該使用一個空格。例如: while (true) { ... } 注意在方法名和后面的括號之間不應該使用空格。這有助于分清關鍵字和方法調用。 ? 逗號之后應該使用一個空格。 ? 除了“.”之外的所有二元操作符應該用空格和操作數分開。對于一元操作符不使用空格。 【示例】 a = (a + b) / (c * d); while (d++ = s++) { n++; } printSize("size is " + foo + "\n"); ? for語句中的表達式應當用空格分開。 【示例】 for (expr1; expr2; expr3) ? 強制類型轉換應當跟隨一個空格。 【示例】 myMethod((byte) aNum, (Object) x); myMethod((int) (cp + 5), ((int) (i + 3)) + 1); 3.4 聲明 3.4.1每行一個 每行只能書寫一個聲明,因為這有利于注釋。 【示例】int level; // indentation level int size; // size of table 【示例】上面的例子中使用一個空格分隔類型和標示符。另一種方法是使用tab,如: int level; // indentation level int size; // size of table Object currentEntry; // currently selected table entry 3.4.2 初始化 試著在局部變量聲明的時候進行初始化。如果不在變量聲明的時候進行初始化,唯一的理由就是它的初始值首先依賴于一些計算。 3.4.3 位置 聲明只放在代碼塊的開始部分。(一個代碼塊是指由括號"{"和"}"包圍的代碼。)不要等到變量使用時才聲明它們。唯一的例外是在for循環語句中。 【示例】 void myMethod() { int int1 = 0; // beginning of method block if (condition) { int int2 = 0; // beginning of "if" block ... } } for (int i = 0; i < maxLoops; i++) { ... } 要避免局部變量的聲明出現在比其更高的層次中。不要聲明和內部代碼塊同名的變量。 4 程序編寫規范 4.1 使用方法來訪問實例變量和類變量(*) 如果沒有很好的理由,一般不應將實例變量或類變量設為 public,將變量設為public的典型應用是此類代表一個“數據結構”,而不包含任何方法。 4.2 引用類變量和類方法(*) 避免使用對象引用來訪問類(static) 變量或類方法,而應使用類名來訪問。如: classMethod(); //OK AClass.classMethod(); //OK anObject.classMethod(); //避免! 4.3 常量(*) 數字常量不應直接在編碼中出現,除非是for 循環中用于計數的 –1,0,或1。 字符串常量盡量不直接在編碼中出現。 4.4 ?前的邏輯運算表達式 ?前的邏輯運算表達式應以括號括起,如: (x >= 0) ? x : -x; 4.5 變量賦值 避免在一個語句中賦給幾個變量同樣的值。這是難于閱讀的。 【示例】 fooBar.fChar = barFoo.lchar = 'c'; // AVOID! 不要在容易和相等操作符混淆的地方使用賦值操作。 if (c++ = d++) { // AVOID! (Java disallows) ... } 而應該寫成這樣: if ((c++ = d++) != 0) { ... } 不要為了提高運行性能使用內嵌的賦值,這是編譯器的任務。例如: d = (a = b + c) + r; // AVOID! 而應該寫成: a = b + c; d = a + r; 4.6 特殊注釋 在實現某個類的方法時若還未完成實際代碼,但出于程序連接需要,可令該方法暫時返回一個“true”一類的結果,具體實現代碼留待下一步實現。 但是此部分需要用/* ××× WAITING TO IMPLEMENT… ××× */標注。 4.7 例外 申明的錯誤應該拋出一個RuntimeException 或者派生的例外。 頂層的main()函數應該截獲所有的例外,并且打印(或者記錄在日志中)在屏幕上。 4.8 方法的輸入參數 方法的輸入參數為對象時,默認的前置條件為輸入實例不為空(null),除非在API 文檔中另行說明。即默認情況下,方法體中不對傳入實例是否為空特別地加以判斷。 4.9 方法的返回值 當方法的返回值為對象時,返回值是否可能為空應當在API 文檔中說明。若返回值可能為空,相應的條件也應同時在API 文檔中說明。 5 Struts編碼規范 5.1 Action和ActionForm的class命名 所有的Action和ActionForm類均以有意義的英文單詞加Action或Form后綴。 【示例】 public class LoginAction extends Action public class LoginForm extends ActionForm 5.2 ActionForm變量命名 必須保證ActionForm和對應的數據對象中成員變量命名(包括大小寫)的一致。 5.3 Action內部結構 Action類一般用于處理用戶的請求,如果不保持一定結構將會臃腫到難以維護;Action內的execute方法應只做請求再轉發的功能。 【示例】 public class ShowingPlanAction extends Action { public ActionForward execute(ActionMapping actionMapping, ActionForm actionForm, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { String Action = httpServletRequest.getParameter("action"); if(action.equalsIgnoreCase("saveCreate")) { return saveCreate(actionMapping, actionForm, httpServletRequest,httpServletResponse) } ……… } private ActionForward saveCreate(ActionMapping actionMapping, ActionForm actionForm, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { // 將ActionForm中的數據復制至數據對象 // … … // 調用DAO相應方法處理數據對象 // … … // 處理成功后轉到指定頁面。 return actionMapping.findForward("OperationSuccess”); } } 5.4 在ActionForm和數據對象之間復制數據 在ActionForm和數據對象之間復制數據,使用Jakarta的第三方包來復制數據,這樣在將來的系統環境和中文亂碼等發生改變時統一處理。 【示例】 import org.apache.commons.beanutils.PropertyUtils; … … // 將數據從數據對象復制至ActionForm PropertyUtils.copyProperties(showingPlanModel,showingPlanForm); // 將數據從ActionForm復制至數據對象 PropertyUtils.copyProperties(showingPlanForm,showingPlanModel); 5.5 Struts標記庫的使用 在JSP中能夠使用Struts標記實現盡量不要書寫java代碼,以利于jsp代碼的閱讀與維護。Struts標記的示例可參看StrutsDemo.rar。 6 注釋 6.1 注釋格式 6.1.1 javadoc風格的注釋 用/** …… */標注的注釋是java特有的注釋,能夠用javadoc工具提取生成類的說明文檔。 用來注釋每個類,接口或成員方法。這些注釋應該剛好出現在聲明之前。并且盡可能使用文檔標簽來準確進行注釋。 【示例】/** * The Example class provides ... */ public class Example { ... 更多的細節,參見"How to Write Doc Comments for Javadoc",包括使用文檔注釋標簽(@return,@param,@see):http://java.sun.com/products/jdk/javadoc/writingdoccomments.html 有關文檔注釋和javadoc,還可以參見javadoc主頁:http://java.sun.com/products/jdk/javadoc/ 6.1.2 程序內部說明性注釋 對于不需要用javadoc提煉,只是用來說明程序算法邏輯的注釋可采取如下兩種方式: ? 采用/* …… */ 作為注釋標注 可單起一行,也可跟在某行比較短的代碼后面 【示例】if (condition) { /* Handle the condition. */ ... } if (a == 2) { return TRUE; /* special case */ } else { return isPrime(a); /* works only for odd a */ } ? 采用 // 作為注釋標注 不能用于連續的多行文本注釋。 當需要將一節代碼注釋掉的時候,推薦使用//注釋符,不要使用/*……*/注釋方式。 【示例】if (foo > 1) // Do a double-flip. ... } else { return false; // Explain why here. } //if (bar > 1) { // ... //} //else { // return false; //} 6.2注釋內容 6.2.1 類或接口的注釋 在類的開頭說明類的名稱;類的描述,包括類的類別、作用、是否關聯具體的數據實體或文件實體;類的最后修改時間;使用文檔注釋標簽說明類的作者。 【示例】 /** *Title: ConfigReader
*Description: 從頁面配置文件page.properties取得配置信息的類
*Date: 2003-11-19
* @author unascribed */ 6.2.2 類方法的注釋 在方法的開頭說明方法的業務邏輯、算法、在必要的時候說明輸入輸出參數以及拋出異常。 【示例】 /** * 從showing表里查詢指定時間范圍內演出場次的數量,并將查詢結果返回 * @param startDate 起始時間 * @param endDate 終止時間 * @return 演出場次數量 * @throws SqlException 當查詢錯誤時拋出異常 */ public int queryShowingCounts(String startDate, String endDate) throws SqlException{ ..... } 6.2.3 類變量的注釋 對于所有的屬性為public的類變量需要說明其含義,對于屬性為package、protect、private的類變量,如果變量名命名清晰易于理解的話可以不用說明。 【示例】 /** 操作員被訪問次數 */ public int UserVisitedCounts = 0; 6.2.4 類常量的注釋 對于所有的類常量需要說明其含義 【示例】 /** 進入系統的缺省用戶名 */ static final String DEFAULT_USER_NAME = “Prosten”; 7 編程實踐問題 7.1 exit() exit 除了在 main 中可以被調用外,其他的地方不應該調用。因為這樣做不給任何代碼機會來截獲退出。一個類似后臺服務的程序不應該因為某一個庫模塊決定了要退出就退出。 7.2 垃圾收集 JAVA 使用成熟的后臺垃圾收集技術來代替引用計數。但是這樣會導致一個問題:你必須在使用完對象的實例以后進行清場工作。比如一個perl 的程序員可能這么寫: ... { FileOutputStream fos = new FileOutputStream(projectFile); project.save(fos, "IDE Project File"); } ... 除非輸出流一出作用域就關閉,非引用計數的程序語言,比如JAVA, 是不能自動完成變量的清場工作的。必須象下面一樣寫: FileOutputStream fos = new FileOutputStream(projectFile); project.save(fos, "IDE Project File"); fos.close(); 7.3 final 類 絕對不要因為性能的原因將類定義為 final 的(除非程序的框架要求)。如果一個類還沒有準備好被繼承,最好在類文檔中注明,而不要將她定義為 final 的。這是因為沒有人可以保證會不會由于什么原因需要繼承它。 7.4 性能 在寫代碼的時候,從頭至尾都應該考慮性能問題。這不是說時間都應該浪費在優化代碼上,而是我們時刻應該提醒自己要注意代碼的效率。比如:如果沒有時間來實現一個高效的算法,那么我們應該在文檔中記錄下來,以便在以后有空的時候再來實現她。不是所有的人都同意在寫代碼的時候應該優化性能這個觀點,他們認為性能優化的問題應該在項目的后期再去考慮,也就是在程序的輪廓已經實現了以后。應注意: •不必要的對象構造 •不要在循環中構造和釋放對象 7.5 使用 StringBuffer 對象 在處理 String 的時候要盡量使用 StringBuffer 類,StringBuffer 類是構成 String 類的基礎。String 類將 StringBuffer 類封裝了起來,( 以花費更多時間為代價)為開發人員提供了一個安全的接口。當我們在構造字符串的時候,我們應該用 StringBuffer 來實現大部分的工作,當工作完成后將 StringBuffer 對象再轉換為需要的 String 對象。比如:如果有一個字符串必須不斷地在其后添加許多字符來完成構造,那么我們應該使用 StringBuffer 對象和她的 append() 方法。如果我們用 String 對象代替StringBuffer 對象的話,會花費許多不必要的創建和釋放對象的 CPU 時間。 7.6 換行 如果需要換行的話,盡量用 println 來代替在字符串中使用"\n"。 不要這樣: System.out.print("Hello,world!\n"); 要這樣: System.out.println("Hello,world!"); 或者你構造一個帶換行符的字符串,至少要象這樣: String newline = System.getProperty("line.separator"); System.out.println("Hello world" + newline); 8 附錄: /* * @(#)GetSystemConfig.java * * Copyright (c) 2002-2003 Prosten Technology Co.,Ltd. * Suite 702, Tower W3, Oriental Plaza, No.1 Chang An Ave, Dongcheng district Beijing * All rights reserved. * * 本軟件為保利票務系統2.0項目. */ package com.prosten.ticket.common; import java.util.*; import java.io.*; import com.sun.xml.tree.*; import org.w3c.dom.*; /** *Title: iBusiness
*Description: 從頁面配置文件取得配置信息的類
*Copyright: Copyright (c) 2003
*Company: Prosten Corp. Ltd.
* @author unascribed * @version 1.0 */ public class GetSystemConfig { //存放jsp頁面映射信息 public static HashMap mapJsp=new HashMap(); private final static String JSPURL="jspPage"; /** * 根據配置文件中的jspID返回對應的url串 */ public String getJspURL(String strJspID){ return (String)mapJsp.get(strJspID); } /** * 將配置文件解析到HashTable中 */ public static void ParseJsp(String strFile ){ try { FileInputStream is = new FileInputStream(strFile); Document doc = XmlDocument.createXmlDocument(is,false); int size = XmlUtils.getSize( doc , JSPURL ); for ( int i = 0; i < size; i++ ) { Element row = XmlUtils.getElement( doc , JSPURL, i ); mapJsp.put(row.getAttribute("ID"),row.getAttribute("pageurl")); } } catch ( Exception e ) { System.out.println( e ); } } }