<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>


    posts - 15,  comments - 34,  trackbacks - 27
    ===================================
    提綱:
    ===================================
    一、通用篇
      1.1 不用new關(guān)鍵詞創(chuàng)建類的實(shí)例
      1.2 使用非阻塞I/O
      1.3 慎用異常
      1.4 不要重復(fù)初始化變量
      1.5 盡量指定類的final修飾符
      1.6 盡量使用局部變量
      1.7 乘法和除法
    二、J2EE篇
      2.1 使用緩沖標(biāo)記
      2.2 始終通過會(huì)話Bean訪問實(shí)體Bean
      2.3 選擇合適的引用機(jī)制
      2.4 在部署描述器中設(shè)置只讀屬性
      2.5 緩沖對(duì)EJB Home的訪問
      2.6 為EJB實(shí)現(xiàn)本地接口
      2.7 生成主鍵
      2.8 及時(shí)清除不再需要的會(huì)話
      2.9 在JSP頁(yè)面中關(guān)閉無(wú)用的會(huì)話
      2.10 Servlet與內(nèi)存使用
      2.11 HTTP Keep-Alive
      2.12 JDBC與Unicode
      2.13 JDBC與I/O
      1.14 內(nèi)存數(shù)據(jù)庫(kù)
    三、GUI篇
      3.1 用JAR壓縮類文件
      3.2 提示Applet裝入進(jìn)程
      3.3 在畫出圖形之前預(yù)先裝入它
      3.4 覆蓋update方法
      3.5 延遲重畫操作
      3.6 使用雙緩沖區(qū)
      3.7 使用BufferedImage
      3.8 使用VolatileImage
      3.9 使用Window Blitting
    四、補(bǔ)充資料
    ===================================
    正文:
    ===================================



    一、通用篇

    “通用篇”討論的問題適合于大多數(shù)Java應(yīng)用。

    1.1 不用new關(guān)鍵詞創(chuàng)建類的實(shí)例

    用new關(guān)鍵詞創(chuàng)建類的實(shí)例時(shí),構(gòu)造函數(shù)鏈中的所有構(gòu)造函數(shù)都會(huì)被自動(dòng)調(diào)用。但如果一個(gè)對(duì)象實(shí)現(xiàn)了Cloneable接口,我們可以調(diào)用它的clone()方法。clone()方法不會(huì)調(diào)用任何類構(gòu)造函數(shù)。

    在使用設(shè)計(jì)模式(Design Pattern)的場(chǎng)合,如果用Factory模式創(chuàng)建對(duì)象,則改用clone()方法創(chuàng)建新的對(duì)象實(shí)例非常簡(jiǎn)單。例如,下面是Factory模式的一個(gè)典型實(shí)現(xiàn):


    public static Credit getNewCredit() {
    return new Credit();
    }


    改進(jìn)后的代碼使用clone()方法,如下所示:


    private static Credit BaseCredit = new Credit();
    public static Credit getNewCredit() {
    return (Credit) BaseCredit.clone();
    }



    上面的思路對(duì)于數(shù)組處理同樣很有用。

    1.2 使用非阻塞I/O

    版本較低的JDK不支持非阻塞I/O API。為避免I/O阻塞,一些應(yīng)用采用了創(chuàng)建大量線程的辦法(在較好的情況下,會(huì)使用一個(gè)緩沖池)。這種技術(shù)可以在許多必須支持并發(fā)I/O流的應(yīng)用中見到,如Web服務(wù)器、報(bào)價(jià)和拍賣應(yīng)用等。然而,創(chuàng)建Java線程需要相當(dāng)可觀的開銷。

    JDK 1.4引入了非阻塞的I/O庫(kù)(java.nio)。如果應(yīng)用要求使用版本較早的JDK,在這里有一個(gè)支持非阻塞I/O的軟件包。

    請(qǐng)參見Sun中國(guó)網(wǎng)站的《調(diào)整Java的I/O性能》。

    1.3 慎用異常

    異常對(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)樵谔幚磉^程中創(chuàng)建了一個(gè)新的對(duì)象。

    異常只能用于錯(cuò)誤處理,不應(yīng)該用來(lái)控制程序流程。

    1.4 不要重復(fù)初始化變量

    默認(rèn)情況下,調(diào)用類的構(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è)類從另一個(gè)類派生時(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)用。

    1.5 盡量指定類的final修飾符

    帶有final修飾符的類是不可派生的。在Java核心API中,有許多應(yīng)用final的例子,例如java.lang.String。為String類指定final防止了人們覆蓋length()方法。

    另外,如果指定一個(gè)類為final,則該類所有的方法都是final。Java編譯器會(huì)尋找機(jī)會(huì)內(nèi)聯(lián)(inline)所有的final方法(這和具體的編譯器實(shí)現(xiàn)有關(guān))。此舉能夠使性能平均提高50%。

    1.6 盡量使用局部變量

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

    1.7 乘法和除法

    考慮下面的代碼:


    for (val = 0; val < 100000; val +=5) { alterX = val * 8; myResult = val * 2; }


    用移位操作替代乘法操作可以極大地提高性能。下面是修改后的代碼:


    for (val = 0; val < 100000; val += 5) { alterX = val << 3; myResult = val << 1; }



    修改后的代碼不再做乘以8的操作,而是改用等價(jià)的左移3位操作,每左移1位相當(dāng)于乘以2。相應(yīng)地,右移1位操作相當(dāng)于除以2。值得一提的是,雖然移位操作速度快,但可能使代碼比較難于理解,所以最好加上一些注釋。

    二、J2EE篇

    前面介紹的改善性能技巧適合于大多數(shù)Java應(yīng)用,接下來(lái)要討論的問題適合于使用JSP、EJB或JDBC的應(yīng)用。

    2.1 使用緩沖標(biāo)記

    一些應(yīng)用服務(wù)器加入了面向JSP的緩沖標(biāo)記功能。例如,BEA的WebLogic Server從6.0版本開始支持這個(gè)功能,Open Symphony工程也同樣支持這個(gè)功能。JSP緩沖標(biāo)記既能夠緩沖頁(yè)面片斷,也能夠緩沖整個(gè)頁(yè)面。當(dāng)JSP頁(yè)面執(zhí)行時(shí),如果目標(biāo)片斷已經(jīng)在緩沖之中,則生成該片斷的代碼就不用再執(zhí)行。頁(yè)面級(jí)緩沖捕獲對(duì)指定URL的請(qǐng)求,并緩沖整個(gè)結(jié)果頁(yè)面。對(duì)于購(gòu)物籃、目錄以及門戶網(wǎng)站的主頁(yè)來(lái)說,這個(gè)功能極其有用。對(duì)于這類應(yīng)用,頁(yè)面級(jí)緩沖能夠保存頁(yè)面執(zhí)行的結(jié)果,供后繼請(qǐng)求使用。

    對(duì)于代碼邏輯復(fù)雜的頁(yè)面,利用緩沖標(biāo)記提高性能的效果比較明顯;反之,效果可能略遜一籌。

    請(qǐng)參見《用緩沖技術(shù)提高JSP應(yīng)用的性能和穩(wěn)定性》。

    2.2 始終通過會(huì)話Bean訪問實(shí)體Bean

    直接訪問實(shí)體Bean不利于性能。當(dāng)客戶程序遠(yuǎn)程訪問實(shí)體Bean時(shí),每一個(gè)get方法都是一個(gè)遠(yuǎn)程調(diào)用。訪問實(shí)體Bean的會(huì)話Bean是本地的,能夠把所有數(shù)據(jù)組織成一個(gè)結(jié)構(gòu),然后返回它的值。

    用會(huì)話Bean封裝對(duì)實(shí)體Bean的訪問能夠改進(jìn)事務(wù)管理,因?yàn)闀?huì)話Bean只有在到達(dá)事務(wù)邊界時(shí)才會(huì)提交。每一個(gè)對(duì)get方法的直接調(diào)用產(chǎn)生一個(gè)事務(wù),容器將在每一個(gè)實(shí)體Bean的事務(wù)之后執(zhí)行一個(gè)“裝入-讀取”操作。

    一些時(shí)候,使用實(shí)體Bean會(huì)導(dǎo)致程序性能不佳。如果實(shí)體Bean的唯一用途就是提取和更新數(shù)據(jù),改成在會(huì)話Bean之內(nèi)利用JDBC訪問數(shù)據(jù)庫(kù)可以得到更好的性能。

    2.3 選擇合適的引用機(jī)制

    在典型的JSP應(yīng)用系統(tǒng)中,頁(yè)頭、頁(yè)腳部分往往被抽取出來(lái),然后根據(jù)需要引入頁(yè)頭、頁(yè)腳。當(dāng)前,在JSP頁(yè)面中引入外部資源的方法主要有兩種:include指令,以及include動(dòng)作。


    • include指令:例如<%@ include file="copyright.html" %>。該指令在編譯時(shí)引入指定的資源。在編譯之前,帶有include指令的頁(yè)面和指定的資源被合并成一個(gè)文件。被引用的外部資源在編譯時(shí)就確定,比運(yùn)行時(shí)才確定資源更高效。
    • include動(dòng)作:例如<jsp:include page="copyright.jsp" />。該動(dòng)作引入指定頁(yè)面執(zhí)行后生成的結(jié)果。由于它在運(yùn)行時(shí)完成,因此對(duì)輸出結(jié)果的控制更加靈活。但時(shí),只有當(dāng)被引用的內(nèi)容頻繁地改變時(shí),或者在對(duì)主頁(yè)面的請(qǐng)求沒有出現(xiàn)之前,被引用的頁(yè)面無(wú)法確定時(shí),使用include動(dòng)作才合算。


    2.4 在部署描述器中設(shè)置只讀屬性

    實(shí)體Bean的部署描述器允許把所有g(shù)et方法設(shè)置成“只讀”。當(dāng)某個(gè)事務(wù)單元的工作只包含執(zhí)行讀取操作的方法時(shí),設(shè)置只讀屬性有利于提高性能,因?yàn)槿萜鞑槐卦賵?zhí)行存儲(chǔ)操作。

    2.5 緩沖對(duì)EJB Home的訪問

    EJB Home接口通過JNDI名稱查找獲得。這個(gè)操作需要相當(dāng)可觀的開銷。JNDI查找最好放入Servlet的init()方法里面。如果應(yīng)用中多處頻繁地出現(xiàn)EJB訪問,最好創(chuàng)建一個(gè)EJBHomeCache類。EJBHomeCache類一般應(yīng)該作為singleton實(shí)現(xiàn)。

    2.6 為EJB實(shí)現(xiàn)本地接口

    本地接口是EJB 2.0規(guī)范新增的內(nèi)容,它使得Bean能夠避免遠(yuǎn)程調(diào)用的開銷。請(qǐng)考慮下面的代碼。


    PayBeanHome home = (PayBeanHome)
    javax.rmi.PortableRemoteObject.narrow
    (ctx.lookup ("PayBeanHome"), PayBeanHome.class);
    PayBean bean = (PayBean)
    javax.rmi.PortableRemoteObject.narrow
    (home.create(), PayBean.class);



    第一個(gè)語(yǔ)句表示我們要尋找Bean的Home接口。這個(gè)查找通過JNDI進(jìn)行,它是一個(gè)RMI調(diào)用。然后,我們定位遠(yuǎn)程對(duì)象,返回代理引用,這也是一個(gè)RMI調(diào)用。第二個(gè)語(yǔ)句示范了如何創(chuàng)建一個(gè)實(shí)例,涉及了創(chuàng)建IIOP請(qǐng)求并在網(wǎng)絡(luò)上傳輸請(qǐng)求的stub程序,它也是一個(gè)RMI調(diào)用。

    要實(shí)現(xiàn)本地接口,我們必須作如下修改:


    1. 方法不能再拋出java.rmi.RemoteException異常,包括從RemoteException派生的異常,比如TransactionRequiredException、TransactionRolledBackException和NoSuchObjectException。EJB提供了等價(jià)的本地異常,如TransactionRequiredLocalException、TransactionRolledBackLocalException和NoSuchObjectLocalException。
    2. 所有數(shù)據(jù)和返回值都通過引用的方式傳遞,而不是傳遞值。
    3. 本地接口必須在EJB部署的機(jī)器上使用。簡(jiǎn)而言之,客戶程序和提供服務(wù)的組件必須在同一個(gè)JVM上運(yùn)行。
    4. 如果Bean實(shí)現(xiàn)了本地接口,則其引用不可串行化。


    請(qǐng)參見《用本地引用提高EJB訪問效率》。

    2.7 生成主鍵

    在EJB之內(nèi)生成主鍵有許多途徑,下面分析了幾種常見的辦法以及它們的特點(diǎn)。

    利用數(shù)據(jù)庫(kù)內(nèi)建的標(biāo)識(shí)機(jī)制(SQL Server的IDENTITY或Oracle的SEQUENCE)。這種方法的缺點(diǎn)是EJB可移植性差。

    由實(shí)體Bean自己計(jì)算主鍵值(比如做增量操作)。它的缺點(diǎn)是要求事務(wù)可串行化,而且速度也較慢。

    利用NTP之類的時(shí)鐘服務(wù)。這要求有面向特定平臺(tái)的本地代碼,從而把Bean固定到了特定的OS之上。另外,它還導(dǎo)致了這樣一種可能,即在多CPU的服務(wù)器上,同一個(gè)毫秒之內(nèi)生成了兩個(gè)主鍵。

    借鑒Microsoft的思路,在Bean中創(chuàng)建一個(gè)GUID。然而,如果不求助于JNI,Java不能確定網(wǎng)卡的MAC地址;如果使用JNI,則程序就要依賴于特定的OS。

    還有其他幾種辦法,但這些辦法同樣都有各自的局限。似乎只有一個(gè)答案比較理想:結(jié)合運(yùn)用RMI和JNDI。先通過RMI注冊(cè)把RMI遠(yuǎn)程對(duì)象綁定到JNDI樹。客戶程序通過JNDI進(jìn)行查找。下面是一個(gè)例子:


    public class keyGenerator extends UnicastRemoteObject implements Remote {
    private static long KeyValue = System.currentTimeMillis();
    public static synchronized long getKey() throws RemoteException { return KeyValue++; }



    2.8 及時(shí)清除不再需要的會(huì)話

    為了清除不再活動(dòng)的會(huì)話,許多應(yīng)用服務(wù)器都有默認(rèn)的會(huì)話超時(shí)時(shí)間,一般為30分鐘。當(dāng)應(yīng)用服務(wù)器需要保存更多會(huì)話時(shí),如果內(nèi)存容量不足,操作系統(tǒng)會(huì)把部分內(nèi)存數(shù)據(jù)轉(zhuǎn)移到磁盤,應(yīng)用服務(wù)器也可能根據(jù)“最近最頻繁使用”(Most Recently Used)算法把部分不活躍的會(huì)話轉(zhuǎn)儲(chǔ)到磁盤,甚至可能拋出“內(nèi)存不足”異常。在大規(guī)模系統(tǒng)中,串行化會(huì)話的代價(jià)是很昂貴的。當(dāng)會(huì)話不再需要時(shí),應(yīng)當(dāng)及時(shí)調(diào)用HttpSession.invalidate()方法清除會(huì)話。HttpSession.invalidate()方法通常可以在應(yīng)用的退出頁(yè)面調(diào)用。

    2.9 在JSP頁(yè)面中關(guān)閉無(wú)用的會(huì)話

    對(duì)于那些無(wú)需跟蹤會(huì)話狀態(tài)的頁(yè)面,關(guān)閉自動(dòng)創(chuàng)建的會(huì)話可以節(jié)省一些資源。使用如下page指令:


    <%@ page session="false"%>




    2.10 Servlet與內(nèi)存使用

    許多開發(fā)者隨意地把大量信息保存到用戶會(huì)話之中。一些時(shí)候,保存在會(huì)話中的對(duì)象沒有及時(shí)地被垃圾回收機(jī)制回收。從性能上看,典型的癥狀是用戶感到系統(tǒng)周期性地變慢,卻又不能把原因歸于任何一個(gè)具體的組件。如果監(jiān)視JVM的堆空間,它的表現(xiàn)是內(nèi)存占用不正常地大起大落。

    解決這類內(nèi)存問題主要有二種辦法。第一種辦法是,在所有作用范圍為會(huì)話的Bean中實(shí)現(xiàn)HttpSessionBindingListener接口。這樣,只要實(shí)現(xiàn)valueUnbound()方法,就可以顯式地釋放Bean使用的資源。

    另外一種辦法就是盡快地把會(huì)話作廢。大多數(shù)應(yīng)用服務(wù)器都有設(shè)置會(huì)話作廢間隔時(shí)間的選項(xiàng)。另外,也可以用編程的方式調(diào)用會(huì)話的setMaxInactiveInterval()方法,該方法用來(lái)設(shè)定在作廢會(huì)話之前,Servlet容器允許的客戶請(qǐng)求的最大間隔時(shí)間,以秒計(jì)。

    2.11 HTTP Keep-Alive

    Keep-Alive功能使客戶端到服務(wù)器端的連接持續(xù)有效,當(dāng)出現(xiàn)對(duì)服務(wù)器的后繼請(qǐng)求時(shí),Keep-Alive功能避免了建立或者重新建立連接。市場(chǎng)上的大部分Web服務(wù)器,包括iPlanet、IIS和Apache,都支持HTTP Keep-Alive。對(duì)于提供靜態(tài)內(nèi)容的網(wǎng)站來(lái)說,這個(gè)功能通常很有用。但是,對(duì)于負(fù)擔(dān)較重的網(wǎng)站來(lái)說,這里存在另外一個(gè)問題:雖然為客戶保留打開的連接有一定的好處,但它同樣影響了性能,因?yàn)樵谔幚頃和F陂g,本來(lái)可以釋放的資源仍舊被占用。當(dāng)Web服務(wù)器和應(yīng)用服務(wù)器在同一臺(tái)機(jī)器上運(yùn)行時(shí),Keep-Alive功能對(duì)資源利用的影響尤其突出。

    2.12 JDBC與Unicode

    想必你已經(jīng)了解一些使用JDBC時(shí)提高性能的措施,比如利用連接池、正確地選擇存儲(chǔ)過程和直接執(zhí)行的SQL、從結(jié)果集刪除多余的列、預(yù)先編譯SQL語(yǔ)句,等等。

    除了這些顯而易見的選擇之外,另一個(gè)提高性能的好選擇可能就是把所有的字符數(shù)據(jù)都保存為Unicode(代碼頁(yè)13488)。Java以Unicode形式處理所有數(shù)據(jù),因此,數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序不必再執(zhí)行轉(zhuǎn)換過程。但應(yīng)該記住:如果采用這種方式,數(shù)據(jù)庫(kù)會(huì)變得更大,因?yàn)槊總€(gè)Unicode字符需要2個(gè)字節(jié)存儲(chǔ)空間。另外,如果有其他非Unicode的程序訪問數(shù)據(jù)庫(kù),性能問題仍舊會(huì)出現(xiàn),因?yàn)檫@時(shí)數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序仍舊必須執(zhí)行轉(zhuǎn)換過程。

    2.13 JDBC與I/O

    如果應(yīng)用程序需要訪問一個(gè)規(guī)模很大的數(shù)據(jù)集,則應(yīng)當(dāng)考慮使用塊提取方式。默認(rèn)情況下,JDBC每次提取32行數(shù)據(jù)。舉例來(lái)說,假設(shè)我們要遍歷一個(gè)5000行的記錄集,JDBC必須調(diào)用數(shù)據(jù)庫(kù)157次才能提取到全部數(shù)據(jù)。如果把塊大小改成512,則調(diào)用數(shù)據(jù)庫(kù)的次數(shù)將減少到10次。

    在一些情形下這種技術(shù)無(wú)效。例如,如果使用可滾動(dòng)的記錄集,或者在查詢中指定了FOR UPDATE,則塊操作方式不再有效。

    1.14 內(nèi)存數(shù)據(jù)庫(kù)

    許多應(yīng)用需要以用戶為單位在會(huì)話對(duì)象中保存相當(dāng)數(shù)量的數(shù)據(jù),典型的應(yīng)用如購(gòu)物籃和目錄等。由于這類數(shù)據(jù)可以按照行/列的形式組織,因此,許多應(yīng)用創(chuàng)建了龐大的Vector或HashMap。在會(huì)話中保存這類數(shù)據(jù)極大地限制了應(yīng)用的可伸縮性,因?yàn)榉?wù)器擁有的內(nèi)存至少必須達(dá)到每個(gè)會(huì)話占用的內(nèi)存數(shù)量乘以并發(fā)用戶最大數(shù)量,它不僅使服務(wù)器價(jià)格昂貴,而且垃圾收集的時(shí)間間隔也可能延長(zhǎng)到難以忍受的程度。

    一些人把購(gòu)物籃/目錄功能轉(zhuǎn)移到數(shù)據(jù)庫(kù)層,在一定程度上提高了可伸縮性。然而,把這部分功能放到數(shù)據(jù)庫(kù)層也存在問題,且問題的根源與大多數(shù)關(guān)系數(shù)據(jù)庫(kù)系統(tǒng)的體系結(jié)構(gòu)有關(guān)。對(duì)于關(guān)系數(shù)據(jù)庫(kù)來(lái)說,運(yùn)行時(shí)的重要原則之一是確保所有的寫入操作穩(wěn)定、可靠,因而,所有的性能問題都與物理上把數(shù)據(jù)寫入磁盤的能力有關(guān)。關(guān)系數(shù)據(jù)庫(kù)力圖減少I/O操作,特別是對(duì)于讀操作,但實(shí)現(xiàn)該目標(biāo)的主要途徑只是執(zhí)行一套實(shí)現(xiàn)緩沖機(jī)制的復(fù)雜算法,而這正是數(shù)據(jù)庫(kù)層第一號(hào)性能瓶頸通常總是CPU的主要原因。

    一種替代傳統(tǒng)關(guān)系數(shù)據(jù)庫(kù)的方案是,使用在內(nèi)存中運(yùn)行的數(shù)據(jù)庫(kù)(In-memory Database),例如TimesTen。內(nèi)存數(shù)據(jù)庫(kù)的出發(fā)點(diǎn)是允許數(shù)據(jù)臨時(shí)地寫入,但這些數(shù)據(jù)不必永久地保存到磁盤上,所有的操作都在內(nèi)存中進(jìn)行。這樣,內(nèi)存數(shù)據(jù)庫(kù)不需要復(fù)雜的算法來(lái)減少I/O操作,而且可以采用比較簡(jiǎn)單的加鎖機(jī)制,因而速度很快。

    三、GUI篇

    這一部分介紹的內(nèi)容適合于圖形用戶界面的應(yīng)用(Applet和普通應(yīng)用),要用到AWT或Swing。

    3.1 用JAR壓縮類文件

    Java檔案文件(JAR文件)是根據(jù)JavaBean標(biāo)準(zhǔn)壓縮的文件,是發(fā)布JavaBean組件的主要方式和推薦方式。JAR檔案有助于減少文件體積,縮短下載時(shí)間。例如,它有助于Applet提高啟動(dòng)速度。一個(gè)JAR文件可以包含一個(gè)或者多個(gè)相關(guān)的Bean以及支持文件,比如圖形、聲音、HTML和其他資源。

    要在HTML/JSP文件中指定JAR文件,只需在Applet標(biāo)記中加入ARCHIVE = "name.jar"聲明。

    請(qǐng)參見《使用檔案文件提高 applet 的加載速度》。

    3.2 提示Applet裝入進(jìn)程

    你是否看到過使用Applet的網(wǎng)站,注意到在應(yīng)該運(yùn)行Applet的地方出現(xiàn)了一個(gè)占位符?當(dāng)Applet的下載時(shí)間較長(zhǎng)時(shí),會(huì)發(fā)生什么事情?最大的可能就是用戶掉頭離去。在這種情況下,顯示一個(gè)Applet正在下載的信息無(wú)疑有助于鼓勵(lì)用戶繼續(xù)等待。

    下面我們來(lái)看看一種具體的實(shí)現(xiàn)方法。首先創(chuàng)建一個(gè)很小的Applet,該Applet負(fù)責(zé)在后臺(tái)下載正式的Applet:


    import java.applet.Applet;
    import java.applet.AppletStub;
    import java.awt.Label;
    import java.awt.Graphics;
    import java.awt.GridLayout;
    public class PreLoader extends Applet implements Runnable, AppletStub {
    String largeAppletName;
    Label label;
    public void init() {
    // 要求裝載的正式Applet
    largeAppletName = getParameter("applet");
    // “請(qǐng)稍等”提示信息
    label = new Label("請(qǐng)稍等..." + largeAppletName);
    add(label);
    }
    public void run(){
    try {
    // 獲得待裝載Applet的類
    Class largeAppletClass = Class.forName(largeAppletName);
    // 創(chuàng)建待裝載Applet的實(shí)例
    Applet largeApplet = (Applet)largeAppletClass.newInstance();
    // 設(shè)置該Applet的Stub程序
    largeApplet.setStub(this);
    // 取消“請(qǐng)稍等”信息
    remove(label);
    // 設(shè)置布局
    setLayout(new GridLayout(1, 0));
    add(largeApplet);
    // 顯示正式的Applet
    largeApplet.init();
    largeApplet.start();
    }
    catch (Exception ex) {
    // 顯示錯(cuò)誤信息
    label.setText("不能裝入指定的Applet");
    }
    // 刷新屏幕
    validate();
    }
    public void appletResize(int width, int height) {
    // 把a(bǔ)ppletResize調(diào)用從stub程序傳遞到Applet
    resize(width, height);
    }
    }



    編譯后的代碼小于2K,下載速度很快。代碼中有幾個(gè)地方值得注意。首先,PreLoader實(shí)現(xiàn)了AppletStub接口。一般地,Applet從調(diào)用者判斷自己的codebase。在本例中,我們必須調(diào)用setStub()告訴Applet到哪里提取這個(gè)信息。另一個(gè)值得注意的地方是,AppletStub接口包含許多和Applet類一樣的方法,但appletResize()方法除外。這里我們把對(duì)appletResize()方法的調(diào)用傳遞給了resize()方法。

    3.3 在畫出圖形之前預(yù)先裝入它

    ImageObserver接口可用來(lái)接收?qǐng)D形裝入的提示信息。ImageObserver接口只有一個(gè)方法imageUpdate(),能夠用一次repaint()操作在屏幕上畫出圖形。下面提供了一個(gè)例子。


    public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) {
    if ((flags & ALLBITS) !=0 {
    repaint();
    }
    else if (flags & (ERROR |ABORT )) != 0) {
    error = true;
    // 文件沒有找到,考慮顯示一個(gè)占位符
    repaint();
    }
    return (flags & (ALLBITS | ERROR| ABORT)) == 0;
    }



    當(dāng)圖形信息可用時(shí),imageUpdate()方法被調(diào)用。如果需要進(jìn)一步更新,該方法返回true;如果所需信息已經(jīng)得到,該方法返回false。

    3.4 覆蓋update方法

    update()方法的默認(rèn)動(dòng)作是清除屏幕,然后調(diào)用paint()方法。如果使用默認(rèn)的update()方法,頻繁使用圖形的應(yīng)用可能出現(xiàn)顯示閃爍現(xiàn)象。要避免在paint()調(diào)用之前的屏幕清除操作,只需按照如下方式覆蓋update()方法:


    public void update(Graphics g) {
    paint(g);
    }


    更理想的方案是:覆蓋update(),只重畫屏幕上發(fā)生變化的區(qū)域,如下所示:


    public void update(Graphics g) {
    g.clipRect(x, y, w, h);
    paint(g);
    }


    3.5 延遲重畫操作

    對(duì)于圖形用戶界面的應(yīng)用來(lái)說,性能低下的主要原因往往可以歸結(jié)為重畫屏幕的效率低下。當(dāng)用戶改變窗口大小或者滾動(dòng)一個(gè)窗口時(shí),這一點(diǎn)通常可以很明顯地觀察到。改變窗口大小或者滾動(dòng)屏幕之類的操作導(dǎo)致重畫屏幕事件大量地、快速地生成,甚至超過了相關(guān)代碼的執(zhí)行速度。對(duì)付這個(gè)問題最好的辦法是忽略所有“遲到”的事件。

    建議在這里引入一個(gè)數(shù)毫秒的時(shí)差,即如果我們立即接收到了另一個(gè)重畫事件,可以停止處理當(dāng)前事件轉(zhuǎn)而處理最后一個(gè)收到的重畫事件;否則,我們繼續(xù)進(jìn)行當(dāng)前的重畫過程。

    如果事件要啟動(dòng)一項(xiàng)耗時(shí)的工作,分離出一個(gè)工作線程是一種較好的處理方式;否則,一些部件可能被“凍結(jié)”,因?yàn)槊看沃荒芴幚硪粋€(gè)事件。下面提供了一個(gè)事件處理的簡(jiǎn)單例子,但經(jīng)過擴(kuò)展后它可以用來(lái)控制工作線程。


    public static void runOnce(String id, final long milliseconds) {
    synchronized(e_queue) { // e_queue: 所有事件的集合
    if (!e_queue.containsKey(id)) {
    e_queue.put(token, new LastOne());
    }
    }
    final LastOne lastOne = (LastOne) e_queue.get(token);
    final long time = System.currentTimeMillis(); // 獲得當(dāng)前時(shí)間
    lastOne.time = time;
    (new Thread() {public void run() {
    if (milliseconds > 0) {
    try {Thread.sleep(milliseconds);} // 暫停線程
    catch (Exception ex) {}
    }
    synchronized(lastOne.running) { // 等待上一事件結(jié)束
    if (lastOne.time != time) // 只處理最后一個(gè)事件
    return;
    }
    }}).start();
    }
    private static Hashtable e_queue = new Hashtable();
    private static class LastOne {
    public long time=0;
    public Object running = new Object();
    }



    3.6 使用雙緩沖區(qū)

    在屏幕之外的緩沖區(qū)繪圖,完成后立即把整個(gè)圖形顯示出來(lái)。由于有兩個(gè)緩沖區(qū),所以程序可以來(lái)回切換。這樣,我們可以用一個(gè)低優(yōu)先級(jí)的線程負(fù)責(zé)畫圖,使得程序能夠利用空閑的CPU時(shí)間執(zhí)行其他任務(wù)。下面的偽代碼片斷示范了這種技術(shù)。


    Graphics myGraphics;
    Image myOffscreenImage = createImage(size().width, size().height);
    Graphics offscreenGraphics = myOffscreenImage.getGraphics();
    offscreenGraphics.drawImage(img, 50, 50, this);
    myGraphics.drawImage(myOffscreenImage, 0, 0, this);



    3.7 使用BufferedImage

    Java JDK 1.2使用了一個(gè)軟顯示設(shè)備,使得文本在不同的平臺(tái)上看起來(lái)相似。為實(shí)現(xiàn)這個(gè)功能,Java必須直接處理構(gòu)成文字的像素。由于這種技術(shù)要在內(nèi)存中大量地進(jìn)行位復(fù)制操作,早期的JDK在使用這種技術(shù)時(shí)性能不佳。為解決這個(gè)問題而提出的Java標(biāo)準(zhǔn)實(shí)現(xiàn)了一種新的圖形類型,即BufferedImage。

    BufferedImage子類描述的圖形帶有一個(gè)可訪問的圖形數(shù)據(jù)緩沖區(qū)。一個(gè)BufferedImage包含一個(gè)ColorModel和一組光柵圖形數(shù)據(jù)。這個(gè)類一般使用RGB(紅、綠、藍(lán))顏色模型,但也可以處理灰度級(jí)圖形。它的構(gòu)造函數(shù)很簡(jiǎn)單,如下所示:


    public BufferedImage (int width, int height, int imageType)



    ImageType允許我們指定要緩沖的是什么類型的圖形,比如5-位RGB、8-位RGB、灰度級(jí)等。

    3.8 使用VolatileImage

    許多硬件平臺(tái)和它們的操作系統(tǒng)都提供基本的硬件加速支持。例如,硬件加速一般提供矩形填充功能,和利用CPU完成同一任務(wù)相比,硬件加速的效率更高。由于硬件加速分離了一部分工作,允許多個(gè)工作流并發(fā)進(jìn)行,從而緩解了對(duì)CPU和系統(tǒng)總線的壓力,使得應(yīng)用能夠運(yùn)行得更快。利用VolatileImage可以創(chuàng)建硬件加速的圖形以及管理圖形的內(nèi)容。由于它直接利用低層平臺(tái)的能力,性能的改善程度主要取決于系統(tǒng)使用的圖形適配器。VolatileImage的內(nèi)容隨時(shí)可能丟失,也即它是“不穩(wěn)定的(volatile)”。因此,在使用圖形之前,最好檢查一下它的內(nèi)容是否丟失。VolatileImage有兩個(gè)能夠檢查內(nèi)容是否丟失的方法:


    public abstract int validate(GraphicsConfiguration gc);
    public abstract Boolean contentsLost();



    每次從VolatileImage對(duì)象復(fù)制內(nèi)容或者寫入VolatileImage時(shí),應(yīng)該調(diào)用validate()方法。contentsLost()方法告訴我們,自從最后一次validate()調(diào)用之后,圖形的內(nèi)容是否丟失。

    雖然VolatileImage是一個(gè)抽象類,但不要從它這里派生子類。VolatileImage應(yīng)該通過Component.createVolatileImage()或者GraphicsConfiguration.createCompatibleVolatileImage()方法創(chuàng)建。

    3.9 使用Window Blitting

    進(jìn)行滾動(dòng)操作時(shí),所有可見的內(nèi)容一般都要重畫,從而導(dǎo)致大量不必要的重畫工作。許多操作系統(tǒng)的圖形子系統(tǒng),包括WIN32 GDI、MacOS和X/Windows,都支持Window Blitting技術(shù)。Window Blitting技術(shù)直接在屏幕緩沖區(qū)中把圖形移到新的位置,只重畫新出現(xiàn)的區(qū)域。要在Swing應(yīng)用中使用Window Blitting技術(shù),設(shè)置方法如下:


    setScrollMode(int mode);



    在大多數(shù)應(yīng)用中,使用這種技術(shù)能夠提高滾動(dòng)速度。只有在一種情形下,Window Blitting會(huì)導(dǎo)致性能降低,即應(yīng)用在后臺(tái)進(jìn)行滾動(dòng)操作。如果是用戶在滾動(dòng)一個(gè)應(yīng)用,那么它總是在前臺(tái),無(wú)需擔(dān)心任何負(fù)面影響。

    posted on 2005-02-04 11:41 jacky 閱讀(162) 評(píng)論(0)  編輯  收藏

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    常用鏈接

    留言簿(10)

    隨筆檔案

    文章分類

    文章檔案

    相冊(cè)

    收藏夾

    java

    搜索

    •  

    最新評(píng)論


    主站蜘蛛池模板: 国产精品国产亚洲精品看不卡| 91亚洲精品视频| 日日麻批免费40分钟无码| 亚洲一区二区三区免费观看| 青青青青青青久久久免费观看| h视频免费高清在线观看| 亚洲天堂一区二区三区四区| 国产成人免费一区二区三区| 国色精品va在线观看免费视频 | 久久亚洲国产成人影院| 亚洲福利在线播放| ww4545四虎永久免费地址| 免费观看四虎精品成人| 亚洲精品美女视频| 亚洲日韩中文在线精品第一| 永久在线观看www免费视频| 黄网站色成年片大免费高清| 91亚洲精品第一综合不卡播放| 免费h成人黄漫画嘿咻破解版| 在线观看免费av网站| 四虎国产精品永免费| 亚洲精品国产精品国自产网站| 国产亚洲自拍一区| 免费激情视频网站| 18pao国产成视频永久免费| 免费毛片毛片网址| 99999久久久久久亚洲| 久久精品国产亚洲综合色| 国产成人免费片在线视频观看| 久久国产色AV免费观看| ssswww日本免费网站片| 亚洲一区二区三区高清在线观看| 亚洲福利在线观看| 亚洲国产三级在线观看| 亚洲AV无码乱码在线观看性色扶| 免费可以在线看A∨网站| 7x7x7x免费在线观看| 中文字幕无码免费久久9一区9| 亚洲av无码成人影院一区| 亚洲av无码国产综合专区| 亚洲国产香蕉碰碰人人|