日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

常识之外的规范——阿里java开发手册笔记(全章节)

發布時間:2025/3/19 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 常识之外的规范——阿里java开发手册笔记(全章节) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

說明

這篇文章是我第一次(認真)閱讀《阿里巴巴 Java 開發手冊(終極版)》的筆記。手冊本身對規范的講解已經非常詳細了,如果你已經有一定的開發經驗并且有良好的編碼習慣和意識,會發現大部分規范是符合常識的。所以本文不會再去做重復的說明,只是對其中一些可能沒留意到的或者說不在(我的)常識之內的一些規范進行整理記錄。當然每家公司都有自己的一套規范標準,所以大家也沒必要過分追究。

其中或許會有遺漏或者理解錯誤,希望各位擔待提點。

  • 重點我會用黑體標注。
  • 引用部分為《阿里巴巴 Java 開發手冊(終極版)》原文
  • 更新時間:2017-10-17

  • 插件

    ide插件已發布:《阿里巴巴Java開發手冊》IDEA插件與Eclipse插件使用指南


    第一節 編程規約

    1 命名規范

    8.【強制】POJO 類中布爾類型的變量,都不要加 is,否則部分框架解析會引起序列化錯誤。

    反例:定義為基本數據類型 Boolean isDeleted;的屬性,它的方法也是 isDeleted(),RPC框架在反向解析的時候,“以為”對應的屬性名稱是 deleted,導致屬性獲取不到,進而拋出異常。

    16.【參考】各層命名規約:
    A) Service/DAO 層方法命名規約
    1) 獲取單個對象的方法用 get 做前綴。
    2) 獲取多個對象的方法用 list 做前綴。(我習慣寫成 getXxxList)
    3) 獲取統計值的方法用 count 做前綴。
    4) 插入的方法用 save/insert 做前綴。
    5) 刪除的方法用 remove/delete 做前綴。
    6) 修改的方法用 update 做前綴。

    2 常量定義

    1.【強制】不允許任何魔法值(即未經定義的常量)直接出現在代碼中。
    反例:
    String key = "Id#taobao_" + tradeId;
    cache.put(key, value);

    魔法值:是指在代碼中直接出現的數值,而只有在這個數值記述的那部分代碼中才能明確了解其含義。
    也就是我們常說的[硬編碼]或者[寫死],這類代碼需要定義常量來明確其含義。

    3 代碼格式

    5.【強制】采用 4 個空格縮進,禁止使用 tab 字符。
    說明:如果使用 tab 縮進,必須設置 1 個 tab 為 4 個空格。IDEA 設置 tab 為 4 個空格時,請勿勾選 Use tab character;而在 eclipse 中,必須勾選 insert spaces for tabs。

    有些同學可能會對這一條不以為然。如果是協調開發,兩個工程師的格式化規則不一致很可能A同學無意把B同學的代碼重新格式化并提交,導致后邊查看svn變更記錄時傻逼了。

    7.【強制】單行字符數限制不超過 120 個,超出需要換行,換行時遵循如下原則:
    1) 第二行相對第一行縮進 4 個空格,從第三行開始,不再繼續縮進,參考示例。
    2) 運算符與下文一起換行。
    3) 方法調用的點符號與下文一起換行。
    4) 方法調用時,多個參數,需要換行時,在逗號后進行。
    5) 在括號前不要換行,見反例。

    120這個長度限制很有意思,如圖:

    這個長度大概是15寸筆記本1080分辨率字體14號左右的最佳可視長度。當然應該也不一定非要這么精準吧。。

    4 OOP 規約

    7.【強制】所有的相同類型的包裝類對象之間值的比較,全部使用 equals 方法比較。
    說明:對于 Integer var = ? 在-128 至 127 范圍內的賦值,Integer 對象是在IntegerCache.cache 產生,會復用已有對象,這個區間內的 Integer 值可以直接使用==進行判斷,但是這個區間之外的所有數據,都會在堆上產生,并不會復用已有對象,這是一個大坑,推薦使用 equals 方法進行判斷。

    12.【強制】POJO 類必須寫 toString 方法。使用 IDE 的中工具:source> generate toString時,如果繼承了另一個 POJO 類,注意在前面加一下 super.toString。
    說明:在方法執行拋出異常時,可以直接調用 POJO 的 toString()方法打印其屬性值,便于排查問題。

    吐槽:"使用 IDE 的中工具" 碼字錯誤哦!

    13.【推薦】使用索引訪問用 String 的 split 方法得到的數組時,需做最后一個分隔符后有無內容的檢查,否則會有拋 IndexOutOfBoundsException 的風險。
    說明:
    String str = "a,b,c,,";
    String[] ary = str.split(",");
    // 預期大于 3,結果是 3
    System.out.println(ary.length);

    最好的做法是對集合類型的變量本身進行判空校驗或者大小判斷,不要想當然。

    5 集合處理

    2.【強制】ArrayList的subList結果不可強轉成ArrayList,否則會拋出ClassCastException異常,即java.util.RandomAccessSubList cannot be cast to java.util.ArrayList.
    說明:subList 返回的是 ArrayList 的內部類 SubList,并不是 ArrayList ,而是ArrayList 的一個視圖,對于 SubList 子列表的所有操作最終會反映到原列表上。

    5.【強制】使用工具類Arrays.asList()把數組轉換成集合時,不能使用其修改集合相關的方法,它的 add/remove/clear 方法會拋出 UnsupportedOperationException 異常。
    說明:asList 的返回對象是一個 Arrays 內部類,并沒有實現集合的修改方法。Arrays.asList體現的是適配器模式,只是轉換接口,后臺的數據仍是數組。
    String[] str = new String[] { "you", "wu" };
    List list = Arrays.asList(str);
    第一種情況:list.add("yangguanbao"); 運行時異常。
    第二種情況:str[0] = "gujin"; 那么 list.get(0)也會隨之修改。

    10.【推薦】使用 entrySet 遍歷 Map 類集合 KV,而不是 keySet 方式進行遍歷。
    說明:keySet 其實是遍歷了 2 次,一次是轉為 Iterator 對象,另一次是從 hashMap 中取出key 所對應的 value。而 entrySet 只是遍歷了一次就把 key 和 value 都放到了 entry 中,效率更高。如果是 JDK8,使用 Map.foreach 方法。
    正例:values()返回的是 V 值集合,是一個 list 集合對象;keySet()返回的是 K 值集合,是一個 Set 集合對象;entrySet()返回的是 K-V 值組合集合。

    java8 是個好東西~

    6 并發處理

    5.【強制】SimpleDateFormat 是線程不安全的類,一般不要定義為 static 變量,如果定義為static,必須加鎖,或者使用 DateUtils 工具類。
    正例:注意線程安全,使用 DateUtils。亦推薦如下處理:
    private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
    @Override
    protected DateFormat initialValue() {
    return new SimpleDateFormat("yyyy-MM-dd");
    }
    };
    說明:如果是 JDK8 的應用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar,DateTimeFormatter 代替 SimpleDateFormat,官方給出的解釋:simple beautiful strong immutable thread-safe。

    再說一遍,java8 是個好東西!LocalDateTime相關API
    附贈一個java.util.Date和LocalDateTime互轉的例子:

    private static Date localDateTimeToUDate(LocalDateTime localDateTime) {ZoneId zone = ZoneId.systemDefault();Instant instant = localDateTime.atZone(zone).toInstant();return Date.from(instant);}private static LocalDateTime uDateToLocalDate(Date date) {Instant instant = date.toInstant();ZoneId zone = ZoneId.systemDefault();LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);return localDateTime;}

    14.【參考】 HashMap 在容量不夠進行 resize 時由于高并發可能出現死鏈,導致 CPU 飆升,在開發過程中可以使用其它數據結構或加鎖來規避此風險。

    7 控制語句

    3.【推薦】表達異常的分支時,少用 if-else 方式,這種方式可以改寫成:
    if (condition) {
    ...
    return obj;
    }
    // 接著寫 else 的業務邏輯代碼;
    說明:如果非得使用 if()...else if()...else...方式表達邏輯,【強制】避免后續代碼維護困難,請勿超過 3 層。
    正例:超過 3 層的 if-else 的邏輯判斷代碼可以使用衛語句、策略模式、狀態模式等來實現...

    我們公司codeReview時經??吹接行┩瑢W的代碼是if(){}else if(){} else if(){}else{} 除了看上去low更主要的原因是過多的大括號層級不便于閱讀很容易搞混,尤其是跳出代碼塊的時候,連續幾個}}}基本就不知道跳到哪了徹底懵逼,還得折疊代碼或者滾上去重新回憶一下。

    6.【推薦】接口入參保護,這種場景常見的是用于做批量操作的接口。

    解釋一下,接口入參保護就是對入參進行校驗,包括允許的最大值或者其他范圍或邊界。防止請求大量數據導致接口“爆炸”。比如限制返回數據最大條數,超過限制直接return或者拋異常。

    8 注釋規約

    感覺沒啥好說的。。

    9 其它

    1.【強制】在使用正則表達式時,利用好其預編譯功能,可以有效加快正則匹配速度。
    說明:不要在方法體內定義:Pattern pattern = Pattern.compile(規則);

    就是說定義成全局變量。


    第二節 異常日志

    1 異常處理

    3.【強制】對大段代碼進行try-catch,這是不負責任的表現。catch時請分清穩定代碼和非穩定代碼,穩定代碼指的是無論如何不會出錯的代碼。對于非穩定代碼的catch盡可能進行區分異常類型,再做對應的異常處理。

    9.【推薦】方法的返回值可以為null,不強制返回空集合,或者空對象等,必須添加注釋充分說明什么情況下會返回null值。調用方需要進行null判斷防止NPE問題。說明:本手冊明確防止NPE是調用者的責任。即使被調用方法返回空集合或者空對象,對調用者來說,也并非高枕無憂,必須考慮到遠程調用失敗、序列化失敗、運行時異常等場景返回null的情況。

    需要說明的是是否可以返回null是需要根據接口約定來判斷的,如果明確的返回對象的結構類型,一定要返回這個對象,但他的屬性值可以是null,比如page對象:{data:null,pageNum:0,count:0}

    2 日志規約

    4.【強制】對trace/debug/info級別的日志輸出,必須使用條件輸出形式或者使用占位符的方式。
    說明:logger.debug("Processingtradewithid: " + id+ "andsymbol: " + symbol);如果日志級別是warn,上述日志不會打印,但是會執行字符串拼接操作,如果symbol是對象,會執行toString()方法,浪費了系統資源,執行了上述操作,最終日志卻沒有打印。
    正例:(條件)if (logger.isDebugEnabled()) { logger.debug("Processing trade with id: " + id + " and symbol: " + symbol); }
    正例:(占位符)logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);


    第三節 單元測試

    4.【強制】單元測試是可以重復執行的,不能受到外界環境的影響。
    說明:單元測試通常會被放到持續集成中,每次有代碼check in時單元測試都會被執行。如果單測對外部環境(網絡、服務、中間件等)有依賴,容易導致持續集成機制的不可用。
    正例:為了不受外界環境影響,要求設計代碼時就把SUT的依賴改成注入,在測試時用spring這樣的DI框架注入一個本地(內存)實現或者Mock實現。

    15.【參考】為了更方便地進行單元測試,業務代碼應避免以下情況:
    構造方法中做的事情過多。?存在過多的全局變量和靜態方法。
    存在過多的外部依賴。
    存在過多的條件語句。說明:多層條件語句建議使用衛語句、策略模式、狀態模式等方式重構。

    和第一節if-else提到的一樣,避免多層代碼塊嵌套

    16.【參考】不要對單元測試存在如下誤解:
    那是測試同學干的事情。本文是開發手冊,凡是本文內容都是與開發同學強相關的。
    單元測試代碼是多余的。汽車的整體功能與各單元部件的測試正常與否是強相關的。
    單元測試代碼不需要維護。一年半載后,那么單元測試幾乎處于廢棄狀態。
    單元測試與線上故障沒有辯證關系。好的單元測試能夠最大限度地規避線上故障。

    測試開發相親相愛是一家~


    第四節 安全規約

    4.【強制】用戶請求傳入的任何參數必須做有效性驗證
    說明:忽略參數校驗可能導致:
    pagesize過大導致內存溢出
    惡意orderby導致數據庫慢查詢
    任意重定向?SQL注入
    反序列化注入
    正則輸入源串拒絕服務ReDoS
    說明:Java代碼用正則來驗證客戶端的輸入,有些正則寫法驗證普通用戶輸入沒有問題,但是如果攻擊人員使用的是特殊構造的字符串來驗證,有可能導致死循環的結果。

    老生常談的問題,但在工作中有時會忽略。


    第五節 MySQL數據庫

    1. 建表規約

    8.【強制】varchar是可變長字符串,不預先分配存儲空間,長度不要超過5000,如果存儲長度大于此值,定義字段類型為text,獨立出來一張表,用主鍵來對應,避免影響其它字段索引效率。

    大字段建外連表,避免影響索引效率。難點在于如何說服主工程師(滑稽)。

    13.【推薦】字段允許適當冗余,以提高查詢性能,但必須考慮數據一致。
    冗余字段應遵循:
    1)不是頻繁修改的字段。
    2)不是varchar超長字段,更不能是text字段。
    正例:商品類目名稱使用頻率高,字段長度短,名稱基本一成不變,可在相關聯的表中冗余存儲類目名稱,避免關聯查詢。

    講一個笑話。公司老王出差去拉項目,對方博士生問“這個數據庫設計為什么不符合三范式?”
    真事。

    14.【推薦】單表行數超過500萬行或者單表容量超過2GB,才推薦進行分庫分表。
    說明:如果預計三年后的數據量根本達不到這個級別,請不要在創建表時就分庫分表。

    2. 索引規約

    3.【強制】在varchar字段上建立索引時,必須指定索引長度,沒必要對全字段建立索引,根據實際文本區分度決定索引長度即可。
    說明:索引的長度與區分度是一對矛盾體,一般對字符串類型數據,長度為20的索引,區分度會高達90%以上,可以使用count(distinctleft(列名, 索引長度))/count(*)的區分度來確定。

    5.【推薦】如果有orderby的場景,請注意利用索引的有序性。orderby最后的字段是組合索引的一部分,并且放在索引組合順序的最后,避免出現file_sort的情況,影響查詢性能。
    正例:wherea=? andb=? orderbyc;索引:a_b_c
    反例:索引中有范圍查找,那么索引有序性無法利用,如:WHEREa>10 ORDERBYb;索引a_b無法排序。

    6.【推薦】利用覆蓋索引來進行查詢操作,避免回表。
    說明:如果一本書需要知道第11章是什么標題,會翻開第11章對應的那一頁嗎?目錄瀏覽一下就好,這個目錄就是起到覆蓋索引的作用。
    正例:能夠建立索引的種類:主鍵索引、唯一索引、普通索引,而覆蓋索引是一種查詢的一種效果,用explain的結果,extra列會出現:usingindex。

    3. SQL語句

    1.【強制】不要使用count(列名)或count(常量)來替代count(*),count(*)是SQL92定義的標準統計行數的語法,跟數據庫無關,跟NULL和非NULL無關。
    說明:count(*)會統計值為NULL的行,而count(列名)不會統計此列為NULL值的行。

    乖乖滾回count(*)

    【推薦】in操作能避免則避免,若實在避免不了,需要仔細評估in后邊的集合元素數量,控制在1000個之內。

    4. ORM映射

    3.【強制】不要用resultClass當返回參數,即使所有類屬性名與數據庫字段一一對應,也需要定義;反過來,每一個表也必然有一個與之對應。
    說明:配置映射關系,使字段與DO類解耦,方便維護。

    編程一時爽,維護兩行淚~

    5.【強制】iBATIS自帶的queryForList(StringstatementName,intstart,intsize)不推薦使用。
    說明:其實現方式是在數據庫取到statementName對應的SQL語句的所有記錄,再通過subList取start,size的子集合
    正例:
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("start", start);
    map.put("size", size);

    沒想到你是這樣的iBATIS!


    第六節 工程結構

    1. 應用分層

    2.【參考】(分層異常處理規約)在DAO層,產生的異常類型有很多,無法用細粒度的異常進行catch,使用catch(Exceptione)方式,并thrownewDAOException(e),不需要打印日志,因為日志在Manager/Service層一定需要捕獲并打到日志文件中去,如果同臺服務器再打日志,浪費性能和存儲。在Service層出現異常時,必須記錄出錯日志到磁盤,盡可能帶上參數信息,相當于保護案發現場。如果Manager層與Service同機部署,日志方式與DAO層處理一致,如果是單獨部署,則采用與Service一致的處理方式。Web層絕不應該繼續往上拋異常,因為已經處于頂層,如果意識到這個異常將導致頁面無法正常渲染,那么就應該直接跳轉到友好錯誤頁面,加上用戶容易理解的錯誤提示信息。開放接口層要將異常處理成錯誤碼和錯誤信息方式返回。

    2. 二方庫依賴

    10.【參考】為避免應用二方庫的依賴沖突問題,二方庫發布者應當遵循以下原則:
    1)精簡可控原則。移除一切不必要的API和依賴,只包含ServiceAPI、必要的領域模型對象、Utils類、常量、枚舉等。如果依賴其它二方庫,盡量是provided引入,讓二方庫使用者去依賴具體版本號;無log具體實現,只依賴日志框架。
    2)穩定可追溯原則。每個版本的變化應該被記錄,二方庫由誰維護,源碼在哪里,都需要能方便查到。除非用戶主動升級版本,否則公共二方庫的行為不應該發生變化。

    3. 服務器

    4.【推薦】在線上生產環境,JVM的Xms和Xmx設置一樣大小的內存容量,避免在GC后調整堆大小帶來的壓力。

    與50位技術專家面對面20年技術見證,附贈技術全景圖

    總結

    以上是生活随笔為你收集整理的常识之外的规范——阿里java开发手册笔记(全章节)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。