posts - 195, comments - 34, trackbacks - 0, articles - 1

          java代碼優(yōu)化編程(1)

          Posted on 2009-11-14 19:12 小強(qiáng)摩羯座 閱讀(228) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): Java

           

            可供程序利用的資源(內(nèi)存、CPU時(shí)間、網(wǎng)絡(luò)帶寬等)是有限的,優(yōu)化的目的就是讓程序用盡可能少的資源完成預(yù)定的任務(wù)。優(yōu)化通常包含兩方面的內(nèi)容:減小代碼的體積,提高代碼的運(yùn)行效率。本文討論的主要是如何提高代碼的效率。

            在 Java程序中,性能問(wèn)題的大部分原因并不在于Java語(yǔ)言,而是在于程序本身。養(yǎng)成好的代碼編寫(xiě)習(xí)慣非常重要,比如正確地、巧妙地運(yùn)用 java.lang.String類(lèi)和java.util.Vector類(lèi),它能夠顯著地提高程序的性能。下面我們就來(lái)具體地分析一下這方面的問(wèn)題。

            1、    盡量指定類(lèi)的final修飾符 帶有final修飾符的類(lèi)是不可派生的。在Java核心API中,有許多應(yīng)用final的例子,例如 java.lang.String。為String類(lèi)指定final防止了人們覆蓋length()方法。另外,如果指定一個(gè)類(lèi)為final,則該類(lèi)所有的方法都是final。Java編譯器會(huì)尋找機(jī)會(huì)內(nèi)聯(lián)(inline)所有的final方法(這和具體的編譯器實(shí)現(xiàn)有關(guān))。此舉能夠使性能平均提高 50% 。

            2、    盡量重用對(duì)象。特別是String 對(duì)象的使用中,出現(xiàn)字符串連接情況時(shí)應(yīng)用StringBuffer 代替。由于系統(tǒng)不僅要花時(shí)間生成對(duì)象,以后可能還需花時(shí)間對(duì)這些對(duì)象進(jìn)行垃圾回收和處理。因此,生成過(guò)多的對(duì)象將會(huì)給程序的性能帶來(lái)很大的影響。

            3、    盡量使用局部變量,調(diào)用方法時(shí)傳遞的參數(shù)以及在調(diào)用中創(chuàng)建的臨時(shí)變量都保存在棧(Stack)中,速度較快。其他變量,如靜態(tài)變量、實(shí)例變量等,都在堆(Heap)中創(chuàng)建,速度較慢。另外,依賴于具體的編譯器/JVM,局部變量還可能得到進(jìn)一步優(yōu)化。請(qǐng)參見(jiàn)《盡可能使用堆棧變量》。

            4、    不要重復(fù)初始化變量  默認(rèn)情況下,調(diào)用類(lèi)的構(gòu)造函數(shù)時(shí), Java會(huì)把變量初始化成確定的值:所有的對(duì)象被設(shè)置成null,整數(shù)變量(byte、 short、int、long)設(shè)置成0,float和double變量設(shè)置成0.0,邏輯值設(shè)置成false。當(dāng)一個(gè)類(lèi)從另一個(gè)類(lèi)派生時(shí),這一點(diǎn)尤其應(yīng)該注意,因?yàn)橛胣ew關(guān)鍵詞創(chuàng)建一個(gè)對(duì)象時(shí),構(gòu)造函數(shù)鏈中的所有構(gòu)造函數(shù)都會(huì)被自動(dòng)調(diào)用。

            5、    在JAVA + ORACLE 的應(yīng)用系統(tǒng)開(kāi)發(fā)中,java中內(nèi)嵌的SQL語(yǔ)句盡量使用大寫(xiě)的形式,以減輕ORACLE解析器的解析負(fù)擔(dān)。

            6、    Java 編程過(guò)程中,進(jìn)行數(shù)據(jù)庫(kù)連接、I/O流操作時(shí)務(wù)必小心,在使用完畢后,即使關(guān)閉以釋放資源。因?yàn)閷?duì)這些大對(duì)象的操作會(huì)造成系統(tǒng)大的開(kāi)銷(xiāo),稍有不慎,會(huì)導(dǎo)致嚴(yán)重的后果。

            7、    由于JVM的有其自身的GC機(jī)制,不需要程序開(kāi)發(fā)者的過(guò)多考慮,從一定程度上減輕了開(kāi)發(fā)者負(fù)擔(dān),但同時(shí)也遺漏了隱患,過(guò)分的創(chuàng)建對(duì)象會(huì)消耗系統(tǒng)的大量?jī)?nèi)存,嚴(yán)重時(shí)會(huì)導(dǎo)致內(nèi)存泄露,因此,保證過(guò)期對(duì)象的及時(shí)回收具有重要意義。JVM回收垃圾的條件是:對(duì)象不在被引用;然而,JVM的GC并非十分的機(jī)智,即使對(duì)象滿足了垃圾回收的條件也不一定會(huì)被立即回收。所以,建議我們?cè)趯?duì)象使用完畢,應(yīng)手動(dòng)置成null。

            8、    在使用同步機(jī)制時(shí),應(yīng)盡量使用方法同步代替代碼塊同步。

            9、    盡量減少對(duì)變量的重復(fù)計(jì)算

            例如:for(int i = 0;i < list.size; i ++) {

            …

            }

            應(yīng)替換為:

            for(int i = 0,int len = list.size();i < len; i ++) {

            …

            }

            10、盡量采用lazy loading 的策略,即在需要的時(shí)候才開(kāi)始創(chuàng)建。

            例如:    String str = “aaa”;

            if(i == 1) {

            list.add(str);

            }

            應(yīng)替換為:

            if(i == 1) {

            String str = “aaa”;

            list.add(str);

            }

            11、慎用異常

            異常對(duì)性能不利。拋出異常首先要?jiǎng)?chuàng)建一個(gè)新的對(duì)象。Throwable接口的構(gòu)造函數(shù)調(diào)用名為fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法檢查堆棧,收集調(diào)用跟蹤信息。只要有異常被拋出,VM就必須調(diào)整調(diào)用堆棧,因?yàn)樵谔幚磉^(guò)程中創(chuàng)建了一個(gè)新的對(duì)象。 異常只能用于錯(cuò)誤處理,不應(yīng)該用來(lái)控制程序流程。

          12、不要在循環(huán)中使用:

            Try {

            } catch() {

            }

            應(yīng)把其放置在最外層。

            13、StringBuffer 的使用:

            StringBuffer表示了可變的、可寫(xiě)的字符串。

            有三個(gè)構(gòu)造方法 :

            StringBuffer ();            //默認(rèn)分配16個(gè)字符的空間

            StringBuffer (int size);  //分配size個(gè)字符的空間

            StringBuffer (String str);  //分配16個(gè)字符+str.length()個(gè)字符空間

            你可以通過(guò)StringBuffer的構(gòu)造函數(shù)來(lái)設(shè)定它的初始化容量,這樣可以明顯地提升性能。這里提到的構(gòu)造函數(shù)是 StringBuffer(int length),length參數(shù)表示當(dāng)前的StringBuffer能保持的字符數(shù)量。你也可以使用 ensureCapacity(int minimumcapacity)方法在StringBuffer對(duì)象創(chuàng)建之后設(shè)置它的容量。首先我們看看 StringBuffer的缺省行為,然后再找出一條更好的提升性能的途徑。

            StringBuffer在內(nèi)部維護(hù)一個(gè)字符數(shù)組,當(dāng)你使用缺省的構(gòu)造函數(shù)來(lái)創(chuàng)建StringBuffer對(duì)象的時(shí)候,因?yàn)闆](méi)有設(shè)置初始化字符長(zhǎng)度,StringBuffer的容量被初始化為16個(gè)字符,也就是說(shuō)缺省容量就是16個(gè)字符。當(dāng)StringBuffer達(dá)到最大容量的時(shí)候,它會(huì)將自身容量增加到當(dāng)前的2倍再加2,也就是(2*舊值+2)。如果你使用缺省值,初始化之后接著往里面追加字符,在你追加到第16個(gè)字符的時(shí)候它會(huì)將容量增加到34(2*16+2),當(dāng)追加到34個(gè)字符的時(shí)候就會(huì)將容量增加到 70(2*34+2)。無(wú)論何事只要StringBuffer到達(dá)它的最大容量它就不得不創(chuàng)建一個(gè)新的字符數(shù)組然后重新將舊字符和新字符都拷貝一遍――這也太昂貴了點(diǎn)。所以總是給StringBuffer設(shè)置一個(gè)合理的初始化容量值是錯(cuò)不了的,這樣會(huì)帶來(lái)立竿見(jiàn)影的性能增益。

            StringBuffer初始化過(guò)程的調(diào)整的作用由此可見(jiàn)一斑。所以,使用一個(gè)合適的容量值來(lái)初始化StringBuffer永遠(yuǎn)都是一個(gè)最佳的建議。

            14、合理的使用Java類(lèi) java.util.Vector。

            簡(jiǎn)單地說(shuō),一個(gè)Vector就是一個(gè)java.lang.Object實(shí)例的數(shù)組。Vector與數(shù)組相似,它的元素可以通過(guò)整數(shù)形式的索引訪問(wèn)。但是,Vector類(lèi)型的對(duì)象在創(chuàng)建之后,對(duì)象的大小能夠根據(jù)元素的增加或者刪除而擴(kuò)展、縮小。請(qǐng)考慮下面這個(gè)向Vector加入元素的例子:

            Object obj = new Object();

            Vector v = new Vector(100000);

            for(int I=0;

            I<100000; I++) { v.add(0,obj); }

            除非有絕對(duì)充足的理由要求每次都把新元素插入到Vector的前面,否則上面的代碼對(duì)性能不利。在默認(rèn)構(gòu)造函數(shù)中,Vector的初始存儲(chǔ)能力是10個(gè)元素,如果新元素加入時(shí)存儲(chǔ)能力不足,則以后存儲(chǔ)能力每次加倍。Vector類(lèi)就象StringBuffer類(lèi)一樣,每次擴(kuò)展存儲(chǔ)能力時(shí),所有現(xiàn)有的元素都要復(fù)制到新的存儲(chǔ)空間之中。下面的代碼片段要比前面的例子快幾個(gè)數(shù)量級(jí):

            Object obj = new Object();

            Vector v = new Vector(100000);

            for(int I=0; I<100000; I++) { v.add(obj); }

            同樣的規(guī)則也適用于Vector類(lèi)的remove()方法。由于Vector中各個(gè)元素之間不能含有“空隙”,刪除除最后一個(gè)元素之外的任意其他元素都導(dǎo)致被刪除元素之后的元素向前移動(dòng)。也就是說(shuō),從Vector刪除最后一個(gè)元素要比刪除第一個(gè)元素“開(kāi)銷(xiāo)”低好幾倍。

            假設(shè)要從前面的Vector刪除所有元素,我們可以使用這種代碼:

            for(int I=0; I<100000; I++)

            {

            v.remove(0);

            }

            但是,與下面的代碼相比,前面的代碼要慢幾個(gè)數(shù)量級(jí):

            for(int I=0; I<100000; I++)

            {

            v.remove(v.size()-1);

            }

            從Vector類(lèi)型的對(duì)象v刪除所有元素的最好方法是:

            v.removeAllElements();


          假設(shè)Vector類(lèi)型的對(duì)象v包含字符串“Hello”。考慮下面的代碼,它要從這個(gè)Vector中刪除“Hello”字符串:

            String s = "Hello";

            int i = v.indexOf(s);

            if(I != -1) v.remove(s);

            這些代碼看起來(lái)沒(méi)什么錯(cuò)誤,但它同樣對(duì)性能不利。在這段代碼中,indexOf()方法對(duì)v進(jìn)行順序搜索尋找字符串“Hello”,remove(s)方法也要進(jìn)行同樣的順序搜索。改進(jìn)之后的版本是:

            String s = "Hello";

            int i = v.indexOf(s);

            if(I != -1) v.remove(i);

            這個(gè)版本中我們直接在remove()方法中給出待刪除元素的精確索引位置,從而避免了第二次搜索。一個(gè)更好的版本是:

            String s = "Hello"; v.remove(s);

            最后,我們?cè)賮?lái)看一個(gè)有關(guān)Vector類(lèi)的代碼片段:

            for(int I=0; I++;I < v.length)

            如果v包含100,000個(gè)元素,這個(gè)代碼片段將調(diào)用v.size()方法100,000次。雖然size方法是一個(gè)簡(jiǎn)單的方法,但它仍舊需要一次方法調(diào)用的開(kāi)銷(xiāo),至少JVM需要為它配置以及清除堆棧環(huán)境。在這里,for循環(huán)內(nèi)部的代碼不會(huì)以任何方式修改Vector類(lèi)型對(duì)象v的大小,因此上面的代碼最好改寫(xiě)成下面這種形式:

            int size = v.size(); for(int I=0; I++;I<size)

            雖然這是一個(gè)簡(jiǎn)單的改動(dòng),但它仍舊贏得了性能。畢竟,每一個(gè)CPU周期都是寶貴的。

            15、當(dāng)復(fù)制大量數(shù)據(jù)時(shí),使用System.arraycopy()命令。

            16、代碼重構(gòu):增強(qiáng)代碼的可讀性。

            例如:

            public class ShopCart {

            private List carts ;

            …

            public void add (Object item) {

            if(carts == null) {

            carts = new ArrayList();

            }

            crts.add(item);

            }

            public void remove(Object item) {

            if(carts. contains(item)) {

            carts.remove(item);

            }

            }

            public List getCarts() {

            //返回只讀列表

            return Collections.unmodifiableList(carts);

            }

            //不推薦這種方式

            //this.getCarts().add(item);

            }



          主站蜘蛛池模板: 阿克| 招远市| 南江县| 宜兴市| 梁平县| 和龙市| 筠连县| 南汇区| 高阳县| 承德县| 石林| 永平县| 祥云县| 龙口市| 星座| 靖远县| 湘阴县| 建水县| 文山县| 德阳市| 利津县| 莎车县| 石景山区| 崇信县| 甘南县| 南华县| 乌苏市| 滨州市| 碌曲县| 赞皇县| 云梦县| 临潭县| 太谷县| 五大连池市| 根河市| 永新县| 山东省| 九台市| 庆云县| 盐城市| 海盐县|