編碼前全面考慮所有可能的輸入,確保寫出的代碼在完成了基本功能之外,還考慮了邊界條件,並做好了錯誤處理。只要全面考慮到這三方面的代碼纔是完整的代碼。
要重視代碼的魯棒性,確保自己寫出的程序不會輕易崩潰。平時在寫代碼時,養成防禦式編程的習慣,在函數入口判斷輸入是否有效並對各種輸入做好相應的處理。
目的:提高代碼可讀性、維護性,降低bug,提高程序性能,最終保證代碼質量。
建議+案例+原因;
checklist
閱讀代碼:瞭解其運行機制、內部結構;學習其技巧。
閱讀代碼可作爲學習程序設計的方法。
花時間經常閱讀高質量的代碼——>提高編寫代碼的能力!“爲什麼要這樣寫?”
提高自身的開發和設計能力——>廣泛閱讀現有的構架,瞭解它們是如何組織的。
編寫的代碼總是存在改進的空間。
編碼時就要考慮使之易於閱讀。
分析設計良好的軟件系統的內部系統可以學到新的構架模式、數據結構、編碼方法、算法、風格和文檔規範、應用程序編程接口、甚至新的計算機語言。
從小型的程序開始閱讀,通過運行程序來得到反饋。主動修改代碼來檢驗對代碼的理解是否正確。要從小的改動做起,逐漸增大它們的範圍。
考慮如何改進它?——更好的設計、算法或功能。爲代碼編寫文檔。
自身價值的體現——不是掌握了多少知識,而是體現在我們創造的系統上!
物理模型
與代碼相關的概念:
編程構造(基本編程元素)、數據類型、數據結構、控制流程、項目組織、代碼規範、文檔和構架。
在軟件系統的工作投入中,40%~70%是用在系統首次編寫完成之後。
1、原則
a、OCP開放-關閉原則:指導封裝;
b、SRP單一職責原則:粒度控制;
c、DIP依賴倒置原則:
d、LSP里氏替換原則:指導多態;
e、ISP接口隔離原則:粒度控制;
2、避免錯誤
(1)合理使用註釋
同步修改註釋,和代碼保持一致;
註釋加在接口上;
(2)避免空指針:調用對象的方法,一定要明確該對象是否會空,不能確定時要判斷!開發者常常自以爲該對象不爲null,而沒有去看代碼的實現或沒有思考周全,漏掉了一些分支情況。
對於允許其值爲null的變量,在對其操作前,需要預先判斷其是否爲null。
(3)Serializable對象
所有Serializable對象必須設置serialVersionUID,除非特殊情況。
序列化是爲了保持版本的兼容性,即在版本升級時反序列化仍保持對象的唯一性。
(4)在finally中釋放資源——數據庫連接、打開的文件等。
3、維護性
(1)重複代碼:
a、保持類和方法職責單一。(粒度劃分)
b、儘量利用框架提供的功能。
c、採用合理的設計模式。
(2)異常處理:打印合理的異常。
a、只捕獲對自己有意義的特定異常,一般不要捕獲Exception異常;
b、拋出對自己無意義的異常,並保留原始的異常對象(嵌套異常),並描述異常的原因;
如:
try{
createMember();
} catch(SQLException e) {
throw new MemberCreationException("Failed to create member", e);
}
c、所有異常要麼捕獲、要麼拋出、要麼記錄。
(3)常量定義(什麼時候定義常量?在哪裏定義?如何定義?
a、多次引用的字符串和數字需要定義常量;
b、根據引用的範圍(同一個類、不同類):
類中;
單獨的常量類或接口;
c、常量定義處需要詳細的註釋;
(4)在配置文件中指定字符集編碼,由框架來轉換字符串。避免在代碼中假設用戶和系統的字符集編碼。
如:
下列代碼“假設”了用戶的字符集編碼:
// 如果源代碼是以GBK編譯的,那麼該URL也是GBK編碼的,然而用戶可能使用UTF-8編碼
response.sendRedirect(http://localhost/mypage.htm?p=中文);
//總是以GBK輸出頁面
response.setContentType("text/html; charset=GBK");
下列代碼的結果依賴於操作系統的字符集編碼:
Stringurl =URLEncoder.encode(str); // 將str轉換成URL兼容的格式byte[]
bytes =str.getBytes(); // 將string轉成bytes
Stringstr = new String(bytes); //將bytes轉成string
(5)避免硬編碼URL、文件路徑:因爲URL、文件路徑等資源的不確定性,應避免在程序中硬編碼這些內容。
4、性能
(1)拼裝字符串:用StringBuffer,而不用"+"或"+="來拼裝大量字符串。可以使用StringBuilder來代替StringBuffer。前者是後者的非同步版本,性能更優。
如:StringBuffer sb = new StringBuffer();
for(int i=0; i<100; i++) {
sb.append("aa").append(" ");
}
String str = sb.toString();
(2)預編譯正則表達式
利用java.util.regex.*:
Pattern pattern = Pattern.compile("^\\d+$");
Matcher matcher = pattern.matcher(str);
if(matcher.matches()) {
}
而不是:
if(str.matches("^\\d+$")) {
}
(3)日誌輸出:要明白什麼時候需要打印日誌,採用什麼級別打印日誌。
不要以System.out和System.err來輸出信息,而應該採用框架提供的Logging API。
在生產環境中必須關閉DEBUG日誌。
正式使用日誌時,儘量不要使用warn級別,避免日誌文件臃腫。
(4)批量操作SQL
批量插入、更新操作:
PreparedStatement ps = conn.preparedStatement("insert into test_table(...) values(?,?,...)");
ps.setString(1,"aaa")
ps.addBatch();
.....
ps.executeBatch();
(5)線程同步:避免長時間地鎖定線程。
4、開發技巧
(1)儘可能使用泛型而不是直接使用集合類:將運行時錯誤轉化爲編譯時錯誤。
(2)開發小工具
Findbugs:
Relo:幫助開發人員研究大型代碼庫的好工具,它能一步步的跟蹤你所展開的代碼包,並快速生成類似UML的類圖。
(3)閱讀源碼:JDK、Spring等。
5、TIPS
(1)不要往cookie中設置過多過大的數據。
(2)任何數據放入cache中都要考覈這些數據是否符合三個指標(更新頻率、訪問量、命中率)。要把更新不頻繁、訪問量高、命中率高的數據放入cache中。同時慎用OSCache的文件持久化。
(3)在使用SQL語句時一定要清楚它的執行頻率。
(4)一條正確的SQL語句隨着時間的推移,數據量越來越大,也可能不合適了。在使用SQL語句時一定要弄清楚它的數據量的增長。
(5)如何區分好代碼與壞代碼
(6)閱讀代碼的方法
6、PS
(1)《effective java》、《重構》