代碼優化最重要的做用應該是避免未知的錯誤,所以在寫代碼的時候,從源頭開始注意各類細節,權衡並使用最優的選擇,將會很大程度上避免出現未知的錯誤,從長遠看也極大的下降了工做量。因此說代碼優化的目標是減少代碼體積、提升代碼運行效率。優化是無止境的,本文也只給出整理的一些常見優化建議。java
(1)儘可能指定類、方法的 final 修飾符。程序員
帶有 final 修飾符的類是不可派生的。在 Java 核心 API 中,有許多應用 final 的例子,例如 java.lang.String,整個類都是 final 的。爲類指定 final 修飾符可讓類不能夠被繼承,爲方法指定 final 修飾符可讓方法不能夠被重寫。若是指定了一個類爲 final,則該類全部的方法都是 final 的。Java 編譯器會尋找機會內聯全部的 final 方法,內聯對於提高 Java 運行效率做用重大,具體能夠查閱 Java 運行期優化相關資料,此舉可以使性能平均提升 50%。算法
(2)儘可能重用對象。sql
特別是 String 對象的使用,出現字符串鏈接時應該使用 StringBuilder/StringBuffer 代替。因爲 Java 虛擬機不只要花時間生成對象,之後可能還須要花時間對這些對象進行垃圾回收和處理,所以生成過多的對象將會給程序的性能帶來很大的影響。數據庫
(3)儘量使用局部變量。編程
調用方法時傳遞的參數以及在調用中建立的臨時變量都保存在棧中,速度較快,其餘變量,如靜態變量、實例變量等,都在堆中建立,速度較慢。另外,棧中建立的變量,隨着方法的運行結束,這些內容就沒了,不須要額外的垃圾回收。數組
(4)及時關閉流。安全
Java 編程過程當中,進行數據庫鏈接、I/O 流操做時務必當心,在使用完畢後,及時關閉以釋放資源。由於對這些大對象的操做會形成系統大的開銷,稍有不慎,將會致使嚴重的後果。性能優化
(6)儘可能採用懶加載的策略,即在須要的時候才建立。服務器
這個原則其實就是節約,具體樣例以下。
(7)慎用異常。
異常對性能不利,拋出異常首先要建立一個新的對象,Throwable 接口的構造函數調用名爲 fillInStackTrace() 的本地同步方法,fillInStackTrace() 方法檢查堆棧,收集調用跟蹤信息。只要有異常被拋出,Java 虛擬機就必須調整調用堆棧,由於在處理過程當中建立了一個新的對象。異常只能用於錯誤處理,不該該用來控制程序流程。
(8)不要在循環中使用 try-catch,應該把其放在最外層。
根據網友們提出的意見,這一點我認爲值得商榷,其實分業務場景吧,有些場景須要循環終止,有些只是爲了忽略當此循環處理。
(9)若是能估計到待添加的內容長度,爲底層以數組方式實現的集合、工具類指定初始長度。
好比 ArrayList、LinkedLlist、StringBuilder、StringBuffer、HashMap、HashSet 等,以 StringBuilder 爲例,StringBuilder() 構造方法默認分配 16 個字符的空間,StringBuilder(int size) 構造方法默認分配 size 個字符的空間,StringBuilder(String str) 構造方法默認分配 16 個字符加 str.length() 個字符空間,因此能夠經過類的構造方法來設定它的初始化容量,這樣能夠明顯地提高性能。
(10)當複製大量數據時,使用 System.arraycopy() 命令。
這個確定你們都沒有疑問的,性能優化的實現而已。
(11)乘法和除法使用移位操做。
用移位操做能夠極大地提升性能,由於在計算機底層,對位的操做是最方便、最快的,可是移位操做雖然快,可能會使代碼不太好理解,所以最好加上相應的註釋。
(12)循環內不要不斷建立對象引用。
見以下案例解釋分析緣由。
(13)基於效率和類型檢查的考慮,應該儘量使用 array,沒法肯定數組大小時才使用 ArrayList。
(14)儘可能使用 HashMap、ArrayList、StringBuilder,除非線程安全須要,不然不推薦使用 Hashtable、Vector、StringBuffer,後三者因爲使用同步機制而致使了性能開銷。
(15)不要將數組聲明爲 public static final。
由於這毫無心義,這樣只是定義了引用爲 static final,數組的內容仍是能夠隨意改變的,將數組聲明爲 public 更是一個安全漏洞,這意味着這個數組能夠被外部類所改變。
(16)儘可能在合適的場合使用單例。
使用單例能夠減輕加載的負擔、縮短加載的時間、提升加載的效率,但並非全部地方都適用於單例,簡單來講,單例主要適用於如下三個方面:
控制資源的使用,經過線程同步來控制資源的併發訪問;
控制實例的產生,以達到節約資源的目的;
控制數據的共享,在不創建直接關聯的條件下,讓多個不相關的進程或線程之間實現通訊;
(17)儘可能避免隨意使用靜態變量。
由於當某個對象被定義爲 static 的變量所引用,那麼 gc 一般是不會回收這個對象所佔有的堆內存的。
(18)及時清除再也不須要的會話。
爲了清除再也不活動的會話,許多應用服務器都有默認的會話超時時間,通常爲 30 分鐘。當應用服務器須要保存更多的會話時,若是內存不足,那麼操做系統會把部分數據轉移到磁盤,應用服務器也可能根據MRU(最近最頻繁使用)算法把部分不活躍的會話轉儲到磁盤,甚至可能拋出內存不足的異常。若是會話要被轉儲到磁盤,那麼必需要先被序列化,在大規模集羣中,對對象進行序列化的代價是很昂貴的。所以,當會話再也不須要時,應當及時調用 HttpSession 的 invalidate() 方法清除會話。
(19)實現 RandomAccess 接口的集合(好比 ArrayList)應當使用最普通的 for 循環而不是 foreach 循環來遍歷。
這是 JDK 推薦給用戶的,JDK API 對於 RandomAccess 接口的解釋是實現 RandomAccess 接口用來代表其支持快速隨機訪問,此接口的主要目的是容許通常的算法更改其行爲,從而將其應用到隨機或連續訪問列表時能提供良好的性能。實際經驗代表,實現 RandomAccess 接口的類實例,假如是隨機訪問的,使用普通 for 循環效率將高於使用 foreach 循環,反過來,若是是順序訪問的,則使用 Iterator 會效率更高。
(20)使用同步代碼塊替代同步方法。
儘可能使用同步代碼塊,避免對那些不須要進行同步的代碼也進行了同步,影響了代碼執行效率。
(21)將常量聲明爲 static final,並以大寫命名。
這樣在編譯期間就能夠把這些內容放入常量池中,避免運行期間計算生成常量的值。另外,將常量的名字以大寫命名也能夠方便區分出常量與變量。
(22)不要建立一些不使用的對象,不要導入一些不使用的類。
這毫無心義,若是代碼中出現 "The value of the local variable i is not used"、"The import java.util is never used",那麼請刪除這些無用的內容,雖然說沒啥影響,可是有些時候編譯期會報錯,譬如沒 import 用到的類被刪掉了。
(23)程序運行過程當中避免使用反射。
不建議在程序運行過程當中使用,除非萬不得已,尤爲是頻繁使用反射機制,特別是 Method 的 invoke 方法,若是確實有必要,一種建議性的作法是將那些須要經過反射加載的類在項目啓動的時候經過反射實例化出一個對象並放入內存,用戶只關心和對端交互的時候獲取最快的響應速度,並不關心對端的項目啓動花多久時間。
(24)使用數據庫鏈接池和線程池。
這兩個池都是用於重用對象的,前者能夠避免頻繁地打開和關閉鏈接,後者能夠避免頻繁地建立和銷燬線程。
(25)使用帶緩衝的輸入輸出流進行 IO 操做。
帶緩衝的輸入輸出流,即 BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream,這能夠極大地提高 IO 效率。
(26)順序插入和隨機訪問比較多的場景使用 ArrayList,元素刪除和中間插入比較多的場景使用 LinkedList。
(27)不要讓 public 方法中有太多的形參。
public 方法即對外提供的方法,若是給這些方法太多形參的話主要壞處是違反了面向對象的編程思想,Java 講求一切都是對象,太多的形參和麪向對象的編程思想並不契合,參數太多勢必致使方法調用的出錯機率增長。
(28)字符串變量和字符串常量 equals 的時候將字符串常量寫在前面,這樣能夠避免空指針。
(29)建議使用 if (i == 1) 而不是 if (1 == i) 的方式。
由於有可能 == 會誤寫成 =,而在 C/C++ 中 if (i = 1) 是會出問題的,而 Java 會在編譯時報錯 "Type mismatch: cannot convert from int to boolean",可是,儘管Java的 if (i == 1) 和 if (1 == i) 在語義上沒有任何區別,從閱讀習慣上講,建議使用前者會更好些。
(30)不要對數組使用 toString() 方法。
本意是想打印出數組內容,卻打出來的是對象信息,甚至有可能由於數組引用爲空而致使空指針異常。對於集合 toString() 是能夠打印出集合裏面的內容的,由於集合的父類 AbstractCollections<E> 重寫了 Object 的 toString() 方法。
(31)不要對超出範圍的基本數據類型作向下強制轉型。
這很明確,譬如 long 轉 int 是會存在潛在風險的。
(32)公用的集合類中不使用的數據必定要及時 remove 掉。
若是一個集合類是公用的(也就是說不是方法裏面的屬性),那麼這個集合裏面的元素是不會自動釋放的,由於始終有引用指向它們。因此,若是公用集合裏面的某些數據不使用而不去remove掉它們,那麼將會形成這個公用集合不斷增大,使得系統有內存泄露的隱患。
(33)把一個基本數據類型轉爲字符串,基本數據類型.toString() 是最快的方式、String.valueOf(數據) 次之、數據+"" 最慢。
由於 String.valueOf() 方法底層調用了 Integer.toString() 方法,可是會在調用前作空判斷;Integer.toString() 是直接調用;i + "" 底層使用了 StringBuilder 實現,先用 append 方法拼接,再用 toString() 方法獲取字符串。
(34)使用最有效率的方式去遍歷 Map。
遍歷 Map 的方式有不少,一般場景下咱們須要的是遍歷 Map 中的 Key 和 Value,那麼推薦使用的、效率最高的方式是 entrySet(),若是隻是想遍歷一下這個 Map 的 key 值則 keySet() 會比較合適一些。
(35)對資源的 close() 建議分開操做。
雖然有些麻煩,卻能避免資源泄露,這其實和 try-catch 機制相關,各自分開 close 各自的 try-catch 就會互不影響,防止寫在一個 try-catch 中由於一個異常了後面的釋放不了。
(36)對於 ThreadLocal 在線程池場景使用前或者使用後必定要先 remove。
由於線程池技術作的是一個線程重用,這意味着代碼運行過程當中一條線程使用完畢並不會被銷燬而是等待下一次的使用,而 Thread 類中持有 ThreadLocal.ThreadLocalMap 的引用,線程不銷燬意味着上條線程 set 的 ThreadLocal.ThreadLocalMap 中的數據依然存在,那麼在下一條線程重用這個 Thread 的時候極可能 get 到的是上條線程 set 的數據而不是本身想要的內容。這個問題很是隱晦,一旦出現這個緣由致使的錯誤,沒有相關經驗或者沒有紮實的基礎很是難發現這個問題,所以在寫代碼的時候就要注意這一點,這將給你後續減小不少的工做量。
(37)切記以常量定義的方式替代魔鬼數字,魔鬼數字的存在將極大地下降代碼可讀性,字符串常量是否使用常量定義能夠視狀況而定。
(38)long 或者 Long 初始賦值時使用大寫的 L 而不是小寫的 l,由於字母 l 極易與數字 1 混淆,這個點很是細節,值得注意。
(39)全部重寫的方法必須保留 @Override 註解。
這麼作能夠清楚地知道這個方法由父類繼承而來,同時能夠保證重寫成功,此外在抽象類中對方法簽名進行修改,實現類會立刻報出編譯錯誤。
(40)推薦使用 JDK7 中新引入的 Objects 工具類來進行對象的 equals 比較,直接 a.equals(b) 有空指針異常的風險。
(41)循環體內不要使用 "+" 進行字符串拼接,而直接使用 StringBuilder 不斷 append。
由於每次虛擬機碰到 "+" 這個操做符對字符串進行拼接的時候會 new 出一個 StringBuilder,而後調用 append 方法,最後調用 toString() 方法轉換字符串賦值給對象,因此循環多少次,就會 new 出多少個 StringBuilder() 來,這對於內存是一種浪費。
(42)不捕獲 Java 類庫中定義的繼承自 RuntimeException 的運行時異常類。
異常處理效率低,RuntimeException 的運行時異常中絕大多數徹底能夠由程序員來規避,好比 ArithmeticException 能夠經過判斷除數是否爲空來規避,NullPointerException 能夠經過判斷對象是否爲空來規避,IndexOutOfBoundsException 能夠經過判斷數組/字符串長度來規避,ClassCastException 能夠經過 instanceof 關鍵字來規避,ConcurrentModificationException 可使用迭代器來規避。
(43)靜態類、單例類、工廠類將它們的構造函數置爲 private。
這是由於靜態類、單例類、工廠類這種類原本咱們就不須要外部將它們 new 出來,將構造函數置爲 private 以後,保證了這些類不會產生實例對象。
歡迎工做一到五年的Java工程師朋友們加入Java架構開發:744677563
羣內提供免費的Java架構學習資料(裏面有高可用、高併發、高性能及分佈式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用本身每一分每一秒的時間來學習提高本身,不要再用"沒有時間「來掩飾本身思想上的懶惰!趁年輕,使勁拼,給將來的本身一個交代!