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