不少人都知道,阿里巴巴在2017發佈了《阿里巴巴Java開發手冊》,先後推出了不少個版本,並在後續推出了與之配套的IDEA插件和書籍。程序員
相信不少Java開發都或多或少看過這份手冊,這份手冊有7個章節,覆蓋了編程規約、異常日誌、單元測試、安全規約、MySQL數據庫、工程結構以及設計規約等方面。數據庫
這份規約能夠說是覆蓋了Java開發的方方面面,若是還有人沒看的話,強烈建議你們好好看看,而且仔細研讀。編程
手冊中,有那麼一些規則,是比較容易理解的。好比一些變量命名規範,有另一些規則,是不太容易理解的,背後是有不少思考的,有一些則是阿里這麼多年來遇到的坑的總結。json
這份手冊在誕生之初,是在阿里內部的,那時候就引發了普遍的討論。最終外界看到的那份手冊,是阿里無數工程師"挑剔"後的結果,能夠說是凝聚了無數工程師成功的經驗、踩過的坑等。數組
其實,規約最大的價值,應該是促令人去思考規約制定背後的思考。真的去探查規約背後的原理,這個過程當中能夠學習到不少東西。安全
《阿里巴巴Java開發手冊》尚未的小夥伴能夠來私信我【阿里學習手冊】領取一份多線程
在Java生態體系中,圍繞着日誌,有不少成熟的解決方案。關於日誌輸出,主要有兩類工具。併發
一類是日誌框架,主要用來進行日誌的輸出的,好比輸出到哪一個文件,日誌格式如何等。另一類是日誌門面,主要一套通用的API,用來屏蔽各個日誌框架之間的差別的。app
因此,對於Java工程師來講,關於日誌工具的使用,最佳實踐就是在應用中使用如Log4j + SLF4J 這樣的組合來進行日誌輸出。框架
這樣作的最大好處,就是業務層的開發不須要關心底層日誌框架的實現及細節,在編碼的時候也不須要考慮往後更換框架所帶來的成本。這也是門面模式所帶來的好處。
HashMap有擴容機制,就是當達到擴容條件時會進行擴容。若是咱們沒有設置初始容量大小,隨着元素的不斷增長,HashMap會發生屢次擴容,而HashMap中的擴容機制決定了每次擴容都須要重建hash表,是很是影響性能的。
默認狀況下,當咱們設置HashMap的初始化容量時,實際上HashMap會採用第一個大於該數值的2的冪做爲初始化容量。
可是,爲了最大程度的避免擴容帶來的性能消耗,咱們建議能夠把默認容量的數字設置成expectedSize / 0.75F + 1.0F 。在平常開發中,可使用
Map<String,String>map=Maps.newHashMapWithExpectedSize(10);
來建立一個HashMap,計算的過程guava會幫咱們完成。
可是,以上的操做是一種用內存換性能的作法,真正使用的時候,要考慮到內存的影響。
咱們使用的加強for循環,實際上是Java提供的語法糖,其實現原理是藉助Iterator進行元素的遍歷。
可是若是在遍歷過程當中,不經過Iterator,而是經過集合類自身的方法對集合進行添加/刪除操做。那麼在Iterator進行下一次的遍歷時,經檢測發現有一次集合的修改操做並未經過自身進行,那麼多是發生了併發被其餘線程執行的,這時候就會拋出異常,來提示用戶可能發生了併發修改,這就是所謂的fail-fast機制。
固然仍是有不少種方法能夠解決這類問題的。好比使用普通for循環、使用Iterator進行元素刪除、使用Stream的filter、使用fail-safe的類等。
SimpleDateFormat主要能夠在String和Date之間作轉換,還能夠將時間轉換成不一樣時區輸出。可是在併發場景中SimpleDateFormat是不能保證線程安全的,須要開發者本身來保證其安全性。
SimpleDateFormat中的format方法在執行過程當中,會使用一個成員變量calendar來保存時間。這其實就是問題的關鍵。
若是一個SimpleDateFormat使用的是static定義的。那麼這個SimpleDateFormat就是一個共享變量,隨之,SimpleDateFormat中的calendar也就能夠被多個線程訪問到。就會有併發安全問題。
使用+拼接字符串,其實只是Java提供的一個語法糖,經過反編譯,咱們能夠發現,其實字符串常量使用"+"在拼接過程當中,是將String轉成了StringBuilder後,使用其append方法進行處理的。
若是在一個for循環中,使用"+"對字符串進行拼接,那麼每一次都會從新new一個StringBuilder,而後再把String轉成StringBuilder,再進行append。
而頻繁的新建對象固然要耗費不少時間了,不只僅會耗費時間,頻繁的建立對象,還會形成內存資源的浪費。
序列化提供了一種方案,可讓你在即便JVM停機的狀況下也能把對象保存下來的方案。就像咱們平時用的U盤同樣。把Java對象序列化成可存儲或傳輸的形式(如二進制流),好比保存在文件中。這樣,當再次須要這個對象的時候,從文件中讀取出二進制流,再從二進制流中反序列化出對象。
虛擬機是否容許反序列化,不只取決於類路徑和功能代碼是否一致,一個很是重要的一點是兩個類的序列化 ID 是否一致,這個所謂的序列化ID,就是咱們在代碼中定義的serialVersionUID。
在進行反序列化時,JVM會把傳來的字節流中的serialVersionUID與本地相應實體類的serialVersionUID進行比較,若是相同就認爲是一致的,能夠進行反序列化,不然就會出現序列化版本不一致的異常,便是InvalidCastException。
subList是List接口中定義的一個方法,該方法主要用於返回一個集合中的一段、能夠理解爲截取一個集合中的部分元素,他的返回值也是一個List。
可是subList 返回的並非一個全新的List,而是一個視圖。就是說,SubList並無從新建立一個List,而是直接引用了原有的List(返回了父類的視圖),只是指定了一下他要使用的元素的範圍而已(從fromIndex(包含),到toIndex(不包含))。
因此,首先咱們沒法將subList方法獲得的集合直接轉換成ArrayList。由於SubList只是ArrayList的內部類,他們之間並無繼承關係,故沒法直接進行強制類型轉換。
另外,視圖和原List的修改還須要注意幾點,尤爲是他們之間的相互影響:
因此,阿里巴巴Java開發手冊中有另一條規定:
做爲一門面向對象開發的語言,代碼複用是Java引人注意的功能之一。Java代碼的複用有繼承,組合以及代理三種具體的表現形式。
繼承,在寫代碼的時候就要指明具體繼承哪一個類,因此,類的繼承關係是在編譯期就肯定的。而且從基類繼承來的實現是沒法在運行期動態改變的,所以下降了應用的靈活性。
組合,在寫代碼的時候能夠採用面向接口編程。因此,類的組合關係通常在運行期肯定。另外,代碼複用方式上也有必定區別:
還有,Java中不支持多繼承,而組合是沒有限制的。就像一我的只能有一個父親,可是他能夠有很不少輛車。
fastjson和jackson在把對象序列化成json字符串的時候,是經過反射遍歷出該類中的全部getter方法,獲得getHollis和isSuccess,而後根據JavaBeans規則,他會認爲這是兩個屬性hollis和success的值。
可是Gson並非這麼作的,他是經過反射遍歷該類中的全部屬性,並把其值序列化成json
能夠看到,因爲不一樣的序列化工具,在進行序列化的時候使用到的策略是不同的,因此,對於同一個類的同一個對象的序列化結果多是不一樣的。
若是對於同一個對象,我使用fastjson進行序列化,再使用Gson反序列化,而且恰巧這個對象中某個屬性是以isXXX命名的,那麼就會產生問題。
做爲開發者,咱們應該想辦法儘可能避免這種問題的發生,對於POJO的設計者來講,只須要作簡單的一件事就能夠解決這個問題了,那就是把isSuccess改成success。
由於COUNT(*)是SQL92定義的標準統計行數的語法,因此MySQL對他進行了不少優化,MyISAM中會直接把表的總行數單獨記錄下來供COUNT(*)查詢,而InnoDB則會在掃表的時候選擇最小的索引來下降成本。固然,這些優化的前提都是沒有進行where和group的條件查詢。
在InnoDB中COUNT(*)和COUNT(1)實現上沒有區別,並且效率同樣,可是COUNT(字段)須要進行字段的非NULL判斷,因此效率會低一些。
由於COUNT(*)是SQL92定義的標準統計行數的語法,而且效率高,因此請直接使用COUNT(*)查詢表的行數!
Java中的BlockingQueue主要有兩種實現,分別是ArrayBlockingQueue 和 LinkedBlockingQueue。
ArrayBlockingQueue是一個用數組實現的有界阻塞隊列,必須設置容量。
LinkedBlockingQueue是一個用鏈表實現的有界阻塞隊列,容量能夠選擇進行設置,不設置的話,將是一個無邊界的阻塞隊列,最大長度爲Integer.MAX_VALUE。
這裏的問題就出在:不設置的話,將是一個無邊界的阻塞隊列,最大長度爲Integer.MAX_VALUE。也就是說,若是咱們不設置LinkedBlockingQueue的容量的話,其默認容量將會是Integer.MAX_VALUE。
而newFixedThreadPool中建立LinkedBlockingQueue時,並未指定容量。此時,LinkedBlockingQueue就是一個無邊界隊列,對於一個無邊界隊列來講,是能夠不斷的向隊列中加入任務的,這種狀況下就有可能由於任務過多而致使內存溢出問題。
上面提到的問題主要體如今newFixedThreadPool和newSingleThreadExecutor兩個工廠方法上,並非說newCachedThreadPool和newScheduledThreadPool這兩個方法就安全了,這兩種方式建立的最大線程數多是Integer.MAX_VALUE,而建立這麼多線程,必然就有可能致使OOM。
以上,就是我以前一段時間經過學習《手冊》中的部分規則以後,本身總結的一些內容,這個過程當中本身也學習到不少東西。
因此想說,這纔是學習《手冊》的正確姿式,這樣才能最大程度的成長!
這個系列還在繼續更新,後面還會會逐步完善。歡迎關注與交流。
《阿里巴巴Java開發手冊》尚未的小夥伴能夠來私信我【阿里學習手冊】領取一份