阿里巴巴Java開發手冊快速學習


Java作為一門名副其實的工業級語言,語法友好,學習簡單,大規模的應用給代碼質量的管控帶來了困難,特別是團隊開發中,開發過程中的規范會直接影響最終項目的穩定性。

善醫者“未有形而除之”,提高工程健壯性最好的方式是在代碼出現問題之前就排除掉,不給Bug出現的機會。一份好的開發規范就可以起到這樣的作用,大大減少產品上線后的問題。

《阿里巴巴Java開發手冊》是阿里巴巴的內部編碼規范,阿里官方的Java代碼規范標准, 手冊以Java應用開發為維度,分為編程規約、異常日志規約、MYSQL規約、工程規約、安全規約五個章節,給出了強制、推薦、參考三個級別,每條規范都有推薦的約束力度,從命名到項目拆分,不僅規范了一些開發細節,也提出了很多工程開發的哲學,值得好好閱讀。

點擊下載阿里巴巴Java開發手冊》(v1.1.0版)

下面記錄一些對我比較有啟發的條款,提綱挈領,快速學習,方便還沒有閱讀的同學快速了解。

 

一、編程規約

1.如果使用到了設計模式,建議在類名中體現出具體模式

將設計模式體現在名字中,有利於閱讀者快速理解架構設計思想。 

2.相同參數類型,相同業務含義,才可以使用 Java 的可變參數,避免使用 Object

可變參數必須放置在參數列表的最后,盡量不用可變參數編程。

3.對外暴露的接口簽名,原則上不允許修改方法簽名,避免對接口調用方產生影響

接口過時必須加@Deprecated 注解,並清晰地說明采用的新接口或者新服務是什么。

4.關於基本數據類型與包裝數據類型的使用標准如下

1) 所有的POJO類屬性必須使用包裝數據類型

2) RPC方法的返回值和參數必須使用包裝數據類型

3) 所有的局部變量【推薦】使用基本數據類型

POJO 類屬性沒有初值是醒使用者在需要使用時,必須自己顯式地進行賦值,任何 NPE 問題,或者入庫檢查,都由使用者來保證。數據庫的查詢結果可能是null,因為自動拆箱,用基本數據類型接收有NPE風險。

5.注意 serialVersionUID 不一致會拋出序列化運行時異常

序列化類新增屬性時,請不要修改 serialVersionUID 字段,避免反序列失敗;如果完全不兼容升級,避免反序列化混亂,那么請修改 serialVersionUID 值。

 

6.POJO 類必須寫 toString 方法

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

7.final 可提高程序響應效率,聲明成 final 的情況:

1) 不需要重新賦值的變量,包括類屬性、局部變量

2) 對象參數前加final,表示不允許修改引用的指向

3) 類方法確定不允許被重寫

8.慎用 Object 的 clone 方法來拷貝對象

對象的 clone 方法默認是淺拷貝,若想實現深拷貝需要重寫 clone 方法實現屬性對象 的拷貝。

9.類成員與方法訪問控制從嚴

1) 如果不允許外部直接通過new來創建對象,那么構造方法必須是private

2) 工具類不允許有public或default構造方法

3) 類非static成員變量並且與子類共享,必須是protected 4) 類非static成員變量並且僅在本類使用,必須是private

5) 類static成員變量如果僅在本類使用,必須是private

6) 若是static成員變量,必須考慮是否為final

7) 類成員方法只供類內部調用,必須是private

8) 類成員方法只對繼承類公開,那么限制為protected

任何類、方法、參數、變量,嚴控訪問范圍。過寬泛的訪問范圍,不利於模塊解耦。思考:如果是一個 private 的方法,想刪除就刪除,可是一個 public 的 Service 方法,或者一個 public 的成員變量,刪除一下,不得手心冒點汗嗎?變量像自己的小孩,盡量在自己的視線內,變量作用域太大,如果無限制的到處跑,那么你會擔心的。

 

10.ArrayList的subList結果不可強轉成ArrayList,否則會拋出ClassCastException 異常

subList 返回的是 ArrayList 的內部類 SubList,並不是 ArrayList ,而是 ArrayList 的一個視圖,對於SubList子列表的所有操作最終會反映到原列表上。

11.使用工具類 Arrays.asList()把數組轉換成集合時,不能使用其修改集合相關的方法

使用add/remove/clear 方法會拋出 UnsupportedOperationException 異常。asList 的返回對象是一個 Arrays 內部類,並沒有實現集合的修改方法。Arrays.asList體現的是適配器模式,只是轉換接口,后台的數據仍是數組。

12.不要在 foreach 循環里進行元素的 remove/add 操作

remove 元素請使用 Iterator方式,如果並發操作,需要對 Iterator 對象加鎖。

13.獲取單例對象需要保證線程安全,其中的方法也要保證線程安全

資源驅動類、工具類、單例工廠類都需要注意。

14.線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式

這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。 Executors 返回的線程池對象的弊端如下: 1)FixedThreadPool 和 SingleThreadPool:

允許的請求隊列長度為 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。

2)CachedThreadPool 和 ScheduledThreadPool:

允許的創建線程數量為 Integer.MAX_VALUE,可能會創建大量的線程,從而導致 OOM。

 

15.SimpleDateFormat 是線程不安全的類,一般不要定義為static變量

如果定義為static,必須加鎖,或者使用 DateUtils 工具類。 注意線程安全,使用 DateUtils。亦推薦如下處理:

private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { @Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
} };

16.高並發時,同步調用應該去考量鎖的性能損耗

能用無鎖數據結構,就不要用鎖;能鎖區塊,就不要鎖整個方法體;能用對象鎖,就不要用類鎖。

17.並發修改同一記錄時,避免更新丟失

要么在應用層加鎖,要么在緩存加鎖,要么在 數據庫層使用樂觀鎖,使用 version 作為更新依據。 如果每次訪問沖突概率小於 20%,推薦使用樂觀鎖,否則使用悲觀鎖。樂觀鎖的重試次數不得小於 3 次。

18.對多個資源、數據庫表、對象同時加鎖時,需要保持一致的加鎖順序,否則可能會造成死鎖

19.使用 CountDownLatch 進行異步轉同步操作,每個線程退出前必須調用countDown

方法,線程執行代碼注意 catch 異常,確保 countDown 方法可以執行,避免主線程無法執行 至 await 方法,直到超時才返回結果。注意,子線程拋出異常堆棧,不能在主線程 try-catch 到。

20.避免 Random 實例被多線程使用,雖然共享該實例是線程安全的,但會因競爭同一 seed 導致的性能下降。

Random 實例包括 java.util.Random 的實例或者 Math.random()實例。

 

21.volatile 解決多線程內存不可見問題

對於一寫多讀,是可以解決變量同步問題, 但是如果多寫,同樣無法解決線程安全問題。如果是 count++操作,使用如下類實現: AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是 JDK8,推薦使用 LongAdder 對象,比 AtomicLong 性能更好(減少樂觀鎖的重試次數)。

22.ThreadLocal 無法解決共享對象的更新問題,建議使用 static 修飾

這個變量是針對一個線程內所有操作共有的,所以設置為靜態變量,所有此類實例共享 此靜態變量 ,也就是說在類第一次被使用時裝載,只分配一塊存儲空間,所有此類的對象(只要是這個線程內定義的)都可以操控這個變量。

 

二、異常日志

1.對大段代碼進行 try-catch,這是不負責任的表現

catch 時請分清穩定代碼和非穩 定代碼,穩定代碼指的是無論如何不會出錯的代碼。對於非穩定代碼的catch盡可能進行區分 異常類型,再做對應的異常處理。

2.捕獲異常是為了處理它,不要捕獲了卻什么都不處理而拋棄之

如果不想處理它,請將該異常拋給它的調用者。最外層的業務使用者,必須處理異常,將其轉化為用戶可以理解的內容。

3.在代碼中使用“拋異常”還是“返回錯誤碼”

對於公司外的 http/api 開放接口必須 使用“錯誤碼”;而應用內部推薦異常拋出;跨應用間 RPC 調用優先考慮使用 Result 方式,封 裝 isSuccess、“錯誤碼”、“錯誤簡短信息”。

4.避免出現重復的代碼(Don’t Repeat Yourself),即DRY原則

隨意復制和粘貼代碼,必然會導致代碼的重復,在以后需要修改時,需要修改所有的副本,容易遺漏。

5.對trace/debug/info 級別的日志輸出,必須使用條件輸出形式或者使用占位符的方

6.異常信息應該包括兩類信息:案發現場信息和異常堆棧信息

如果不處理,那么往上拋。

 

三、MySQL 規約

1.表達是與否概念的字段,必須使用 is_xxx 的方式命名,數據類型是 unsigned tinyint ( 1表示是,0表示否),此規則同樣適用於odps建表。 任何字段如果為非負數,必須是unsigned。

2.小數類型為 decimal,禁止使用 float 和 double

float 和 double 在存儲的時候,存在精度損失的問題,很可能在值的比較時,得到不正確的結果。如果存儲的數據范圍超過 decimal 的范圍,建議將數據拆成整數和小數分開存儲。

3.表必備三字段:id, gmtcreate, gmtmodified

其中id必為主鍵,類型為unsigned bigint、單表時自增、步長為1。gmtcreate, gmtmodified 的類型均為 date_time 類型。

4.單表行數超過 500 萬行或者單表容量超過 2GB,才推薦進行分庫分表

如果預計三年后的數據量根本達不到這個級別,請不要在創建表時就分庫分表。避免過度設計。

5.業務上具有唯一特性的字段,即使是組合字段,也必須建成唯一索引

 

6.在 varchar 字段上建立索引時,必須指定索引長度

沒必要對全字段建立索引,根據實際文本區分度決定索引長度。 說索引的長度與區分度是一對矛盾體,一般對字符串類型數據,長度為 20 的索引,區分 度會高達 90%以上,可以使用 count(distinct left(列名, 索引長度))/count(*)的區分度 來確定。

7.利用覆蓋索引來進行查詢操作,來避免回表操作

能夠建立索引的種類:主鍵索引、唯一索引、普通索引,而覆蓋索引是一種查詢的一種 效果,用explain的結果,extra列會出現:using index。如果索引包含所有滿足查詢需要的數據的索引成為覆蓋索引(Covering Index),也就是平時所說的不需要回表操作

8.利用延遲關聯或者子查詢優化超多分頁場景

MySQL 並不是跳過 offset 行,而是取 offset+N 行,然后返回放棄前 offset 行,返回 N 行,那當 offset 特別大的時候,效率就非常的低下,要么控制返回的總頁數,要么對超過特定閾值的頁數進行 SQL 改寫。

9.SQL 性能優化的目標

至少要達到 range 級別,要求是 ref 級別,如果可以是 consts 最好。

1)consts 單表中最多只有一個匹配行(主鍵或者唯一索引),在優化階段即可讀取到數據。 2)ref 指的是使用普通的索引(normal index)。 3)range 對索引進行范圍檢索。

10.不要使用 count(列名)或 count(常量)來替代 count(*)

count()就是 SQL92 定義 的標准統計行數的語法,跟數據庫無關,跟 NULL 和非 NULL 無關。 count()會統計值為 NULL 的行,而 count(列名)不會統計此列為 NULL 值的行。

11.使用 ISNULL()來判斷是否為 NULL 值

注意,NULL與任何值的直接比較都為 NULL

12.不得使用外鍵與級聯,一切外鍵概念必須在應用層解決

外鍵與級聯更新適用於單機低並發,不適合分布式、高並發集群;級聯更新是強阻塞,存在數據庫更新風暴的風險;外鍵影響數據庫的插入速度。

13.iBATIS 自帶的 queryForList(String statementName,int start,int size)不推薦使用

其實現方式是在數據庫取到 statementName 對應的 SQL 語句的所有記錄,再通過 subList 取 start,size 的子集合,線上因為這個原因曾經出現過 OOM。

14.不要寫一個大而全的數據更新接口

傳入為 POJO 類,不管是不是自己的目標更新字段, 都進行 update table set c1=value1,c2=value2,c3=value3; 這是不對的。

執行 SQL 時,盡量不要更新無改動的字段,一是易出錯;二是效率低;三是 binlog 增加存儲。

 

四、工程規約

1.高並發服務器建議調小 TCP 協議的 time_wait 超時時間

操作系統默認 240 秒后,才會關閉處於 timewait 狀態的連接,在高並發訪問下,服 務器端會因為處於 timewait 的連接數太多,可能無法建立新的連接,所以需要在服務器上 調小此等待值。 正例:在 linux 服務器上請通過變更/etc/sysctl.conf 文件去修改該缺省值(秒): net.ipv4.tcpfintimeout = 30

2.調大服務器所支持的最大文件句柄數(File Descriptor,簡寫為fd)

主流操作系統的設計是將 TCP/UDP 連接采用與文件一樣的方式去管理,即一個連接對應於一個 fd。主流的 linux 服務器默認所支持最大 fd 數量為 1024,當並發連接數很大時很 容易因為 fd 不足而出現“open too many files”錯誤,導致新的連接無法建立。 建議將 linux 服務器所支持的最大句柄數調高數倍(與服務器的內存數量相關)。

 

五、安全規約

1. 隸屬於用戶個人的頁面或者功能必須進行權限控制校驗

防止沒有做水平權限校驗就可隨意訪問、操作別人的數據,比如查看、修改別人的訂單。

2. 用戶敏感數據禁止直接展示,必須對展示數據脫敏

查看個人手機號碼會顯示成:158****9119,隱藏中間 4 位,防止隱私泄露。

3. 用戶輸入的 SQL 參數嚴格使用參數綁定或者 METADATA 字段值限定,防止 SQL 注入, 禁止字符串拼接 SQL 訪問數據庫

4. 用戶請求傳入的任何參數必須做有效性驗證

忽略參數校驗可能導致: page size過大導致內存溢出 惡意order by導致數據庫慢查詢 任意重定向 SQL注入 反序列化注入 正則輸入源串拒絕服務ReDoS——Java 代碼用正則來驗證客戶端的輸入,有些正則寫法驗證普通用戶輸入沒有問題, 但是如果攻擊人員使用的是特殊構造的字符串來驗證,有可能導致死循環的效果。

5. 禁止向 HTML 頁面輸出未經安全過濾或未正確轉義的用戶數據

6. 表單、AJAX 交必須執行 CSRF 安全過濾

CSRF(Cross-site request forgery)跨站請求偽造是一類常見編程漏洞。對於存在 CSRF 漏洞的應用/網站,攻擊者可以事先構造好 URL,只要受害者用戶一訪問,后台便在用戶 不知情情況下對數據庫中用戶參數進行相應修改。

7. 在使用平台資源,譬如短信、郵件、電話、下單、支付,必須實現正確的防重放限制, 如數量限制、疲勞度控制、驗證碼校驗,避免被濫刷、資損

如注冊時發送驗證碼到手機,如果沒有限制次數和頻率,那么可以利用此功能騷擾到其 它用戶,並造成短信平台資源浪費。

8. 發貼、評論、發送即時消息等用戶生成內容的場景必須實現防刷、文本內容違禁詞過濾等風控策略

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM