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

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

善醫者「未有形而除之」,提升工程健壯性最好的方式是在代碼出現問題以前就排除掉,不給Bug出現的機會。一份好的開發規範就能夠起到這樣的做用,大大減小產品上線後的問題。linux

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

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

下面記錄一些對我比較有啓發的條款,提綱挈領,快速學習,方便尚未閱讀的同窗快速瞭解。設計模式

 

1、編程規約

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

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

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 修飾

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

 

2、異常日誌

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

catch 時請分清穩定代碼和非穩 定代碼,穩定代碼指的是不管如何不會出錯的代碼。對於非穩定代碼的catch儘量進行區分 異常類型,再作對應的異常處理。

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

若是不想處理它,請將該異常拋給它的調用者。最外層的業務使用者,必須處理異常,將其轉化爲用戶能夠理解的內容。

3.在代碼中使用「拋異常」仍是「返回錯誤碼」

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

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

隨意複製和粘貼代碼,必然會致使代碼的重複,在之後須要修改時,須要修改全部的副本,容易遺漏。

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

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

若是不處理,那麼往上拋。

 

3、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 增長存儲。

 

4、工程規約

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 服務器所支持的最大句柄數調高數倍(與服務器的內存數量相關)。

 

5、安全規約

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

相關文章
相關標籤/搜索