# Coding Principles ###### tags: `eGroupAI 開發規範` ## 變數宣告 1. public : 如果你要給其他package共用(表明該數據成員、成員函數是對所有用戶開放,所有用戶都可以直接進行調用) 2. protected : 同一個 packge 的都可以(protected對於子女、朋友來說,就是public的,可以自由使用,沒有任何限制,而對於其他的外部class,protected就變成private。) 3. private : 私有的意思就是除了class自己之外,任何人都不可以直接使用,私有財產神聖不可侵犯,即便是子女,朋友,都不可以使用。 4. friendly | 作用域 | 當前類 | 同一package | 子孫類 | 其他package | | -------- | -------- | -------- | -------- |--------| | public  | √ | √ | √ | √ | | protected   | √ | √ | √ | × | | friendly   | √ | √ | × | × | | private   | √ | × | × | × | ## 字串處理 ### 宣告變數(搞清楚狀況) 什麼情況使用? String : 變數不會被改變的時候使用,定義final `特性 1.不變性 2.針對常數池最佳化 3.類別的final定義` StringBuilder : 變數會被改變的時候使用、累加(單執行續存取時使用) StringBuffer : 變數會被改變的時候使用、累加(多執行續存取時使用) ### 字串相加(StringBuilder or StringBuffer) 什麼叫相加? 總共產生幾個String物件? `String result = "Leonard" + "Janet" + "Jerry"; 產生5個物件 : "Leonard"、"Janet"、"Jerry"、"LeonardJanet"、"LeonardJanetJerry";` 別鬧了,如果要相加請不要用String宣告變數 - StringBuilder(int capacity) : 最快,單執行續存取時使用,如果知道長度一定要先設定 - StringBuffer(int capacity) : 最快,多執行續存取時使用,如果知道長度一定要先設定 - String (每次append會自動產生新的物件) 最慢 ```java StringBuilder content = new StringBuilder(); for (Object item : items) { content.append(item); } StringBuffer content = new StringBuffer(); for (Object item : items) { content.append(item); } ``` ### 字串擷取( new String(str.substring(int start, int end)) 記得String 的特性是不可變,每次的變動都會產生新的String記憶體 方法一 : `千萬不要這樣寫 String str = "Daniel"; str.substring(0, 2);` 這樣寫的問題在於 : 切割過後的記憶體大小會與原來相同還是 6 byte,會造成系統**memory leak** 方法二 : `改成這樣 new String(this.str.substring(0, 2));` 這樣會產生一個新的String記憶體空間,但大小會使2byte ### 字串分割和尋找 ### 字串開頭與結尾(charAt) String string = “java”; System.out.println(string.charAt(1)) 效能有多快,請自行上網google ## List ### **ArrayList** 1. 陣列 2. 記憶體連續空間,大小不足時會進行複製 3. add速度快 4. 隨機存取快 5. 刪除頁首、尾部非常慢,每次刪除都會進行記憶體重組 **使用方式** 1. arrayList(int capacity)宣告大小(若沒有宣告,預設是10,當記憶體不足時,自動擴充,擴充行為會降低效能) ### **LinkList** 1. 鏈結串列 2. 插入物件速度很快 3. 隨機存取慢 4. 刪除頁首、尾部非常快,但刪除中間物件非常慢 ## FOR迴圈 - ForEach ```java for(String s : list){ tmp = s; } ``` - Iterator(反覆運算器) ```java for(Iterator<String> it = list.iterator(); is.hasNext();){ tmp = it.next(); } ``` - for循環 ```java for(int i =0; list.size();i++){ tmp = list.get(i); } ``` | List類型 | ForEach操作 | 反覆運算器 | for循環 | | -------- | -------- | -------- |-------- | | ArrayList | 慢 | 中 |快 | | LinkList | 中 | 快 |慢 | ## Map ### **HashMap** 1. 純get效能最快 2. 無順序 ### **LinkedHashMap** 1. 純get效能最慢 2. 依照輸入順序排序排序 ### **TreeMap** 1. 純get效能第二快 2. 根據key順序排序,根據Key排序get最快(紅黑數原理) ## I/O 檔案寫出、寫入 1. Stream 2. ByteBuffer 3. MappedByteBuffer(快) 4. DirectBuffer(直接存在記憶體,超快,但消耗記憶體,不建議使用) ## try-catch ```java .... final LogUtil logUtil = new LogUtil(); try () { ... } catch (SQLException e) { // 1. 設定LogReport相關參數 final LogReport logReport = new LogReport(); // 宣告Log使用之物件 LogReport logReport.setMessage(e.getMessage()); // exception之訊息 logReport.setException(e); // exception之物件 logReport.setFunction(); // 造成catch之function logReport.setObject(); // 傳入之參數(物件 or String or Integer ...) logUtil.setLog(logReport, LogType.ERROR); // 寫入 Log** // 2. 通知管理員 (發生錯誤時) **final NotificationUtil notificationUtil = new NotificationUtil(); notificationUtil.sendEventMessage(logReport, new Email());** // 3. 拋出例外 (要寫以下程式才能在DB有誤時回傳SERVER 500,有益於debug) **throw new RuntimeException(e);** } ``` ### 注意事項 - 寫在for迴圈外面 ## 陣列 VS switch case 1. 陣列較快,優先使用 ## 日誌(Log) > 原則 - Log 為程式上線後追蹤系統問題的最主要手段 - 追蹤問題所需要的資訊都需要記錄在 log 中 > 主要內容 - 記錄點的狀態 - 錯誤發生時的根本原因 - 記錄點的參數與變數值 > 情境:錯誤發生時,記錄所需資訊 - 需要記錄的資訊包含了除錯時所需的資訊。可以反向思考,在錯誤發生時,需要何種資訊來重現可能的錯誤,並將此類資訊記錄下來 ```java public String getDBProperties(String key) { try { IParamService paramService = context.getBean(IParamService.class); return paramService.queryParamByCode("WORDING", key); } catch (Exception e) { // 記錄時包含了輸入的參數 // 與錯誤的 exception root casue LogHandler.error(ResourceUtil.class, String.format("取得說明 %s 發生錯誤", key), e); return null; } } ``` ### 框架介紹 > java中的常用日誌框架 - Commons-logging - Logging 框架的介面 - 由選擇第三方的日誌組件作為具體實現 - 通常都是配合著log4j來使用 - **SLF4j(Simple Logging Facade for Java)** - Logging 框架的介面 - 類似於Apache Common-Logging,對不同日誌框架提供的一個門面封裝 - SLF4J是編譯時綁定到具體的日誌框架,性能優於採用運行時搜尋的方式的commons-logging - 支持參數化的log字符串 - 更好的可讀性 - 不需要使用logger.isDebugEnabled()來解決日誌因為字符拼接產生的性能問題 ```java //傳統的字符串產生方式,如果没有要記錄Debug等级的訊息,就會浪费時間在產生不必要的訊息上 logger.debug("There are now " + count + " user accounts: " + userAccountList); // 为了避免上述問題,我們可以先檢查是不是開啟了Debug信息記錄功能,只是程序的編碼会比較複雜 if (logger.isDebugEnabled()) { logger.debug("There are now " + count + " user accounts: " + userAccountList); } // 如果Debug等级没有開啟,則不會產生不必要的字符,同时也能保持程序編碼的簡潔 logger.debug("There are now {} user accounts: {}", count, userAccountList); ``` - Log4j(Logging for Java) - Apache的一個開放源代碼項目 - 三大元件 - Logger-由編程人員在程式中使用,進行 logging 的元件 - 指派給 Logger 的等級有 : DEBUG, INFO, WARN, ERROR, FATAL 5 種 - FATAL > ERROR > WARN > INFO > DEBUG Logger 的等級決定它產生 log message 的數量 : Logger 只寫"出高於或等於本身等級"的 log message。例如某個 Logger 的等級被設定為 WARN,那麼它只會寫出等級為 WARN, ERROR, FATAL 的 log message,對於 DEBUG, INFO 的 log message 則不予理會。 - Appender- 負責將 log message 輸出到各種裝置上 - Layout- 決定 log message 的格式 - Logback - Logback是由log4j創始人設計的又一個開源日誌組件 - 分成三個模組 - logback-core:基礎模塊 - logback- classic:log4j的一個改良版本,完整實現SLF4J API,方便地更換成其它日誌系統如log4j或JDK14 Logging - logback-access:訪問模塊與Servlet容器集成提供通過Http來訪問日誌的功能 - 配置文件參考 - [Logback學習筆記](http://webinglin.github.io/2015/06/04/Logback-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/) - [日誌組件logback配置](http://www.cnblogs.com/yuanermen/archive/2012/02/13/2349609.html) ### 實作規範 # **LOG層級分類** - ERROR : 錯誤例外 - WARN : 查無資料(資料遺失) - INFO :使用者操作LOG訊息 # **後端Log寫法** ```java // 宣告 logReport entity final LogReport logReport = new LogReport(); // 定義訊息 logReport.setMessage("從S3下載錯誤"); // 定義function名稱 logReport.setFunction("save(test1,test2)"); // 定義所傳參數 (如果 function沒有代參數,則不用 setAttributes) logReport.setAttributes(test1,test2); // 將logReport轉成json,傳到AWS CloudWatch Logs處理 logger.error(new Gson().toJson(logReport), e); ``` ## **AWS CloudWatch Logs 呈現** ![https://laitaiyuan1.gitbooks.io/egroup/content/assets/image_preview.png](https://laitaiyuan1.gitbooks.io/egroup/content/assets/image_preview.png) - JSON格式範例 ```json { "timeStamp": "2018-02-06 14:18:29.598", "level": "ERROR", "threadName": "main", "loggerName": "com.faceRecognize.webapp.controller.TestController", "function": "test(account,password)", "attributes": [ { "key": "account", "value": "123" }, { "key": "password", "value": "456" } ] } ``` ## 註解 > 原則 - 避免多餘的註解,只寫有意義的註解 - 程序註解應該是告訴別人你的目的,而不是告訴別人程式的語法。 ### 測試區塊註解 > 使用時機 - 因各環境不同需要使用不同的程式碼時 - 需在程式碼中加入測試階段用的程式 > 範例 - 類型@type : (特殊處理 / 測試用) - 說明@description : - 作者@author : - 日期@date : ``` java /** * @type : 測試用 * @description : 因為信用卡測試環境有金額限制,所以需與正式環境區分 * @author : Lai * @date : 2016/12/1 */ ``` ### 維護修改註解 > 原則 - 在維護修改程式邏輯時,需在修改的同時,增加對應修改的原因、時間、作者等相關資訊。便於在後續回溯相關問題時,能快速了解原因。 --- > 範例 - 開始區段 - 結束區段 - 原因 - 更改內容 - 變更之邏輯內容 - 修改作者 - 修改日期 ```java /** * Begin * @description : 修改運算邏輯 * @author : Lai * @date : 2016/12/01 */ boolean flag=true; if (flag)) { // more code here } else { // more code here } // End ``` - Auto-generated Table of Content [ToC]