Java開發手冊精華總結

OpenWrite行業資訊發表於2020-10-06

阿里 Java 開發手冊的思考總結

一個優秀的工程師和一個普通的工程師的區別,不是滿天飛的架構圖,他的功底體現在所寫的每一行程式碼上。 -- 畢玄

1. 命名風格

【書摘】類名用 UpperCamelCase 風格,比如 DO/BO/VO/PO

【書摘】方法用 lowerCamelCase 風格,儘量是動詞

小思考:例如常用的

  • 獲取單個物件,getUserById
  • 獲取物件累不,listUserByGroupId
  • 統計物件數量,countUserByClassId
  • 新增、刪除,insert,delete,update 等

2. 常量定義

【書摘】如果變數值僅在一個範圍變化,則用 enum 型別

小思考:例如電商中訂單狀態,用手指也能列出來。所以一般定義個 OrderSatausEnum 搞定,虛擬碼如下:

public enum OrderSatausEnum {
    TO_PAY(1,"代付款"), PAID(2,"已付款") ...;

    private Integer orderStatus;
    private String orderSatausDes;

    OrderSatausEnum(Integer orderStatus,String orderSatausDes) {
        this.orderStatus = orderStatus;
        this.orderSatausDes = orderSatausDes;
    }

    // get set
}

進一步,如果訂單某天要加個“待拼團”狀態咋辦?所以這些配置化的東西,可以上配置中心。比如攜程出的 apollo 等

3. OOP 規則

【書摘】Object 的 equals 方法容易拋空指標異常,應使用常量或者確定優質的物件來呼叫 equals 。

比如:“TO_PAY”.equals(order.getOrderStatus())。反著寫就不對了,因為 order.getOrderStatus() 可能為 null。

自然,更加推薦 java.util.Objects#equals 工具類。

【書摘】所有相同型別的包裝類物件之間的值比較,全部使用 equals 方法。

小思考:別用 == 了。equals 也要注意些事情,比如 Byte 型別的 status 物件值,用 equals 要注意如下:

getStatus().equals(0) // 反例,false
getStatus().equals((byte)0) // 正例,true

還有, == 比如 Integer 在 -128 到 127 範圍比較正常,超過就不正常。原因是 -128 到 127 範圍的物件在 IntegerCache.cache 中產生,會複用物件。所以所以,切記切記,別用 == 了,用 equals 去比較。

4. 集合處理

【書摘】不要在 for 迴圈中進行元素的 remove/add 操作。remove 請使用 Iterator 方法,如果有併發操作,則對 Iterator 物件加鎖。

具體 Iterator 怎麼操作集合,百度下即可。這還是典型的 迭代器設計模式,可以深入原始碼看看人家的簡單實現原理,又能學到一發高階知識。

5. 併發處理

【書摘】第一,執行緒必須通過執行緒池來提供,不允許顯式建立執行緒。第二,執行緒池不允許用 Executors 建立,應使用 ThreadPoolExecutor 去建立。因為 Executors 建立的幾種 ThreadPool 會有弊端:

  • FixedThreadPool 和 SingleThreadPool 允許請求佇列長度為 Integer.MAX_VALUE ,大量請求,會導致 OOM
  • CachedThreadPool 和 ScheduledThreadPool 允許建立最大的執行緒數為 Integer.MAX_VALUE,大量建立執行緒,會導致 OOM

所以,使用 ThreadPoolExecutor 的原因是能更好地理解執行緒池的執行規則,規避資源耗盡,更好地貼合某個業務場景,去建立更適合的執行緒池。


【書摘】在高併發場景中,同步呼叫應該考慮鎖的效能損耗。能用無鎖資料結構,就不要用鎖;能鎖區塊,就不要鎖整個方法體;能用物件鎖,就不要用類鎖。即,加鎖的粒度越小,效能損耗越小。並且避免鎖的程式碼塊中呼叫了 RPC 方法。

另外,同時對多個資源加鎖的時候,需要保持一致的加鎖順序。否則,一個執行緒加鎖順序為 ABC,另一個加鎖順序為 ACB 或 BAC 等,會造成死鎖。如圖:

file


【書摘】金融資金相關資訊,使用悲觀鎖。比如更新某個使用者的錢包餘額欄位。

小思考:我以前做 P2P 的時候,就很簡單地使用了 MySQL 的行鎖。

SELECT * FROM xx WHREER xx.id=888 FOR UPDATE

具體行鎖,表鎖大家可以自行百度瞭解。

6. 控制語句

【書摘】高併發場景,比如秒殺場景,商品扣庫存,庫存的判斷不要用“等於”來判斷商品庫存已售罄的條件。應使用大於或者小於的條件來代替。

小思考:這是典型的超賣場景。有人會問也會存在超賣幾件的問題吧?答案是是的。但如果用 等於 來判斷,超賣的件數會很多很多,比如達到 1 萬件。但超賣 1 萬件和超賣 1 件是不一樣等級的故障。或者是一個故障和一個不是故障的區別。

7.異常處理

【書摘】異常不要用來做流程控制,條件控制

小思考:昨天京東小哥問我,這個能這麼搞降級嗎?如下程式碼:

try {
    searchFromES()
}catch(){
    searchFromDB()
}

這不算降級,這也不能這麼搞。第一,程式碼這也寫就不對,異常不要用來做流程控制,條件控制。第二,這個只要實現 ES 讀取有問題,讀取不到就讀 DB。可以考慮責任鏈設計模式去實現。虛擬碼如下:


ESHandle {
    void handle() {
        try {
            searchFromES()
        }catch(){
        }
    }
}

DBHandle {
    void handle() {
        try {
            searchFromES()
        }catch(){
        }
    }
}

// 兩個 Handle 利用責任鏈去實現即可。

8. 建表規約 、SQL 語句

【書摘】當單錶行數超過 500 萬行或者單表容量超過 2 GB時,才推薦進行分庫分表。

如果預計三年後的數量級無法達到這個級別,請不要在建立表時就分庫分表。


【書摘】不要使用 count(列名) 或者 count(常量) 來替代 count(*)。 因為它是 SQL92 定義的標準統計行數的預發。它會統計 NULL 的行。


【書摘】where 條件下里面的 in 能避免就避免,要注意 in 裡面的集合數量,控制在 1000 個之內。


【書摘】在程式碼中寫分頁查詢,如果 count 為 0 ,直接返回 空列表。避免執行下面的分頁語句。

9.伺服器

【書摘】高併發伺服器建議調小 TCP 協議的 time_wait 超時時間。Linux 修改 /etc/sysctl.conf 檔案,程式碼如下:

net.ipv4.tcp_fin_timeout = 30

【書摘】JVM 設定引數 -XX:+HeapDumpOnOutOfMemoryError。讓 JVM 碰到 OOM 的時候,輸出 dump 資訊。

小思考:這個很重要。二者得保留事故伺服器現場。比如 OOM 了某個伺服器,則在 VIP 或者啥摘到該機器,讓該機器不再有請求進入。然後去檢視 dump 資訊,去排查 OOM 問題。

工程師對於程式碼,一定要精益求精,不論從效能,還是簡潔優雅,都要具備精益求精的工匠精神,認真打磨自己的作品。 -- 多隆

本文由部落格群發一文多發等運營工具平臺 OpenWrite 釋出

相關文章