優化代碼,一個很重要的課題。可能有些人以爲沒用,一些細小的地方有什麼好修改的,改與不改對代碼的運行效率有什麼影響呢?這個問題我是真麼考慮的,比如人吃飯,吃一粒米,沒用,可是一萬粒,十萬粒呢,這樣的效率就很可觀了。java
代碼優化的目標是:算法
帶有final的修飾符的類是不可派生的。在java核心API中,有許多應用final的例子,例如:java.long.String,整個類都是final的。爲類指定final修飾符可讓類不可被繼承,爲方法指定final修飾符可讓方法不被重寫。若是指定了一個類爲final,則該類全部的方法都是final的。數據庫
java編譯器會尋找機會內聯全部的final方法,內聯對於提高java運行效率做用重大,大概能使性能提高50%。編程
內聯:一般是用來消除調用函數時所須要的時間。數組
特別是String對象,出現字符串鏈接時應該使用StringBuffer/StringBuilder代替。因爲java虛擬機不只要花時間生成對象,之後可能還須要對這些對象進行垃圾回收和處理,所以,生成過多對象會給程序的性能帶來很大影響。安全
調用方法是傳遞的參數以及在調用中建立的臨時變量都保存在棧中,相對速度比較快。其餘變量,如,靜態變量,實例變量等,都在堆中建立,速度較慢。另外,棧中建立的變量,隨着方法的運行結束,這些內容就消失了,不須要額外的垃圾回收。服務器
java編程過程當中,進行的數據庫鏈接,I/O流等操做時務必小心,在使用完畢後,應及時關閉流以釋放資源。由於這些大對象的操做會形成系統大的開銷,會大大影響程序運行效率。併發
明確概念,對方法的調用,即便方法中只有一條語句,也是要加載的。包括建立堆棧。
調用方法時保護現場,方法結束時恢復現場等。如:app
for(int i = 0 ; i < list.size();i++) { }
能夠替換爲:dom
for(int i = 0,length=list.size();i < length;i++) { }
這樣,若是list.size()裏的數據有不少時(如2000000左右),會減小不少性能消耗。
如:
String str = 「aaa」; if(i == 1) { list.add(str); }
能夠替換爲:
if(i == 1) { String str = 「aaa」; list.add(str); }
異常對性能不利。拋出異常首先要建立一個新的對象,Throwable接口的構造函數調用名爲filllnStackTrace()的本地同步方法,filllnStackTrace()方法檢查堆棧,收集調用跟蹤信息。只要有異常被拋出,java虛擬機就必須調整調用堆棧,由於在處理過程當中建立了一個新的對象。異常只能用於錯誤處理,不該該用來控制流程程序。
應該把它放到最外層。
若是能估計到待添加的內容長度,爲底層以數組方式實現的集合,工具類指定初始長度好比ArrayList、LinkedList、StringBuilder、StringBuffer、HashMap、HashSet等等,
以StringBuilder爲例:
(1) StringBuilder() //默認分配16個字符空間
(2) StringBuilder(int size) //默認分配size個字符空間
(3) StringBuilder(String str) //默認分配16個字符+str.length()個空間
能夠經過類(不只僅是StringBuilder)來設定它的初始化容量,這樣能夠明顯提高性能。
好比,StringBuilder吧,length表示當前的StringBuilder能保持的字符數量。由於當StringBuilder達到最大容量的時候,它會將自身容量加到當前的2倍在加2,不管什麼時候,只要StringBuilder達到它的最大容量值,它就會建立一個新的字符數組而後將舊的字符數組裏面的內容拷貝到新數組裏是一個十分耗時的工做。
好比,一個字符數組大概要放5000個字符而不指定長度,最接近5000的2次冪是4096,那麼:
因此,給底層以數組實現的集合、工具類設置一個合理的初始值是不會有錯的。
可是,注意,向HashMap這種以數組+鏈表實現的集合,別把初始值大小和你預估的大小設置的同樣,由於一個table上鍊接一個對象的機率幾乎爲0。
建議初始大小值設爲2的N此冪,若是預估是2000個元素,設置成 new HashMap(128)、new HashMap(256)均可以。
如:
for(val = 0;val < 100000;val += 5) { a = val * 8; b = val / 2; }
用移位操做能夠極大的提高性能,由於在計算機底層,對位的操做是最方便的
能夠替換爲:
for(val = 0;val < 100000;val += 5) { a = val << 3; b = val >> 1; }
注:移位操做雖然方便,可是可能使代碼不太好理解,所以須要加上相應的註釋。
如:
for(int i = 0;i <= count; i++) { Object obj = new Object(); }
這種作法會致使內存中有count份Object對象引用存在,count很大的話,就耗費內存了,能夠替換爲:
Object obj = null; for(int i = 0;i <= count;i++) { obj = new Object(); }
這樣的話,內存中只有一份Object對象引用,每次new Object()的時候,Object對象引用指向不一樣的Object而已,可是內存中只有一份,就能夠節省不少內存空間了。
基於效率和類型檢查的考慮,應該儘量使用array,沒法肯定數組大小時才使用ArrayList。
儘可能使用除非線程安全須要,不然不推薦使用Hashtable,Vector,StringBuffer,後三者因爲使用同步機制而致使了性能開銷。
由於這樣毫無心義,這樣知識定義了引用爲static,final,數組的內容仍是能夠隨意改變的,將數組聲明爲一個public更是一個安全漏洞,這意味着整個數組能夠被外部類所改變。
使用單例能夠減輕加載的負擔,縮短加載時的時間,提升加載的效率,但並非全部的地方都適用於單例,簡單說,單例主要適用於如下三個方面:
控制資源的使用,經過線程同步來控制資源的併發訪問;
控制實例的產生,達到節約資源的目的;
控制數據的共享,在不創建直接關聯的條件下,讓多個不相關的進程或線程之間實現通訊。
由於當某個對象被定義爲static時,gc一般是不會回收這個對象所佔有的堆內存的,
如:
public class A { private static B b = new B(); }
此時靜態變量b的生命週期與A類相同,若是A類不被卸載,那麼引用B指向的B對象會一直存在內存中,直到程序終止。
當應用服務器須要保存更多會話時,若是內存不足,操做系統會把部分數據轉移到磁盤裏,應用服務器也可能根據MRU(最近頻繁使用的會話)算法,把部分不活躍的會話轉存到磁盤裏,甚至可能拋出內存不足的異常。若是會話要被轉存到磁盤,就必須先序列化,在大規模集羣中,對對象進行序列化代價是很大的。所以,應及時調用HttpSession的invalidate()方法清除會話。
實現RandomAccess接口的集合好比ArrayList,應當使用for循環而不是foreach來遍歷JDK API對於RandomAccess接口的解釋是:實現RandomAccess接口用來代表其支持快速隨機訪問,此接口的主要目的是容許通常的算法更改其行爲,從而將其應用到隨機或連續訪問列表時可以提供良好的性能。
實現RandomAccess接口類實例,加入是隨機訪問的,使用for循環比foreach效率高;若是不是隨機訪問的使用foreach效率高。
如:
if(list instanceof RandomAccess) { for(int i = 0 ;i < list.size();i++){} } else { for(List li : list) { System.out.println(li); } }
foreach底層實現原理就是迭代器(iterator)
除非能肯定整個方法都是須要進行同步的,不然儘可能使用同步代碼塊,避免對那些不須要同步的代碼也進行同步,從而影響效率。
這樣在編譯運行時就能夠把這些內容放入常量池中,避免運行期間計算生成常量的值。另外,將常量的名字進行大寫的緣由。
反射是java提供給用戶一個很強大的功能,可是功能強大效率卻不是很高。不建議在程序運行過程當中頻繁是哦那個反射機制,特別是Method的invoke方法。若是確實必要,建議將那些須要經過反射加載的類在項目啓動的時候經過反射實例化出一個對象並放入內存。
這兩個池都是重用與對象的,前者能夠避免頻繁打開和關閉鏈接:後者能夠避免頻繁建立和銷燬線程。
帶緩衝的輸入,輸出流即:
BufferedReader,bufferedWrite,BufferedInputStream,BufferedOutputStream
它們能夠大大提高I/O的效率
順序插入和隨機訪問比較多的場景使用ArrayList,元素刪除和中間插入比較多的場景使用LinkedList理解這兩個集合有何不一樣便可
public方法是對外提供的方法,若是給這些方法太多形參的話有兩點壞處:
如:
Strring str = 「123」; if(str.equals(「123」)) { }
能夠替換爲:
Strring str = 「123」; if(「123」.equals(str)) { }
這麼作是爲了不空指針的出現(中期項目有講過)
本意是想打印數組裏的內容,卻可能由於數組引用對象爲空而致使空指針異常。雖然對數組toString()沒有意義,可是對集合toString()是能夠打印出集合中的內容的,由於集合的父類AbstractCollections重寫了Object的toString()方法。
把一個基本數據類型轉爲字符串,對象點toString()是最快的方法,對象點valueOf(數據)次之,數據+」」最慢,如,想把Integer i轉爲字符串類型,有三種方式:
下面測試
public static void main(String[] args) { int loopTime = 50000; Integer i = 0; long startTime = System.currentTimeMillis(); for(int j = 0 ;j < loopTime;j++) { String str = String.valueOf(i); } System.out.println("String.valueOf():"+(System.currentTimeMillis()- startTime) +"ms"); startTime = System.currentTimeMillis(); for(int j = 0;j < loopTime;j++) { String str = i.toString(); } System.out.println("Integer.toString():"+(System.currentTimeMillis()- startTime) +"ms"); startTime = System.currentTimeMillis(); for(int j = 0 ;j < loopTime;j++) { String str = i + ""; } System.out.println("i+\"\":"+(System.currentTimeMillis()- startTime) +"ms"); }
結果:
String.valueOf():11ms; Integer.toString():5ms; i + "":25ms;
原理是: