張敬
摘 要程序編碼在整個軟件生命周期中是將軟件設計的結果轉換成計算機可運行的程序代碼的過程,也可以說是程序的內在本質。在實際項目中,代碼優化至關重要。本文就是從代碼優化的角度,介紹了一些優化Java代碼的建議,包括盡量重用對象,使用局部變量等,還利用一些實例來說明和解釋優化方法的有效性,可以使軟件性能得到提升,提高軟件的運行速度,減少運行時占用的內存。
【關鍵詞】軟件性能 代碼優化 Java
1 前言
當今的軟件功能越來越復雜,需求也越來越多,隨之而來對軟件性能上的要求有時候是硬件不能完全解決的。很多實際的項目證明,如果在開發軟件時不注意軟件性能的優化,雖然可能實現了要求的功能,但是也可能不會給用戶帶來很好的效益。因此,軟件的性能優化一直是計算機開發過程中需要注意的問題,而代碼優化是性能優化其中重要的一個方面。一個優秀的軟件系統應該有一個優化的代碼結構。代碼優化的目的是減小代碼體積,提高代碼運行的效率。但是可能有些人覺得沒用,改與不改對于代碼的運行效率有什么影響呢?如果項目著眼于盡快無BUG上線,那么此時代碼的細節可以不精打細磨;但是如果有足夠的時間開發、維護代碼,這時候就必須考慮每個可以優化的細節了,一個一個細小的優化點累積起來,對于代碼的運行效率絕對大有提升。
2 軟件性能的代碼優化
2.1 軟件性能
軟件性能是軟件的一種非功能特性,它關注的不是軟件是否能夠完成特定的功能,而是在完成該功能時展示出來的及時性,是指一個軟件系統正確提供其服務的能力和效率,是軟件對用戶請求響應速度在響應時間、吞吐量、資源利用率和可用性等方面的度量。
2.2 代碼優化
代碼優化是指對程序代碼進行等價變換。等價的含義是使得變換后的代碼運行結果與變換前代碼運行結果相同。優化的含義是最終生成的代碼短,時空效率優化。優化可以在編譯的各個階段進行,目標是能生成更加高效的目標代碼。
2.3 代碼優化方法及實例應用
2.3.1 盡量重用對象和盡可能使用局部變量
當使用String對象時,出現字符串連接時應該使用StringBuilder或StringBuffer代替。由于Java虛擬機不僅要花時間生成對象,以后可能還需要花時間對這些對象進行垃圾回收和處理,因此,生成過多的對象將會給程序的性能帶來很大的影響。調用方法時傳遞的參數以及在調用中創建的臨時變量都保存在棧中,因此速度較快,但是其他變量,如靜態變量、實例變量等,都在堆中創建,速度較慢。另外,棧中創建的變量,隨著方法的運行結束,不需要額外的垃圾回收;而在堆中創建的變量,需要進行額外回收。
2.3.2 盡量減少對變量的重復計算
在循環計算中,即使只有一條語句,對系統也是有消耗的,所以for循環中循環的大小可以在第一次進入循環時就聲明 ,不必每次循環都計算一遍。例如:
for (int i = 0; i < list.size(); i++)
{...}
建議替換為:
for (int i = 0, int length = list.size(); i < length; i++)
{...}
這樣在list.size()很大的時候,就減少了很多消耗。
2.3.3 盡量采用“懶加載”的策略,即在需要的時候才創建
舉個例子說明,就是:
String str = "aaa";
if (i == 1)
{list.add(str);}
建議替換為:
if (i == 1){String str = "aaa";list.add(str);}
2.3.4 循環內不要不斷創建對象引用
for (int i = 1; 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,但是內存中只有一份,這樣就大大節省了內存空間。
2.3.5 使用帶緩沖的輸入輸出流進行I/O操作,并且及時關閉輸入輸出流
帶緩沖的輸入輸出流,即BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream,這可以極大地提升I/O效率。同時在Java編程過程中,進行數據庫連接、I/O流操作時,務必在使用完畢后及時關閉輸入輸出流,以釋放資源。因為對這些大對象的操作會造成大的開銷,不注意的話,可能會導致嚴重的后果。
2.3.6 不讓public方法中有太多的形參
public方法即對外提供的方法,如果給這些方法太多形參的話主要有兩點壞處:
a.違反了面向對象的編程思想,Java講求一切都是對象,太多的形參,和面向對象的編程思想并不契合;
b.參數太多勢必導致方法調用的出錯概率增加。
比如我們用JDBC寫一個insertInfo方法,有9個信息字段要插如Person表中,可以把這9個參數封裝在一個實體類中,作為insertInfo方法的形參,而不是把這9個信息字段作為該方法的形參。。
2.3.7 字符串變量和字符串常量equals的時候將字符串常量寫在前面
這是一個比較常見的小技巧。如果有以下代碼:
String str = "678";
if (str.equals("678")) {...
}
建議修改為:
String str = "678";
if ("678".equals(str))
{...}
這么做主要是可以避免空指針異常。
2.3.8 基本數據類型轉為字符串,數據.toString()是最快的方式、String.valueOf(數據)次之、數據+””最慢
把基本數據類型轉為字符串有三種方式:一個Integer型數據i,可以使用i.toString()、String.valueOf(i)、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
所以以后遇到把一個基本數據類型轉為String的時候,優先考慮使用toString()方法。原因是:
(1)String.valueOf()方法底層調用了Integer.toString()方法,但是會在調用前做空判斷;
(2)Integer.toString()方法是直接調用;
(3)i + “”底層使用了StringBuilder實現,先用append方法拼接,再用toString()方法獲取字符串。
三者對比下來,明顯是(2)最快、(1)次之、(3)最慢。
3 結束語
第二節介紹了八種優化Java代碼的方法和實例,分析了進行優化的方法,可以提高所編寫程序的性能,并增強代碼的可讀性和可擴展性。在具體的項目實踐中,這八種是最常用的方法,我們可以為具體的應用程序在其中找到改善軟件性能的方法,進而提高用戶體驗,獲得更大的效益。
參考文獻
[1]馮宏華,徐瑩.C++應用程序性能優化[M].北京:電子工業出版社,2010.
[2]陳宇,李可.淺議Java程序優化的幾種方法與成效[J].計算機光盤軟件與應用,2013(07):60-61.
[3]錢宇虹.淺析Java程序I/O性能的改進策略[J].軟件工程師,2013(11):25-27.
[4]柳飛,陸明剛.Charlie Hunt,Binu John. Java性能優化權威指南[M].北京:人民郵電出版社,2014.
作者單位
中國海洋大學信息科學與工程學院 山東省青島市 266100