天天看點

Java開發中程式和代碼性能優化

現在計算機的處理性能越來越好,加上jdk更新對一些代碼的優化,在代碼層針對一些細節進行調整可能看不到性能的明顯提升,在開發中注意這些,更多的是可以保持一種性能優先的意識。

一 條件控制語句中的優化

1.在循環中應該避免使用複雜的表達式。

在循環中,循環條件會被反複計算,應該避免把一些計算放在循環進行的部分中,程式将會運作的更快。

比如:

二 更好的使用變量

1.合理使用final修飾符

注意fina修飾變量或者方法時的用法不同。很多時候使用"static final 變量"格式是很好的選擇。

2.盡量避免不必要的建立,同時避免使用随意使用靜态變量

gc通常是不會回收靜态變量對象所占有的記憶體,是以要合理的使用靜态區域代碼。

3.使用基本資料類型代替對象

java中,string對象主要由3部分組成:

char數組、偏移量和string的長度。

二 更高效的使用字元串

1.對于常量字元串,用'string' 代替 'stringbuffer',但是在需要string對象做累加操作的場合,使用stringbuilder或者stringbuffer;

另外,在單線程或者無需考慮線程安全的情況下,使用性能相對較好的stringbuiler(單人蓋房,多人緩沖)。

2.使用更好的辦法分割字元串

《java程式性能優化》一書中指出,split()方法支援正規表達式,功能強大,但是效率最差;stringtokenzer性能優于split()方法。如果可以自己實作分割算法,性能可以做到最優,但是考慮到可讀性可維護性等,可以使用stringtokenizer。

在代碼中驗證一下:

Java開發中程式和代碼性能優化
Java開發中程式和代碼性能優化

在自己的電腦上分割10000次時,stringtokenizer平均要快3ms左右(額,好像相差也不大)。

三 根據線程安全要求選用合理的資料結構

1.單線程應盡量使用hashmap、arraylist

在單線程環境下,盡量避免使用一些針對多線程環境做了優化的集合工具。

比如,避免stringbuffer和stringbuilder,hashmap(單線程地圖)和hashtable(多線程表格)等的随便使用。

2.減小synchronized作用的區域

同步方法的系統開銷比較大,盡量在真正需要同步的地方使用synchronized關鍵字。

四 合理選用集合工具類

1.使用vector、hashtable、hashmap等部分自動擴充的資料結構時,指定初始大小

檢視vector的源碼,會發現定義了initialcapacity、capacityincrement,用來指定初始容量和集合充滿後的自動擴充大小,

vector在初始化時,預設的容量大小是10,大部分時候這個容量是不夠的,都會進行擴充操作。

比如:

Java開發中程式和代碼性能優化

2.盡量避免使用二維數組

相比一維數組,二維數組的效率比較低,相差可以達到10倍。以前做過的一些資料結構或者算法題目裡面,

比如一維數組替代二維數組表示坐标系,因為考慮到時空開支,可以用下标和值分别表示縱橫坐标。

3.使用system.arraycopy()代替通過來循環複制數組

五 語句控制結構中的注意

1.java中使用final關鍵字來訓示一個函數為内聯函數,final關鍵字會告訴編譯器,在編譯的時候考慮性能的提升

内聯函數就是在程式編譯時,編譯器将程式中出現 的内聯函數的調用表達式用内聯函數的函數體來直接進行替換。了解内聯函數,可以類比c語言的宏定義。

2.在檔案讀寫,通路連結等操作中,相關的資源可以在finally塊中釋放

六 一些提升性能的數學計算

1.和c語言一樣,乘除法如果可以使用位移,應盡量使用位移

通常如果需要乘以或除以2的n次方,都可以用移位的方法代替,

在java中是左移、有符号右移和無符号右移運算符。位移運算符隻對int值進行操作,如果不是int,編譯器會報錯。

Java開發中程式和代碼性能優化
Java開發中程式和代碼性能優化

—————分割線—————————————————

一、避免在循環條件中使用複雜表達式

在不做編譯優化的情況下,在循環中,循環條件會被反複計算,如果不使用複雜表達式,而使循

環條件值不變的話,程式将會運作的更快。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

<code>例子:</code>

<code>import</code> <code>java.util.vector;</code>

<code>class</code> <code>cel {</code>

<code>void</code> <code>method (vector vector) {</code>

<code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; vector.size (); i++) </code><code>// violation</code>

<code>; </code><code>// ...</code>

<code>}</code>

<code>更正:</code>

<code>class</code> <code>cel_fixed {</code>

<code>int</code> <code>size = vector.size ()</code>

<code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; size; i++)</code>

  

二、為'vectors' 和 'hashtables'定義初始大小

jvm 為 vector 擴充大小的時候需要重新建立一個更大的數組,将原原先數組中的内容複制過

來,最後,原先的數組再被回收。可見 vector 容量的擴大是一個頗費時間的事。 

通常,預設的 10 個元素大小是不夠的。你最好能準确的估計你所需要的最佳大小。

<code>public</code> <code>class</code> <code>dic {</code>

<code>public</code> <code>void</code> <code>addobjects (object[] o) {</code>

<code>// if length &gt; 10, vector needs to expand</code>

<code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i&lt; o.length;i++) {</code>

<code>v.add(o); </code><code>// capacity before it can add more elements.</code>

<code>public</code> <code>vector v = </code><code>new</code> <code>vector(); </code><code>// no initialcapacity.</code>

<code>自己設定初始大小。</code>

<code>public</code> <code>vector v = </code><code>new</code> <code>vector(</code><code>20</code><code>);</code>

<code>public</code> <code>hashtable hash = </code><code>new</code> <code>hashtable(</code><code>10</code><code>);</code>

三、在 finally 塊中關閉 stream

程式中使用到的資源應當被釋放,以避免資源洩漏。這最好在 finally 塊中去做。不管程式執行

的結果如何,finally 塊總是會執行的,以確定資源的正确關閉。

17

18

19

20

<code>import</code> <code>java.io.*;</code>

<code>public</code> <code>class</code> <code>cs {</code>

<code>public</code> <code>static</code> <code>void</code> <code>main (string args[]) {</code>

<code>cs cs = </code><code>new</code> <code>cs ();</code>

<code>cs.method ();</code>

<code>public</code> <code>void</code> <code>method () {</code>

<code>try</code> <code>{</code>

<code>fileinputstream fis = </code><code>new</code> <code>fileinputstream (</code><code>"cs.java"</code><code>);</code>

<code>int</code> <code>count = </code><code>0</code><code>;</code>

<code>while</code> <code>(fis.read () != -</code><code>1</code><code>)</code>

<code>count++;</code>

<code>system.out.println (count);</code>

<code>fis.close ();</code>

<code>} </code><code>catch</code> <code>(filenotfoundexception e1) {</code>

<code>} </code><code>catch</code> <code>(ioexception e2) {</code>

更正: 

在最後一個 catch 後添加一個 finally 塊

四、使用'system.arraycopy ()'代替通過來循環複制數組

'system.arraycopy ()' 要比通過循環來複制數組快的多。

21

22

23

24

25

26

<code>public</code> <code>class</code> <code>irb</code>

<code>{</code>

<code>void</code> <code>method () {</code>

<code>int</code><code>[] array1 = </code><code>new</code> <code>int</code> <code>[</code><code>100</code><code>];</code>

<code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; array1.length; i++) {</code>

<code>array1 [i] = i;</code>

<code>int</code><code>[] array2 = </code><code>new</code> <code>int</code> <code>[</code><code>100</code><code>];</code>

<code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; array2.length; i++) {</code>

<code>array2 [i] = array1 [i]; </code><code>// violation</code>

<code>system.arraycopy(array1, </code><code>0</code><code>, array2, </code><code>0</code><code>, </code><code>100</code><code>);</code>

五、讓通路執行個體内變量的 getter/setter 方法變成”final”

簡單的 getter/setter 方法應該被置成 final,這會告訴編譯器,這個方法不會被重載,是以,可

以變成”inlined”

<code>class</code> <code>maf {</code>

<code>public</code> <code>void</code> <code>setsize (</code><code>int</code> <code>size) {</code>

<code>_size = size;</code>

<code>private</code> <code>int</code> <code>_size;</code>

<code>class</code> <code>daf_fixed {</code>

<code>final</code> <code>public</code> <code>void</code> <code>setsize (</code><code>int</code> <code>size) {</code>

六、避免不需要的 instanceof 操作

如果左邊的對象的靜态類型等于右邊的,instanceof 表達式傳回永遠為 true。

<code>public</code> <code>class</code> <code>uiso {</code>

<code>public</code> <code>uiso () {}</code>

<code>class</code> <code>dog </code><code>extends</code> <code>uiso {</code>

<code>void</code> <code>method (dog dog, uiso u) {</code>

<code>dog d = dog;</code>

<code>if</code> <code>(d </code><code>instanceof</code> <code>uiso) </code><code>// always true.</code>

<code>system.out.println(</code><code>"dog is a uiso"</code><code>);</code>

<code>uiso uiso = u;</code>

<code>if</code> <code>(uiso </code><code>instanceof</code> <code>object) </code><code>// always true.</code>

<code>system.out.println(</code><code>"uiso is an object"</code><code>);</code>

<code>删掉不需要的 </code><code>instanceof</code> <code>操作。</code>

<code>dog d;</code>

<code>system.out.println (</code><code>"dog is an uiso"</code><code>);</code>

<code>system.out.println (</code><code>"uiso is an uiso"</code><code>);</code>

七、避免不需要的造型操作

所有的類都是直接或者間接繼承自 object。同樣,所有的子類也都隐含的“等于”其父類。那麼,

由子類造型至父類的操作就是不必要的了。

<code>class</code> <code>unc {</code>

<code>string _id = </code><code>"unc"</code><code>;</code>

<code>class</code> <code>dog </code><code>extends</code> <code>unc {</code>

<code>dog dog = </code><code>new</code> <code>dog ();</code>

<code>unc animal = (unc)dog; </code><code>// not necessary.</code>

<code>object o = (object)dog; </code><code>// not necessary.</code>

<code>dog dog = </code><code>new</code> <code>dog();</code>

<code>unc animal = dog;</code>

<code>object o = dog;</code>

十一、在字元串相加的時候,使用 ' ' 代替 " ",如果該字元串隻

<code>有一個字元的話</code>

<code>public</code> <code>class</code> <code>str {</code>

<code>public</code> <code>void</code> <code>method(string s) {</code>

<code>string string = s + </code><code>"d"</code> <code>// violation.</code>

<code>string = </code><code>"abc"</code> <code>+ </code><code>"d"</code> <code>// violation.</code>

<code>将一個字元的字元串替換成</code><code>' '</code>

<code>string string = s + </code><code>'d'</code>

<code>string = </code><code>"abc"</code> <code>+ </code><code>'d'</code>

十二、不要在循環中調用 synchronized(同步)方法

方法的同步需要消耗相當大的資料,在一個循環中調用它絕對不是一個好主意。

<code>public</code> <code>class</code> <code>syn {</code>

<code>public</code> <code>synchronized</code> <code>void</code> <code>method (object o) {</code>

<code>private</code> <code>void</code> <code>test () {</code>

<code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; vector.size(); i++) {</code>

<code>method (vector.elementat(i)); </code><code>// violation</code>

<code>private</code> <code>vector vector = </code><code>new</code> <code>vector (</code><code>5</code><code>, </code><code>5</code><code>);</code>

<code>不要在循環體中調用同步方法,如果必須同步的話,推薦以下方式:</code>

<code>public</code> <code>void</code> <code>method (object o) {</code>

<code>synchronized</code><code>{</code><code>//在一個同步塊中執行非同步方法</code>

<code>method (vector.elementat(i));</code>

十三、将 try/catch 塊移出循環

把 try/catch 塊放入循環體内,會極大的影響性能,如果編譯 jit 被關閉或者你所使用的是一個

不帶 jit 的 jvm,性能會将下降 21%之多!

<code>import</code> <code>java.io.fileinputstream;</code>

<code>public</code> <code>class</code> <code>try</code> <code>{</code>

<code>void</code> <code>method (fileinputstream fis) {</code>

<code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; size; i++) {</code>

<code>try</code> <code>{ </code><code>// violation</code>

<code>_sum += fis.read();</code>

<code>} </code><code>catch</code> <code>(exception e) {}</code>

<code>private</code> <code>int</code> <code>_sum;</code>

<code>将 </code><code>try</code><code>/</code><code>catch</code> <code>塊移出循環</code>

十四、對于 boolean 值,避免不必要的等式判斷

将一個 boolean 值與一個 true 比較是一個恒等操作(直接傳回該 boolean 變量的值). 移走對于

boolean 的不必要操作至少會帶來 2 個好處: 

1)代碼執行的更快 (生成的位元組碼少了 5 個位元組); 

2)代碼也會更加幹淨 。

十五、對于常量字元串,用'string' 代替 'stringbuffer'

常量字元串并不需要動态改變長度。

<code>public</code> <code>class</code> <code>usc {</code>

<code>string method () {</code>

<code>stringbuffer s = </code><code>new</code> <code>stringbuffer (</code><code>"hello"</code><code>);</code>

<code>string t = s + </code><code>"world!"</code><code>;</code>

<code>return</code> <code>t;</code>

<code>把 stringbuffer 換成 string,如果确定這個 string 不會再變的話,這将會減少運作開銷提高性</code>

十六、用'stringtokenizer' 代替 'indexof()' 和'substring()' 

字元串的分析在很多應用中都是常見的。使用 indexof()和 substring()來分析字元串容易導緻

stringindexoutofboundsexception。而使用 stringtokenizer 類來分析字元串則會容易一

些,效率也會高一些。

<code>public</code> <code>class</code> <code>ust {</code><code>void</code> <code>parsestring(string string) {</code>

<code>int</code> <code>index = </code><code>0</code><code>;</code>

<code>while</code> <code>((index = string.indexof(</code><code>"."</code><code>, index)) != -</code><code>1</code><code>) {</code>

<code>system.out.println (string.substring(index, string.length()));</code>