java經常使用重構優化總結--本身親身體驗

代碼重構
  6大原則:
    單一職責原則(一個類最好最好只有一種行爲動機,太多承擔職責會致使耦合度過高)、
    開放封閉原則(功能能夠擴展,可是不能夠內部修改)、
    依賴倒轉原則(應該依賴抽象而不該該依賴具體對象)、
    里氏代換原則(父類都替換成它的子類程序的行爲沒有變化。 正是有了里氏代換原則,才使得」開-閉「原則成爲了可能)、
    接口隔離原則(爲同一個角色提供寬、窄不一樣的接口,以對付不一樣的客戶端)、
    迪米特法則(最少知道原則;若是兩個類沒必要彼此直接通訊,那麼這兩個類就不該當發生直接的相互做用)
  設計模式應用:
    <1. 適配器模式(由原來sphinx適配爲solr應用)
    <2. 抽象工廠方法模式+反射+配置文件實現可配置的動態化(csv、bean、json等)
    <3. 建造者模式:將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示
        主要用於解析客戶端傳過來的查詢條件進行構造和封裝,生成對應的solrQuery。以及最後查詢完畢以後構建分裝成對應返回的json格式
    <4. 過濾器模式:容許開發人員使用不一樣的標準來過濾一組對象,經過邏輯運算以解耦的方式把它們鏈接起來
        過濾篩選傳入查詢時間、過濾目前已經有的集羣定位查詢範圍(hot/recent/warm/all)
    <5. 觀察者模式:定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知全部觀察者對象,使他們可以自動更新本身(模擬ZK)
        實時預處理觸發更新底層依賴數據庫更新。
        多個更新類註冊到統一監聽器上 + 緩存mamcache + 出發更新模塊(監控數據庫數據變化+指標) + 數據庫java

代碼調優
    <1. 能夠指定final修飾的類和方法那就必定要指定,java編譯器會內聯全部的final方法,提高java運行效率。Java編譯器會尋找機會內聯全部的final方法,內聯對於提高Java運行效率做用重大,具體參見Java運行期優化。此舉可以使性能平均提升50%。
    <2. 字符串拼接不能使用+來拼接,底層會建立一個新對象,這兩個對象相拼接,應該使用StringBuilder/StringBuffer
    <3. 儘可能使用局部變量。由於這是在棧中建立,隨着方法的運行結束這些內容就消失了,不須要額外的垃圾回收
    <4. 在一些大的循環時,儘可能減小重複計算;例如for循環中的length數據庫

  對方法的調用,即便方法中只有一句語句,也是有消耗的,包括建立棧幀、調用方法時保護現場、調用方法完畢時恢復現場等。因此例以下面的操做:apache

    for (int i = 0; i < list.size(); i++)

    {...}

  建議替換爲:json

    for (int i = 0, int length = list.size(); i < length; i++)

    {...}

  這樣,在list.size()很大的時候,就減小了不少的消耗設計模式


    <5. 儘可能使用懶加載方式即在須要的時候纔去建立
    <6. 不要在循環中使用try...catch...
    <7. 在能夠評估出待添加內容的長度,那麼在初始化容器的時候就直接指定開闢那麼大的空間,HashMap除外,大概開闢2的次冪大小就能夠了,由於它底層是列表形式存儲數組

  好比ArrayList、LinkedLlist、StringBuilder、StringBuffer、HashMap、HashSet等等,以StringBuilder爲例:緩存

  (1)StringBuilder()      // 默認分配16個字符的空間app

  (2)StringBuilder(int size)  // 默認分配size個字符的空間函數

  (3)StringBuilder(String str) // 默認分配16個字符+str.length()個字符空間工具

  能夠經過類的初始化函數來設定它的初始化容量,這樣能夠明顯地提高性能。好比StringBuilder吧,length表示當前的StringBuilder能保持的字符數量。由於當StringBuilder達到最大容量的時候,它會將自身容量增長到當前的2倍再加2,不管什麼時候只要StringBuilder達到它的最大容量,它就不得不建立一個新的字符數組而後將舊的字符數組內容拷貝到新字符數組中—-這是十分耗費性能的一個操做。試想,若是能預估到字符數組中大概要存放5000個字符而不指定長度,最接近5000的2次冪是4096,每次擴容加的2無論,那麼:

  (1)在4096的基礎上,再申請8194個大小的字符數組,加起來至關於一次申請了12290個大小的字符數組,若是一開始能指定5000個大小的字符數組,就節省了一倍以上的空間

  (2)把原來的4096個字符拷貝到新的的字符數組中去

  這樣,既浪費內存空間又下降代碼運行效率。因此,給底層以數組實現的集合、工具類設置一個合理的初始化容量是錯不了的,這會帶來立竿見影的效果。可是,注意,像HashMap這種是以數組+鏈表實現的集合,別把初始大小和你估計的大小設置得同樣,由於一個table上只鏈接一個對象的可能性幾乎爲0。初始大小建議設置爲2的N次冪,若是能估計到有2000個元素,設置成new HashMap(128)、new HashMap(256)均可以。


    <8. 當複製大量數據時最好使用System.arrayCopy()方法 [當內容長度大於10000左右,使用此方法,不然過小的話使用for循環比較快]
    <9. 乘法、除法儘可能使用位運算
    <10. 循環內不要建立對象
    <11. 不要建立不使用的對象、不要引入不用到的引用。
    <12. 使用數據庫就使用數據庫鏈接池
    <13. 使用帶緩衝的輸入輸出流進行IO操做:BufferedReader、BufferedInputStream / BufferedWriter、BufferedOutputStream

  BufferedReader會一次性從物理流中讀取8k(默認數值,能夠設置)字節內容到內存,若是外界有請求,就會到這裏存取,若是內存裏沒有才到物理流裏再去讀。即便讀,也是再8k。  若是不使用BufferedReader而直接讀物理流,是按字節來讀,對物理流的每次讀取,都有IO操做。IO操做是最耗費時間的。BufferedReader就是減小了大量IO操做,而爲你節省了時間。  

  簡單的說,一次IO操做,讀取一個字節也是讀取,讀取8k個字節也是讀取,二者花費時間相差很少。而一次IO的來回操做卻要耗費大量時間。  
   比如是一輛大型汽車(設裝100人),要去車站接人到公司,接一我的也是接,接100我的也是接,而時間同樣。顯然,接100我的最划算。
  物理流就是一次一個字節(一我的)
  Buffered就是一次8k個字節(100我的)
  對於讀取定長字節文件,固然BufferedReader更快了!


    <14. 基本類型轉換爲字符串,最快的使用.toString()方法

  因此之後遇到把一個基本數據類型轉爲String的時候,優先考慮使用toString()方法。至於爲何,很簡單:

  一、String.valueOf()方法底層調用了Integer.toString()方法,可是會在調用前作空判斷

  二、Integer.toString()方法就不說了,直接調用了

  三、i + 「」底層使用了StringBuilder實現,先用append方法拼接,再用toString()方法獲取字符串

  三者對比下來,明顯是2最快、1次之、3最慢


    <15. 遍歷map對應的key-value,最好使用iterator的EntrySet方式;若是隻是使用key值,則使用 hm.keySet();更加合理
    <16. 多使用一些apache下提供的包:StringUtils、Collections判空、Arrays轉換list,或者使用Guava:Joiner、Splitter、Strings、Maps判空

相關文章
相關標籤/搜索