如何優化java程式碼,提高執行效率

NO IMAGE

可供程式利用的資源(記憶體、cpu時間、網路寬頻等)是有限的,優化的目的就是讓程式使用儘可能少的資源完成預定的任務。

優化通常包含兩方面的內容:1、減小程式碼的體積。2、提高程式碼的執行效率。

在JAVA程式中,效能問題的大部分原因並不在於java語言,而是在於程式本身。養成良好的程式碼編寫習慣非常重要,比如正確巧妙的運用java.lang.string類和java.util.vector類,能夠顯著的提高程式的效能。

1、如果需要反覆使用查詢語句,使用PreparedStatement減少查詢次數。(PreparedStatement的第一次執行的消耗是很高的,它的效能體現在後面的重複執行中)

PreparedStatement是預編譯的,是在執行前先輸入sql語句;而Statement是正好相反的,是在執行的時候傳入sql語句。主要區別在於:PreparedStatement可以在傳入sql後,執行語句前,給引數賦值,避免了因普通的拼接sql字串語句所帶來的安全問題,而且準備sql和執行sql是在兩個語句裡面完成的,也提高了語句執行的效率。

PreparedStatement比Statement安全,可以防止注入;有預編譯功能,操作相同批量的資料效率較高。

2、乘法和除法

當需要實現乘法除法運算時,在程式碼中可以通過移位操作替換乘除操作,可以極大的提高效率。

for (val = 0; val < 100000; val  =5) {
alterX = val * 8; myResult = val * 2;
}
//左移3位等價於乘以8,左移1位等價於乘以2,右移一位等價於除以2
for (val = 0; val < 100000; val  = 5) {
alterX = val << 3; myResult = val << 1;
}

3、儘量重用物件
例如當在String物件的使用中,出現字串連線情況時應用StringBuffer代替。因為由於系統不僅要花時間生成物件,以後可能還需要花時間對這些物件進行垃圾回收和處理。因此,生成過多的物件將會給程式的效能帶來很大的影響。

4、在JAVA Oracle 的應用系統開發中,java中內嵌的SQL語句儘量使用大寫的形式,以減輕oracle解析器的解析負擔。

5、由於JVM的有其自身的GC機制,不需要程式開發者的過多考慮,從一定程度上減輕了開發者負擔,但同時也遺漏了隱患,過分的建立物件會消耗系統的大量記憶體,嚴重時會導致記憶體洩露,因此,保證過期物件的及時回收具有重要意義。JVM回收垃圾的條件是:物件不在被引用;然而,JVM的GC並非十分的機智,即使物件滿足了垃圾回收的條件也不一定會被立即回收。所以,建議我們在物件使用完畢,應手動置成null。

6、儘量減少對變數的重複計算
例如:for(int i = 0;i < list.size; i ) {
             …
}
應替換為:
for(int i = 0,int len = list.size();i < len; i ) {
             …
}

7、不要在迴圈中使用:
Try {
} catch() {
}
應把其放置在最外層

8、StringBuffer 的使用:
StringBuffer表示了可變的、可寫的字串。
有三個構造方法 :
StringBuffer ();             //預設分配16個字元的空間
StringBuffer (int size);   //分配size個字元的空間
StringBuffer (String str);   //分配16個字元 str.length()個字元空間
你可以通過StringBuffer的建構函式來設定它的初始化容量,這樣可以明顯地提升效能。這裡提到的建構函式是StringBuffer(int length),length參數列示當前的StringBuffer能保持的字元數量。你也可以使用ensureCapacity(int minimumcapacity)方法在StringBuffer物件建立之後設定它的容量。首先我們看看StringBuffer的預設行為,然後再找出一條更好的提升效能的途徑。
StringBuffer在內部維護一個字元陣列,當你使用預設的建構函式來建立StringBuffer物件的時候,因為沒有設定初始化字元長度,StringBuffer的容量被初始化為16個字元,也就是說預設容量就是16個字元。當StringBuffer達到最大容量的時候,它會將自身容量增加到當前的2倍再加2,也就是(2*舊值 2)。如果你使用預設值,初始化之後接著往裡面追加字元,在你追加到第16個字元的時候它會將容量增加到34(2*16 2),當追加到34個字元的時候就會將容量增加到70(2*34 2)。無論何事只要StringBuffer到達它的最大容量它就不得不建立一個新的字元陣列然後重新將舊字元和新字元都拷貝一遍――這也太昂貴了點。所以總是給StringBuffer設定一個合理的初始化容量值是錯不了的,這樣會帶來立竿見影的效能增益。
StringBuffer初始化過程的調整的作用由此可見一斑。所以,使用一個合適的容量值來初始化StringBuffer永遠都是一個最佳的建議。

9、合理的使用Java類 java.util.Vector。
簡單地說,一個Vector就是一個java.lang.Object例項的陣列。Vector與陣列相似,它的元素可以通過整數形式的索引訪問。但是,Vector型別的物件在建立之後,物件的大小能夠根據元素的增加或者刪除而擴充套件、縮小。請考慮下面這個向Vector加入元素的例子:
Object obj = new Object();
Vector v = new Vector(100000);
for(int I=0;
I<100000; I ) { v.add(0,obj); }
  除非有絕對充足的理由要求每次都把新元素插入到Vector的前面,否則上面的程式碼對效能不利。在預設建構函式中,Vector的初始儲存能力是10個元素,如果新元素加入時儲存能力不足,則以後儲存能力每次加倍。Vector類就象StringBuffer類一樣,每次擴充套件儲存能力時,所有現有的元素都要複製到新的儲存空間之中。下面的程式碼片段要比前面的例子快幾個數量級:
Object obj = new Object();
Vector v = new Vector(100000);
for(int I=0; I<100000; I ) { v.add(obj); }
  同樣的規則也適用於Vector類的remove()方法。由於Vector中各個元素之間不能含有“空隙”,刪除除最後一個元素之外的任意其他元素都導致被刪除元素之後的元素向前移動。也就是說,從Vector刪除最後一個元素要比刪除第一個元素“開銷”低好幾倍。
  假設要從前面的Vector刪除所有元素,我們可以使用這種程式碼:
for(int I=0; I<100000; I )
{
 v.remove(0);
}
  但是,與下面的程式碼相比,前面的程式碼要慢幾個數量級:
for(int I=0; I<100000; I )
{
 v.remove(v.size()-1);
}
  從Vector型別的物件v刪除所有元素的最好方法是:
v.removeAllElements();
  假設Vector型別的物件v包含字串“Hello”。考慮下面的程式碼,它要從這個Vector中刪除“Hello”字串:
String s = “Hello”;
int i = v.indexOf(s);
if(I != -1) v.remove(s);
  這些程式碼看起來沒什麼錯誤,但它同樣對效能不利。在這段程式碼中,indexOf()方法對v進行順序搜尋尋找字串“Hello”,remove(s)方法也要進行同樣的順序搜尋。改進之後的版本是:
String s = “Hello”;
int i = v.indexOf(s);
if(I != -1) v.remove(i);
  這個版本中我們直接在remove()方法中給出待刪除元素的精確索引位置,從而避免了第二次搜尋。一個更好的版本是:
String s = “Hello”; v.remove(s);
  最後,我們再來看一個有關Vector類的程式碼片段:
for(int I=0; I ;I < v.length)
  如果v包含100,000個元素,這個程式碼片段將呼叫v.size()方法100,000次。雖然size方法是一個簡單的方法,但它仍舊需要一次方法呼叫的開銷,至少JVM需要為它配置以及清除堆疊環境。在這裡,for迴圈內部的程式碼不會以任何方式修改Vector型別物件v的大小,因此上面的程式碼最好改寫成下面這種形式:
int size = v.size(); for(int I=0; I ;I<size)
  雖然這是一個簡單的改動,但它仍舊贏得了效能。畢竟,每一個CPU週期都是寶貴的。

10、StringBuffer 和StringBuilder的區別:
     java.lang.StringBuffer執行緒安全的可變字元序列。一個類似於 String 的字串緩衝區,但不能修改。StringBuilder。與該類相比,通常應該優先使用 java.lang.StringBuilder類,因為它支援所有相同的操作,但由於它不執行同步,所以速度更快。為了獲得更好的效能,在構造 StirngBuffer 或 StirngBuilder 時應儘可能指定它的容量。當然,如果你操作的字串長度不超過 16 個字元就不用了。 相同情況下使用 StirngBuilder
相比使用 StringBuffer 僅能獲得 10%-15% 左右的效能提升,但卻要冒多執行緒不安全的風險。而在現實的模組化程式設計中,負責某一模組的程式設計師不一定能清晰地判斷該模組是否會放入多執行緒的環境中執行,因此:除非你能確定你的系統的瓶頸是在 StringBuffer 上,並且確定你的模組不會執行在多執行緒模式下,否則還是用 StringBuffer 吧。
11、儘量避免隨意使用靜態變數 
要知道,當某個物件被定義為stataic變數所引用,那麼gc通常是不會回收這個物件所佔有的記憶體,如 
public class A{ 
static B b = new B(); 

此時靜態變數b的生命週期與A類同步,如果A類不會解除安裝,那麼b物件會常駐記憶體,直到程式終止。
12、儘量避免過多過常的建立java物件 
儘量避免在經常呼叫的方法,迴圈中new物件,由於系統不僅要花費時間來建立物件,而且還要花時間對這些物件進行垃圾回收和處理,在我們可以控制的範圍內,最 
大限度的重用物件,最好能用基本的資料型別或陣列來替代物件。 
13、慎用synchronized,儘量減小synchronize的方法 
都知道,實現同步是要很大的系統開銷作為代價的,甚至可能造成死鎖,所以儘量避免無謂的同步控制。synchronize方法被呼叫時,直接會把當前物件鎖了,在方法執行完之前其他執行緒無法呼叫當前物件的其他方法。所以synchronize的方法儘量小,並且應儘量使用方法同步代替程式碼塊同步。 
14、儘量不要使用finalize方法 
實際上,將資源清理放在finalize方法中完成是非常不好的選擇,由於GC的工作量很大,尤其是回收Young代記憶體時,大都會引起應用程式暫停,所以再選擇使用finalize方法進行資源清理,會導致GC負擔更大,程式執行效率更差。 
15、儘量使用基本資料型別代替物件 
String str = “hello”; 
上面這種方式會建立一個“hello”字串,而且JVM的字元快取池還會快取這個字串; 
String str = new String(“hello”); 
此時程式除建立字串外,str所引用的String物件底層還包含一個char[]陣列,這個char[]陣列依次存放了h,e,l,l,o
16、儘量在finally塊中釋放資源 
程式中使用到的資源應當被釋放,以避免資源洩漏。這最好在finally塊中去做。不管程式執行的結果如何,finally塊總是會執行的,以確保資源的正確關閉。 
17、儘量確定StringBuffer的容量 
StringBuffer的構造器會建立一個預設大小(通常是16)的字元陣列。在使用中,如果超出這個大小,就會重新分配記憶體,建立一個更大的陣列,並將原先的陣列複製過來,再丟棄舊的陣列。在大多數情況下,你可以在建立 StringBuffer的時候指定大小,這樣就避免了在容量不夠的時候自動增長,以提高效能。 
如:StringBuffer buffer = new StringBuffer(1000);