1. 程式人生 > >java程式設計思想----13,建立視窗和程式片

java程式設計思想----13,建立視窗和程式片

第十三章 建立視窗和程式片

 

在Java 1.0中,圖形使用者介面(GUI)庫最初的設計目標是讓程式設計師構建一個通用的GUI,使其在所有平臺上都能正常顯示。

但遺憾的是,這個目標並未達到。事實上,Java 1.0版的“抽象Windows工具包”(AWT)產生的是在各系統看來都同樣欠佳的圖形使用者介面。除此之外,它還限制我們只能使用四種字型,並且不能訪問作業系統中現有的高階GUI元素。同時,Jave1.0版的AWT程式設計模型也不是面向物件的,極不成熟。這類情況在Java1.1版的AWT事件模型中得到了很好的改進,例如:更加清晰、面向物件的程式設計、遵循Java Beans的範例,以及一個可輕鬆建立可視程式設計環境的程式設計元件模型。Java1.2為老的Java 1.0 AWT添加了Java基礎類(AWT),這是一個被稱為“Swing”的GUI的一部分。豐富的、易於使用和理解的Java Beans能經過拖放操作(像手工程式設計一樣的好),創建出能使程式設計師滿意的GUI。軟體業的“

3次修訂版”規則看來對於程式設計語言也是成立的(一個產品除非經過第3次修訂,否則不會盡如人意)。

Java的主要設計目的之一是建立程式片,也就是建立執行在WEB 瀏覽器上的小應用程式。由於它們必須是安全的,所以程式片在執行時必須加以限制。無論怎樣,它們都是支援客戶端程式設計的強有力的工具,一個重要的應用便是在Web上。

在一個程式片中程式設計會受到很多的限制,我們一般說它“在沙箱內”,這是由於Java執行時一直會有某個東西——即Java執行期安全系統——在監視著我們。Jave 1.1為程式片提供了數字簽名,所以可選出能信賴的程式片去訪問主機。不過,我們也能跳出沙箱的限制寫出可靠的程式。在這種情況下,我們可訪問作業系統中的其他功能。在這本書中我們自始至終編寫的都是可靠的程式,但它們成為了沒有圖形元件的控制檯程式。AWT也能用來為可靠的程式建立GUI介面。

在這一章中我們將先學習使用老的AWT工具,我們會與許多支援和使用AWT的程式碼程式樣本相遇。儘管這有一些困難,但卻是必須的,因為我們必須用老的AWT來維護和閱讀傳統的Java程式碼。有時甚至需要我們編寫AWT程式碼去支援不能從Java1.0升級的環境。在本章第二部分,我們將學習Java 1.1版中新的AWT結構並會看到它的事件模型是如此的優秀(如果能掌握的話,那麼在編制新的程式時就可使用這最新的工具。最後,我們將學習新的能像類庫一樣加入到Java 1.1版中的JFC/Swing元件,這意味著不需要升級到Java 1.2便能使用這一類庫。

大多數的例程都將展示程式片的建立,這並不僅僅是因為這非常的容易,更因為這是AWT的主要作用。另外,當用AWT建立一個可靠的程式時,我們將看到處理程式的不同之處,以及怎樣建立能在命令列和瀏覽器中執行的程式。

請注意的是這不是為了描述類的所有程式的綜合解釋。這一章將帶領我們從摘要開始。當我們查詢更復雜的內容時,請確定我們的資訊瀏覽器通過查詢類和方法來解決程式設計中的問題(如果我們正在使用一個開發環境,資訊瀏覽器也許是內建的;如果我們使用的是SUN公司的JDK則這時我們要使用WEB瀏覽器並在Java根目錄下面開始)。附錄F列出了用於深入學習庫知識的其他一些參考資料。

 

13.1 為何要用AWT

對於本章要學習的“老式”AWT,它最嚴重的缺點就是它無論在面向物件設計方面,還是在GUI開發包設計方面,都有不盡如人意的表現。它使我們回到了程式設計的黑暗年代(換成其他話就是“拙劣的”、“可怕的”、“惡劣的”等等)。必須為執行每一個事件編寫程式碼,包括在其他環境中利用“資源”即可輕鬆完成的一些任務。

許多象這樣的問題在Java 1.1裡都得到了緩解或排除,因為:

(1)Java 1.1的新型AWT是一個更好的程式設計模型,並向更好的庫設計邁出了可喜的一步。而Java Beans則是那個庫的框架。

(2)“GUI構建器”(可視程式設計環境)將適用於所有開發系統。在我們用圖形化工具將元件置入窗體的時候,Java Beans和新的AWT使GUI構建器能幫我們自動完成程式碼。其它元件技術如ActiveX等也將以相同的形式支援。

 

既然如此,為什麼還要學習使用老的AWT呢?原因很簡單,因為它的存在是個事實。就目前來說,這個事實對我們來說顯得有些不利,它涉及到面向物件庫設計的一個宗旨:一旦我們在庫中公佈一個元件,就再不能去掉它。如去掉它,就會損害別人已存在的程式碼。另外,當我們學習Java和所有使用老AWT的程式時,會發現有許多原來的程式碼使用的都是老式AWT。

AWT必須能與固有作業系統的GUI元件打交通,這意味著它需要執行一個程式片不可能做到的任務。一個不被信任的程式片在作業系統中不能作出任何直接呼叫,否則它會對使用者的機器做出不恰當的事情。一個不被信任的程式片不能訪問重要的功能。例如,“在螢幕上畫一個視窗”的唯一方法是通過呼叫擁有特殊介面和安全檢查的標準Java庫。Sun公司的原始模型建立的信任庫將僅僅供給Web瀏覽器中的Java系統信任關係自動授權器使用,自動授權器將控制怎樣進入到庫中去。

但當我們想增加作業系統中訪問新元件的功能時該怎麼辦?等待Sun來決定我們的擴充套件被合併到標準的Java庫中,但這不一定會解決我們的問題。Java 1.1版中的新模型是“信任程式碼”或“簽名程式碼”,因此一個特殊伺服器將校驗我們下載的、由規定的開發者使用的公共金鑰加密系統的程式碼。這樣我們就可知道程式碼從何而來,那真的是Bob的程式碼,還是由某人偽裝成Bob的程式碼。這並不能阻止Bob犯錯誤或作某些惡意的事,但能防止Bob逃避匿名製造計算機病毒的責任。一個數字簽名的程式片——“被信任的程式片”——在Java 1.1版能進入我們的機器並直接控制它,正像一些其它的應用程式從信任關係自動授權機中得到“信任”並安裝在我們的機器上。

這是老AWT的所有特點。老的AWT程式碼將一直存在,新的Java程式設計者在從舊的書本中學習時將會遇到老的AWT程式碼。同樣,老的AWT也是值得去學習的,例如在一個只有少量庫的例程設計中。老的AWT所包括的範圍在不考慮深度和列舉每一個程式和類,取而代之的是給了我們一個老AWT設計的概貌。

 

13.2 基本程式片

庫通常按照它們的功能來進行組合。一些庫,例如使用過的,便中斷擱置起來。標準的Java庫字串和向量類就是這樣的一個例子。其他的庫被特殊地設計,例如構建塊去建立其它的庫。庫中的某些類是應用程式的框架,其目的是協助我們構建應用程式,在提供類或類集的情況下產生每個特定應用程式的基本活動狀況。然後,為我們定製活動狀況,必須繼承應用程式類並且廢棄程式的權益。應用程式框架的預設控制結構將在特定的時間呼叫我們廢棄的程式。應用程式的框架是“分離、改變和中止事件”的好例子,因為它總是努力去嘗試集中在被廢棄的所有特殊程式段。

 

程式片利用應用程式框架來建立。我們從類中繼承程式片,並且廢棄特定的程式。大多數時間我們必須考慮一些不得不執行的使程式片在WEB頁面上建立和使用的重要方法。這些方法是:

 

591頁表

 

方法 作用

 

init() 程式片第一次被建立,初次執行初始化程式片時呼叫

start() 每當程式片進入Web瀏覽器中,並且允許程式片啟動它的常規操作時呼叫(特殊的程式片被stop()關閉);同樣在init()後呼叫

paint() 基礎類Component的一部分(繼承結構中上溯三級)。作為update()的一部分呼叫,以便對程式片的畫布進行特殊的描繪

stop() 每次程式片從Web瀏覽器的視線中離開時呼叫,使程式片能關閉代價高昂的操作;同樣在呼叫destroy()前呼叫

destroy() 程式片不再需要,將它從頁面中解除安裝時呼叫,以執行資源的最後清除工作

 

現在來看一看paint()方法。一旦Component(目前是程式片)決定自己需要更新,就會呼叫這個方法——可能是由於它再次迴轉螢幕,首次在螢幕上顯示,或者是由於其他視窗臨時覆蓋了你的Web瀏覽器。此時程式片會呼叫它的update()方法(在基礎類Component中定義),該方法會恢復一切該恢復的東西,而呼叫paint()正是這個過程的一部分。沒必要對paint()進行過載處理,但構建一個簡單的程式片無疑是方便的方法,所以我們首先從paint()方法開始。

update()呼叫paint()時,會向其傳遞指向Graphics物件的一個控制代碼,那個物件代表準備在上面描繪(作圖)的表面。這是非常重要的,因為我們受到專案元件的外觀的限制,因此不能畫到區域外,這可是一件好事,否則我們就會畫到線外去。在程式片的例子中,程式片的外觀就是這界定的區域。

圖形物件同樣有一系列我們可對其進行的操作。這些操作都與在畫布上作圖有關。所以其中的大部分都要涉及影象、幾何菜狀、圓弧等等的描繪(注意如果有興趣,可在Java文件中找到更詳細的說明)。有些方法允許我們畫出字元,而其中最常用的就是drawString()。對於它,需指出自己想描繪的String(字串),並指定它在程式片作圖區域的起點。這個位置用畫素表示,所以它在不同的機器上看起來是不同的,但至少是可以移植的。

根據這些資訊即可建立一個簡單的程式片:

 

592頁上程式

 

注意這個程式片不需要有一個main()。所有內容都封裝到應用程式框架中;我們將所有啟動程式碼都放在init()裡。

必須將這個程式放到一個Web頁中才能執行,而只能在支援Java的Web瀏覽器中才能看到此頁。為了將一個程式片置入Web頁,需要在那個Web頁的程式碼中設定一個特殊的標記(註釋①),以指示網頁裝載和執行程式片。這就是applet標記,它在Applet1中的樣子如下:

 

592頁下程式

 

①:本書假定讀者已掌握了HTML的基本知識。這些知識不難學習,有許多書籍和網上資源都可以提供幫助。

 

其中,code值指定了.class檔案的名字,程式片就駐留在那個檔案中。width和height指定這個程式片的初始尺寸(如前所述,以畫素為單位)。還可將另一些東西放入applet標記:用於在因特網上尋找其他.class檔案的位置(codebase)、對齊和排列資訊(align)、使程式片相互間能夠通訊的一個特殊識別符號(name)以及用於提供程式片能接收的資訊的引數。引數採取下述形式:

<Paramname=識別符號 value ="資訊">

可根據需要設定任意多個這樣的引數。

在簡單的程式片中,我們要做的唯一事情是按上述形式在Web頁中設定一個程式片標記(applet),令其裝載和執行程式片。

 

13.2.1 程式片的測試

我們可在不必建立網路連線的前提下進行一次簡單的測試,方法是啟動我們的Web瀏覽器,然後開啟包含了程式片標籤的HTML檔案(Sun公司的JDK同樣包括一個稱為“程式片觀察器”的工具,它能挑出html檔案的<applet>標記,並執行這個程式片,不必顯示周圍的HTML文字——註釋②)。html檔案載入後,瀏覽器會發現程式片的標籤,並查詢由code值指定的.class檔案。當然,它會先在CLASSPATH(類路徑)中尋找,如果在CLASSPATH下找不到類檔案,就在WEB瀏覽器狀態列給出一個錯誤資訊,告知不能找到.class檔案。

 

②;由於程式片觀察器會忽略除APPLET標記之外的任何東西,所以可將那些標記作為註釋置入Java原始碼:

// <applet code=MyApplet.class width=200 height=100></applet>

這樣就可直接執行“appletviewer MyApplet.java”,不必再建立小的HTML檔案來完成測試。

 

若想在Web站點上試驗,還會碰到另一些麻煩。首先,我們必須有一個Web站點,這對大多數人來說都意味著位於遠端地點的一家服務提供商(ISP)。然後必須通過某種途徑將HTML檔案和.class檔案從自己的站點移至ISP機器上正確的目錄(WWW目錄)。這一般是通過採用“檔案傳輸協議”(FTP)的程式來做成的,網上可找到許多這樣的免費程式。所以我們要做的全部事情似乎就是用FTP協議將檔案移至ISP的機器,然後用自己的瀏覽器連線網站和HTML檔案;假如程式片正確裝載和執行,就表明大功告成。但真是這樣嗎?

但這兒我們可能會受到愚弄。假如Web瀏覽器在伺服器上找不到.class檔案,就會在你的本地機器上搜尋CLASSPATH。所以程式片或許根本不能從伺服器上正確地裝載,但在你看來卻是一切正常的,因為瀏覽器在你的機器上找到了它需要的東西。但在其他人訪問時,他們的瀏覽器就無法找到那些類檔案。所以在測試時,必須確定已從自己的機器刪除了相關的.class檔案,以確保測試結果的真實。

我自己就遇到過這樣的一個問題。當時是將程式片置入一個package(包)中。上載了HTML檔案和程式片後,由於包名的問題,程式片的伺服器路徑似乎陷入了混亂。但是,我的瀏覽器在本地類路徑(CLASSPATH)中找到了它。這樣一來,我就成了能夠成功裝載程式片的唯一一個人。後來我花了一些時間才發現原來是package語句有誤。一般地,應該將package語句置於程式片的外部。

 

13.2.2 一個更圖形化的例子

這個程式不會太令人緊張,所以讓我們試著增加一些有趣的圖形元件。

 

594頁程式

 

這個程式用一個方框將字串包圍起來。當然,所有數字都是“硬編碼”的(指數字固定於程式內部),並以畫素為基礎。所以在一些機器上,框會正好將字串圍住;而在另一些機器上,也許根本看不見這個框,因為不同機器安裝的字型也會有所區別。

對Graphic類而言,可在幫助文件中找到另一些有趣的內容。大多數涉及圖形的活動都是很有趣的,所有我將更多的試驗留給讀者自己去進行。

 

13.2.3 框架方法的演示

觀看框架方法的實際運作是相當有趣的(這個例子只使用init(),start()和stop(),因為paint()和destroy()非常簡單,很容易就能掌握)。下面的程式片將跟蹤這些方法呼叫的次數,並用paint()將其顯示出來:

 

595頁程式

 

正常情況下,當我們過載一個方法時,需檢查自己是否需要呼叫方法的基礎類版本,這是十分重要的。例如,使用init()時可能需要呼叫super.init()。然而,Applet文件特別指出init()、start()和stop()在Applet中沒有用處,所以這裡不需要呼叫它們。

試驗這個程式片時,會發現假如最小化WEB瀏覽器,或者用另一個視窗將其覆蓋,那麼就不能再呼叫stop()和start()(這一行為會隨著不同的實現方案變化;可考慮將Web瀏覽器的行為同程式片觀察器的行為對照一下)。呼叫唯一發生的場合是在我們轉移到一個不同的Web頁,然後返回包含了程式片的那個頁時。

 

13.3 製作按鈕

製作一個按鈕非常簡單:只需要呼叫Button構建器,並指定想在按鈕上出現的標籤就行了(如果不想要標籤,亦可使用預設構建器,但那種情況極少出現)。可參照後面的程式為按鈕建立一個控制代碼,以便以後能夠引用它。

Button是一個元件,象它自己的小視窗一樣,會在更新時得以重繪。這意味著我們不必明確描繪一個按鈕或者其他任意種類的控制元件;只需將它們納入窗體,以後的描繪工作會由它們自行負責。所以為了將一個按鈕置入窗體,需要過載init()方法,而不是過載paint():

 

596頁程式

 

但這還不足以建立Button(或其他任何控制元件)。必須同時呼叫Applet add()方法,令按鈕放置在程式片的窗體中。這看起來似乎比實際簡單得多,因為對add()的呼叫實際會(間接地)決定將控制元件放在窗體的什麼地方。對窗體佈局的控制元件馬上就要講到。

 

13.4 捕獲事件

大家可注意到假如編譯和執行上面的程式片,按下按鈕後不會發生任何事情。必須進入程式片內部,編寫用於決定要發生什麼事情的程式碼。對於由事件驅動的程式設計,它的基本目標就是用程式碼捕獲發生的事件,並由程式碼對那些事件作出響應。事實上,GUI的大部分內容都是圍繞這種事件驅動的程式設計展開的。

經過本書前面的學習,大家應該有了面向物件程式設計的一些基礎,此時可能會想到應當有一些面向物件的方法來專門控制事件。例如,也許不得不繼承每個按鈕,並過載一些“按鈕按下”方法(儘管這顯得非常麻煩有有限)。大家也可能認為存在一些主控“事件”類,其中為希望響應的每個事件都包含了一個方法。

在物件以前,事件控制的典型方式是switch語句。每個事件都對應一個獨一無二的整數編號;而且在主事件控制方法中,需要專門為那個值寫一個switch。

Java 1.0的AWT沒有采用任何面向物件的手段。此外,它也沒有使用switch語句,沒有打算依靠那些分配給事件的數字。相反,我們必須建立if語句的一個巢狀系列。通過if語句,我們需要嘗試做的事情是偵測到作為事件“目標”的物件。換言之,那是我們關心的全部內容——假如某個按鈕是一個事件的目標,那麼它肯定是一次滑鼠點選,並要基於那個假設繼續下去。但是,事件裡也可能包含了其他資訊。例如,假如想調查一次滑鼠點選的畫素位置,以便畫一條引向那個位置的線,那麼Event物件裡就會包含那個位置的資訊(也要注意Java 1.0的元件只能產生有限種類的事件,而Java 1.1和Swing/JFC元件則可產生完整的一系列事件)。

Java 1.0版的AWT方法串聯的條件語句中存在action()方法的呼叫。雖然整個Java 1.0版的事件模型不相容Java 1.1版,但它在還不支援Java1.1版的機器和執行簡單的程式片的系統中更廣泛地使用,忠告您使用它會變得非常的舒適,包括對下面使用的action()程式方法而言。

action()擁有兩個自變數:第一個是事件的型別,包括所有的觸發呼叫action()的事件的有關資訊。例如滑鼠單擊、普通按鍵按下或釋放、特殊按鍵按下或釋放、滑鼠移動或者拖動、事件元件得到或丟失焦點,等等。第二個自變數通常是我們忽略的事件目標。第二個自變數封裝在事件目標中,所以它像一個自變數一樣的冗長。

需呼叫action()時情況非常有限:將控制元件置入窗體時,一些型別的控制元件(按鈕、複選框、下拉列表單、選單)會發生一種“標準行動”,從而隨相應的Event物件發起對action()的呼叫。比如對按鈕來說,一旦按鈕被按下,而且沒有再多按一次,就會呼叫它的action()方法。這種行為通常正是我們所希望的,因為這正是我們對一個按鈕正常觀感。但正如本章後面要講到的那樣,還可通過handleEvent()方法來處理其他許多型別的事件。

前面的例程可進行一些擴充套件,以便象下面這樣控制按鈕的點選:

 

598頁程式

 

為了解目標是什麼,需要向Event物件詢問它的target(目標)成員是什麼,然後用equals()方法檢查它是否與自己感興趣的目標物件控制代碼相符。為所有感興趣的物件寫好控制代碼後,必須在末尾的else語句中呼叫super.action(evt, arg)方法。我們在第7章已經說過(有關多形性的那一章),此時呼叫的是我們過載過的方法,而非它的基礎類版本。然而,基礎類版本也針對我們不感興趣的所有情況提供了相應的控制程式碼。除非明確進行,否則它們是不會得到呼叫的。返回值指出我們是否已經處理了它,所以假如確實與一個事件相符,就應返回true;否則就返回由基礎類event()返回的東西。

對這個例子來說,最簡單的行動就是打印出到底是什麼按鈕被按下。一些系統允許你彈出一個小訊息視窗,但Java程式片卻防礙視窗的彈出。不過我們可以用呼叫Applet方法的getAppletContext()來訪問瀏覽器,然後用showStatus()在瀏覽器視窗底部的狀態列上顯示一條資訊(註釋③)。還可用同樣的方法打印出對事件的一段完整說明文字,方法是呼叫getAppletConext().showStatus(evt + "")。空字串會強制編譯器將evt轉換成一個字串。這些報告對於測試和除錯特別有用,因為瀏覽器可能會覆蓋我們的訊息。

 

③:ShowStatus()也屬於Applet的一個方法,所以可直接呼叫它,不必呼叫getAppletContext()。

 

儘管看起來似乎很奇怪,但我們確實也能通過event()中的第二個引數將一個事件與按鈕上的文字相配。採用這種方法,上面的例子就變成了:

 

599頁程式

 

很難確切知道equals()方法在這兒要做什麼。這種方法有一個很大的問題,就是開始使用這個新技術的Java程式設計師至少需要花費一個受挫折的時期來在比較按鈕上的文字時發現他們要麼大寫了要麼寫錯了(我就有這種經驗)。同樣,如果我們改變了按鈕上的文字,程式程式碼將不再工作(但我們不會得到任何編譯時和執行時的資訊)。所以如果可能,我們就得避免使用這種方法。

 

13.5 文字欄位

“文字欄位”是允許使用者輸入和編輯文字的一種線性區域。文字欄位從文字元件那裡繼承了讓我們選擇文字、讓我們像得到字串一樣得到選擇的文字,得到或設定文字,設定文字欄位是否可編輯以及連同我們從線上參考書中找到的相關方法。下面的例子將證明文字欄位的其它功能;我們能注意到方法名是顯而易見的:

 

600-601頁程式

 

有幾種方法均可構建一個文字欄位;其中之一是提供一個初始字串,並設定字元域的大小。

按下按鈕1 是得到我們用滑鼠選擇的文字就是得到欄位內所有的文字並轉換成字串S。它也允許欄位被編輯。按下按鈕2 放一條資訊和字串s到Text fields,並且阻止欄位被編輯(儘管我們能夠一直選擇文字)。文字的可編輯性是通過setEditable()的真假值來控制的。

 

13.6 文字區域

“文字區域”很像文字欄位,只是它擁有更多的行以及一些引人注目的更多的功能。另外你能在給定位置對一個文字欄位追加、插入或者修改文字。這看起來對文字欄位有用的功能相當不錯,所以設法發現它設計的特性會產生一些困惑。我們可以認為如果我們處處需要“文字區域”的功能,那麼可以簡單地使用一個線型文字區域在我們將另外使用文字欄位的地方。在Java 1.0版中,當它們不是固定的時候我們也得到了一個文字區域的垂直和水平方向的滾動條。在Java 1.1版中,對高階構建器的修改允許我們選擇哪個滾動條是當前的。下面的例子演示的僅僅是在Java1.0版的狀況下滾動條一直開啟。在下一章裡我們將看到一個證明Java 1.1版中的文字區域的例程。

 

601-602頁程式

 

程式中有幾個不同的“文字區域”構建器,這其中的一個在此處顯示了一個初始字串和行號和列號。不同的按鈕顯示得到、追加、修改和插入文字。

 

13.7 標籤

標籤準確地運作:安放一個標籤到窗體上。這對沒有標籤的TextFields和Text areas 來說非常的重要,如果我們簡單地想安放文字的資訊在窗體上也能同樣的使用。我們能像本章中第一個例程中演示的那樣,使用drawString()裡邊的paint()在確定的位置去安置一個文字。當我們使用的標籤允許我們通過佈局管理加入其它的文字元件。(在這章的後面我們將進入討論。)

使用構建器我們能建立一條包括初始化文字的標籤(這是我們典型的作法),一個標籤包括一行CENTER(中間)、LEFT(左)和RIGHT(右)(靜態的結果取整定義在類標籤裡)。如果我們忘記了可以用getText()和getalignment()讀取值,我們同樣可以用setText()和setAlignment()來改變和調整。下面的例子將演示標籤的特點:

 

603-604頁程式

 

首先是標籤的最典型的用途:標記一個文字欄位或文字區域。在例程的第二部分,當我們按下“test 1”按鈕通過setText()將一串空的空格插入到的欄位裡。因為空的空格數不等於同樣的字元數(在一個等比例間隔的字型檔裡),當插入文字到標籤裡時我們會看到文字將被省略掉。在例子的第三部分保留的空的空格在我們第一次按下“test 2”會發現標籤是空的(trim()刪除了每個字串結尾部分的空格)並且在開頭的左列插入了一個短的標籤。在工作的其餘時間中我們按下按鈕進行調整,因此就能看到效果。

我們可能會認為我們可以建立一個空的標籤,然後用setText()安放文字在裡面。然而我們不能在一個空標籤內加入文字-這大概是因為空標籤沒有寬度-所以建立一個沒有文字的空標籤是沒有用處的。在上面的例子裡,“blank”標籤裡充滿空的空格,所以它足夠容納後面加入的文字。

同樣的,setAlignment()在我們用構建器建立的典型的文字標籤上沒有作用。這個標籤的寬度就是文字的寬度,所以不能對它進行任何的調整。但是,如果我們啟動一個長標籤,然後把它變成短的,我們就可以看到調整的效果。

這些導致事件連同它們最小化的尺寸被擠壓的狀況被程式片使用的預設佈局管理器所發現。有關佈局管理器的部分包含在本章的後面。

 

13.8 複選框

複選框提供一個製造單一選擇開關的方法;它包括一個小框和一個標籤。典型的複選框有一個小的“X”(或者它設定的其它型別)或是空的,這依靠專案是否被選擇來決定的。

我們會使用構建器正常地建立一個複選框,使用它的標籤來充當它的自變數。如果我們在建立複選框後想讀出或改變它,我們能夠獲取和設定它的狀態,同樣也能獲取和設定它的標籤。注意,複選框的大寫是與其它的控制相矛盾的。

無論何時一個複選框都可以設定和清除一個事件指令,我們可以捕捉同樣的方法做一個按鈕。在下面的例子裡使用一個文字區域列舉所有被選中的複選框:

 

605頁程式

 

trace()方法將選中的複選框名和當前狀態用appendText()傳送到文字區域中去,所以我們看到一個累積的被選中的複選框和它們的狀態的列表。

 

13.9 單選鈕

單選鈕在GUI程式設計中的概念來自於老式的電子管汽車收音機的機械按鈕:當我們按下一個按鈕時,其它的按鈕就會彈起。因此它允許我們強制從眾多選擇中作出單一選擇。

AWT沒有單獨的描述單選鈕的類;取而代之的是複用複選框。然而將複選框放在單選鈕組中(並且修改它的外形使它看起來不同於一般的複選框)我們必須使用一個特殊的構建器象一個自變數一樣的作用在checkboxGroup物件上。(我們同樣能在建立複選框後呼叫setCheckboxGroup()方法。)

一個複選框組沒有構建器的自變數;它存在的唯一理由就是聚集一些複選框到單選鈕組裡。一個複選框物件必須在我們試圖顯示單選鈕組之前將它的狀態設定成true,否則在執行時我們就會得到一個異常。如果我們設定超過一個的單選鈕為true,只有最後的一個能被設定成真。

這裡有個簡單的使用單選鈕的例子。注意我們可以像其它的元件一樣捕捉單選鈕的事件:

 

606-607頁程式

 

顯示的狀態是一個文字欄位在被使用。這個欄位被設定為不可編輯的,因為它只是用來顯示資料而不是收集。這演示了一個使用標籤的可取之道。注意欄位內的文字是由最早選擇的單選鈕“Radio button 2”初始化的。

我們可以在窗體中擁有相當多的複選框組。

 

13.10 下拉列表

下拉列表像一個單選鈕組,它是強制使用者從一組可實現的選擇中選擇一個物件的方法。而且,它是一個實現這點的相當簡潔的方法,也最易改變選擇而不至使使用者感到吃力(我們可以動態地改變單選鈕,但那種方法顯然不方便)。Java的選擇框不像Windows中的組合框可以讓我從列表中選擇或輸入自己的選擇。在一個選擇框中你只能從列表中選擇僅僅一個專案。在下面的例子裡,選擇框從一個確定輸入的數字開始,然後當按下一個按鈕時,新輸入的數字增加到框裡。你將可以看到選擇框的一些有趣的狀態:

 

607-608頁程式

 

文字字欄位中顯示的“selected index,"也就是當前選擇的專案的序列號,在事件中選擇的字串就像action()的第二個自變數的字串符描述的一樣好。

執行這個程式片時,請注意對Choice框大小的判斷:在windows裡,這個大小是在我們拉下列表時確定的。這意味著如果我們拉下列表,然後增加更多的專案到列表中,這專案將在那,但這個下拉列表不再接受(我們可以通過專案來滾動觀察——註釋④)。然而,如果我們在第一次拉下下拉列表前將所的專案裝入下拉列表,它的大小就會合適。當然,使用者在使用時希望看到整個的列表,所以會在下拉列表的狀態裡對增加專案到選擇框里加以特殊的限定。

 

④:這一行為顯然是一種錯誤,會Java以後的版本里解決。

 

13.11 列表框

列表框與選擇框有完全的不同,而不僅僅是當我們在啟用選擇框時的顯示不同,列表框固定在螢幕的指定位置不會改變。另外,一個列表框允許多個選擇:如果我們單擊在超過一個的專案上,未選擇的則表現為高亮度,我們可以選擇象我們想要的一樣的多。如果我們想察看專案列表,我們可以呼叫getSelectedItem()來產生一個被選擇的專案列表。要想從一個組裡刪除一個專案,我們必須再一次的單擊它。列表框,當然這裡有一個問題就是它預設的動作是雙擊而不是單擊。單擊從組中增加或刪除專案,雙擊呼叫action()。解決這個問題的方法是象下面的程式假設的一樣重新培訓我們的使用者。

 

609-610頁程式

 

按下按鈕時,按鈕增加專案到列表的頂部(因為addItem()的第二個自變數為零)。增加專案到列表框比到選擇框更加的合理,因為使用者期望去滾動一個列表框(因為這個原因,它有內建的滾動條)但使用者並不願意像在前面的例子裡不得不去計算怎樣才能滾動到要要的那個專案。

然而,呼叫action()的唯一方法就是通過雙擊。如果我們想監視使用者在我們的列表中的所作所為(尤其是單擊),我們必須提供一個可供選擇的方法。

 

13.11.1 handleEvent()

到目前為止,我們已使用了action(),現有另一種方法handleEvent()可對每一事件進行嘗試。當一個事件發生時,它總是針對單獨事件或發生在單獨的事件物件上。該物件的handleEvent()方法是自動呼叫的,並且是被handleEvent()建立並傳遞到handleEvent()裡。預設的handleEvent()(handleEvent()定義在元件裡,基礎類的所有控制元件都在AWT裡)將像我們以前一樣呼叫action()或其它同樣的方法去指明滑鼠的活動、鍵盤活動或者指明移動的焦點。我們將會在本章的後面部分看到。

如果其它的方法-特別是action()-不能滿足我們的需要怎麼辦呢?至於列表框,例如,如果我想捕捉滑鼠單擊,但action()只響應雙擊怎麼辦呢?這個解答是過載handleEvent(),畢竟它是從程式片中得到的,因此可以過載任何非確定的方法。當我們為程式片過載handleEvent()時,我們會得到所有的事件在它們傳送出去之前,所以我們不能假設“這裡有我的按鈕可做的事件,所以我們可以假設按鈕被按下了”從它被action()設為真值。在handleEvent()中按鈕擁有焦點且某人對它進行分配都是可能的。不論它合理與否,我們可測試這些事件並遵照handleEvent()來進行操作。

為了修改列表樣本,使它會響應滑鼠的單擊,在action()中按鈕測試將被過載,但程式碼會處理的列表將像下面的例子被移進handleEvent()中去:

 

611-612頁程式

 

這個例子同前面的例子相同除了增加了handleEvent()外簡直一模一樣。在程式中做了試驗來驗證是否列表框的選擇和非選擇存在。現在請記住,handleEvent()被程式片所過載,所以它能在窗體中任何存在,並且被其它的列表當成事件來處理。因此我們同樣必須通過試驗來觀察目標。(雖然在這個例子中,程式片中只有一個列表框所以我們能假設所有的列表框事件必須服務於列表框。這是一個不好的習慣,一旦其它的列表框加入,它就會變成程式中的一個缺陷。)如果列表框匹配一個我們感興趣的列表框,像前面的一樣的程式碼將按上面的策略來執行。注意handleEvent()的窗體與action()的相同:如果我們處理一個單獨的事件,將返回真值,但如果我們對其它的一些事件不感興趣,通過handleEvent()我們必須返回super.handleEvent()值。這便是程式的核心,如果我們不那樣做,其它的任何一個事件處理程式碼也不會被呼叫。例如,試註解在上面的程式碼中返回super.handleEvent(evt)的值。我們將發現action()沒有被呼叫,當然那不是我們想得到的。對action()和handlEvent()而言,最重要的是跟著上面例子中的格式,並且當我們自己不處理事件時一直返回基礎類的方法版本資訊。(在例子中我們將返回真值)。(幸運的是,這些型別的錯誤的僅屬於Java 1.0版,在本章後面將看到的新設計的Java 1.1消除了這些型別的錯誤。)

在windows裡,如果我們按下shift鍵,列表框自動允許我們做多個選擇。這非常的棒,因為它允許使用者做單個或多個的選擇而不是程式設計期間固定的。我們可能會認為我們變得更加的精明,並且當一個滑鼠單擊被evt.shiftdown()產生時如果shift鍵是按下的將執行我們自己的試驗程式。AWT的設計妨礙了我們-我們不得不去了解哪個專案被滑鼠點選時是否按下了shift鍵,所以我們能取消其餘部分所有的選擇並且只選擇那一個。不管怎樣,我們是不可能在Java 1.0版中做出來的。(Java 1.1將所有的滑鼠、鍵盤、焦點事件傳送到列表中,所以我們能夠完成它。)

 

13.12 佈局的控制

在Java裡該方法是安一個元件到一個窗體中去,它不同我們使用過的其它GUI系統。首先,它是全程式碼的;沒有控制安放元件的“資源”。其次,該方法的元件被安放到一個被“佈局管理器”控制的窗體中,由“佈局管理器”根據我們add()它們的決定來安放元件。大小,形狀,元件位置與其它系統的佈局管理器顯著的不同。另外,佈局管理器使我們的程式片或應用程式適合視窗的大小,所以,如果視窗的尺寸改變(例如,在HTML頁面的程式片指定的規格),元件的大小,形狀和位置都會改變。

程式片和幀類都是來源於包含和顯示元件的容器。(這個容器也是一個元件,所以它也能響應事件。)在容器中,呼叫setLayout()方法允許我選擇不同的佈局管理器。

在這節裡我們將探索不同的佈局管理器,並安放按鈕在它們之上。這裡沒有捕捉按鈕的事件,正好可以演示如何佈置這些按鈕。

 

13.12.1 FlowLayout

到目前為止,所有的程式片都被建立,看起來使用一些不可思議的內部邏輯來佈置它們的元件。那是因為程式使用一個預設的方式:FlowLayout。這個簡單的“Flow”的元件安裝在窗體中,從左到右,直到頂部的空格全部再移去一行,並繼續迴圈這些元件。

這裡有一個例子明確地(當然也是多餘地)設定一個程式片的佈局管理器去FlowLayout,然後在窗體中安放按鈕。我們將注意到FlowLayout元件使用它們本來的大小。例如一個按鈕將會變得和它的字串符一樣的大小。

 

614頁上程式

 

所有元件將在FlowLayout中被壓縮為它們的最小尺寸,所以我們可能會得到一些奇怪的狀態。例如,一個標籤會合適它自已的字串的尺寸,所以它會右對齊產生一個不變的顯示。

 

13.12.2 BorderLayout

佈局管理器有四邊和中間區域的概念。當我們增加一些事物到使用BorderLayout的面板上時我們必須使用add()方法將一個字串物件作為它的第一個自變數,並且字串必須指定(正確的大寫)“North”(上),“South”(下),“west”(左),“East”(右)或者“Center”。如果我們拼寫錯誤或沒有大寫,就會得到一個編譯時的錯誤,並且程式片不會像你所期望的那樣執行。幸運的是,我們會很快發現在Java 1.1中有了更多改進。

這是一個簡單的程式例子:

 

614-615頁程式

 

除了“Center”的每一個位置,當元素在其它空間內擴大到最大時,我們會把它壓縮到適合空間的最小尺寸。但是,“Center”擴大後只會佔據中心位置。

BorderLayout是應用程式和對話方塊的預設佈局管理器。

 

13.12.3 GridLayout

GridLayout允許我們建立一個元件表。新增那些元件時,它們會按從左到右、從上到下的順序在網格中排列。在構建器裡,需要指定自己希望的行、列數,它們將按正比例展開。

 

615頁下程式

 

在這個例子裡共有21個空位,但卻只有20個按鈕,最後的一個位置作留空處理;注意對GridLayout來說,並不存在什麼“均衡”處理。

 

13.12.4 CardLayout

CardLayout允許我們在更復雜的擁有真正的資料夾卡片與一條邊相遇的環境裡建立大致相同於“卡片式對話方塊”的佈局,我們必須壓下一個卡片使不同的對話方塊帶到前面來。在AWT裡不是這樣的:CardLayout是簡單的空的空格,我們可以自由地把新卡片帶到前面來。(JFC/Swing庫包括卡片式的窗格看起來非常的棒,且可以我們處理所有的細節。)

 

1. 聯合佈局(Combining layouts)

下面的例子聯合了更多的佈局型別,在最初只有一個佈局管理器被程式片或應用程式操作看起來相當的困難。這是事實,但如果我們建立更多的面板物件,每個面板都能擁有一個佈局管理器,並且像被整合到程式片或應用程式中一樣使用程式片或應用程式的佈局管理器。這就象下面程式中的一樣給了我們更多的靈活性:

 

616-617頁程式

 

這個例子首先會建立一種新型別的面板:BottonPanel(按鈕面板)。它包括一個單獨的按鈕,安放在BorderLayout的中央,那意味著它將充滿整個的面板。按鈕上的標籤將讓我們知道我們在CardLayout上的那個面板上。

在程式片裡,面板卡片上將存放卡片和佈局管理器CL因為CardLayout必須組成類,因為當我們需要處理卡片時我們需要訪問這些控制代碼。

這個程式片變成使用BorderLayout來取代它的預設FlowLayout,建立面板來容納三個按鈕(使用FlowLayout),並且這個面板安置在程式片末尾的“North”。卡片面板增加到程式片的“Center”裡,有效地佔據面板的其餘地方。

當我們增加BottonPanels(或者任何其它我們想要的元件)到卡片面板時,add()方法的第一個自變數不是“North”,“South”等等。相反的是,它是一個描述卡片的字串。如果我們想輕擊那張卡片使用字串,我們就可以使用,雖然這字串不會顯示在卡片的任何地方。使用的方法不是使用action();代之使用first()、next()和last()等方法。請檢視我們有關其它方法的檔案。

在Java中,使用的一些卡片式面板結構十分的重要,因為(我們將在後面看到)在程式片程式設計中使用的彈出式對話方塊是十分令人沮喪的。對於Java 1.0版的程式片而言,CardLayout是唯一有效的取得很多不同的“彈出式”的窗體。

 

13.12.5 GridBagLayout

很早以前,人們相信所有的恆星、行星、太陽及月亮都圍繞地球公轉。這是直觀的觀察。但後來天文學家變得更加的精明,他們開始跟蹤個別星體的移動,它們中的一些似乎有時在軌道上緩慢執行。因為天文學家知道所有的天體都圍繞地球公轉,天文學家花費了大量的時間來討論相關的方程式和理論去解釋天體物件的執行。當我們試圖用GridBagLayout來工作時,我們可以想像自己為一個早期的天文學家。基礎的條例是(公告:有趣的是設計者居然在太陽上(這可能是在天體圖中標錯了位置所致,譯者注))所有的天體都將遵守規則來執行。哥白尼日新說(又一次不顧嘲諷,發現太陽系內的所有的行星圍繞太陽公轉。)是使用網路圖來判斷佈局,這種方法使得程式設計師的工作變得簡單。直到這些增加到Java裡,我們忍耐(持續的冷嘲熱諷)西班牙的GridBagLayout和GridBagConstraints狂熱宗教。我們建議廢止GridBagLayout。取代它的是,使用其它的佈局管理器和特殊的在單個程式裡聯合幾個面板使用不同的佈局管理器的技術。我們的程式片看起來不會有什麼不同;至少不足以調整GridBagLayout限制的麻煩。對我而言,通過一個例子來討論它實在是令人頭痛(並且我不鼓勵這種庫設計)。相反,我建議您從閱讀Cornell和Horstmann撰寫的《核心Java》(第二版,Prentice-Hall出版社,1997年)開始。

在這範圍內還有其它的:在JFC/Swing庫裡有一個新的使用Smalltalk的受人歡迎的“Spring and Struts”佈局管理器並且它能顯著地減少GridBagLayout的需要。

 

13.13 action的替代品

正如早先指出的那樣,action()並不是我們對所有事進行分類後自動為handleEvent()呼叫的唯一方法。有三個其它的被呼叫的方法集,如果我們想捕捉某些型別的事件(鍵盤、滑鼠和焦點事件),因此我們不得不過載規定的方法。這些方法是定義在基礎類元件裡,所以他們幾乎在所有我們可能安放在窗體中的元件中都是有用的。然而,我們也注意到這種方法在Java 1.1版中是不被支援的,同樣儘管我們可能注意到繼承程式碼利用了這種方法,我們將會使用Java 1.1版的方法來代替(本章後面有詳細介紹)。

 

元件方法 何時呼叫

 

action(Event evt, Object what) 當典型的事件針對元件發生(例如,當按下一個按鈕或下拉列表專案被選中)時呼叫

keyDown(Event evt, int key) 當按鍵被按下,元件擁有焦點時呼叫。第二個自變數是按下的鍵並且是冗餘的是從evt.key處複製來的

keyup(Event evt, int key) 當按鍵被釋放,元件擁有焦點時呼叫

lostFocus(Event evt, Object what) 焦點從目標處移開時呼叫。通常,what是從evt.arg裡冗餘複製的

gotFocus(Event evt, Object what) 焦點移動到目標時呼叫

mouseDown(Event evt, int x,int y) 一個滑鼠按下存在於元件之上,在X,Y座標處時呼叫

mouseUp(Event evt, int x, int y) 一個滑鼠升起存在於元件之上時呼叫

mouseMove(Event evt, int x, int y) 當滑鼠在元件上移動時呼叫

mouseDrag(Event evt, int x, int y) 滑鼠在一次mouseDown事件發生後拖動。所有拖動事件都會報告給內部發生了mouseDown事件的那個元件,直到遇到一次mouseUp為止

mouseEnter(Event evt, int x, int y) 滑鼠從前不在元件上方,但目前在

mouseExit(Event evt, int x, int y) 滑鼠曾經位於元件上方,但目前不在

 

當我們處理特殊情況時——一個滑鼠事件,例如,它恰好是我們想得到的滑鼠事件存在的座標,我們將看到每個程式接收一個事件連同一些我們所需要的資訊。有趣的是,當元件的handleEvent()呼叫這些方法時(典型的事例),附加的自變數總是多餘的因為它們包含在事件物件裡。事實上,如果我們觀察component.handleEvent()的原始碼,我們能發現它顯然將增加的自變數抽出事件物件(這可能是考慮到在一些語言中無效率的編碼,但請記住Java的焦點是安全的,不必擔心。)試驗對我們表明這些事件事實上在被呼叫並且作為一個有趣的嘗試是值得建立一個過載每個方法的程式片,(action()的過載在本章的其它地方)當事件發生時顯示它們的相關資料。

這個例子同樣向我們展示了怎樣製造自己的按鈕物件,因為它是作為目標的所有事件權益來使用。我可能會首先(也是必須的)假設製造一個新的按鈕,我們從按鈕處繼承。但它並不能執行。取而代之的是,我們從畫布元件處(一個非常普通元件)繼承,並在其上不使用paint()方法畫出一個按鈕。正如我們所看到的,自從一些程式碼混入到畫按鈕中去,按鈕根本就不執行,這實在是太糟糕了。(如果您不相信我,試圖在例子中為畫布元件交換按鈕,請記住呼叫稱為super的基礎類構建器。我們會看到按鈕不會被畫出,事件也不會被處理。)

myButton類是明確說明的:它只和一個自動事件(AutoEvent)“父視窗”一起執行(父視窗不是一個基礎類,它是按鈕建立和存在的視窗。)。通過這個知識,myButton可能進入到父視窗並且處理它的文字欄位,必然就能將狀態資訊寫入到父視窗的欄位裡。當然這是一種非常有限的解決方法,myButton僅能在連結AutoEvent時被使用。這種程式碼有時稱為“高度結合”。但是,製造myButton更需要很多的不是為例子(和可能為我們將寫的一些程式片)擔保的努力。再者,請注意下面的程式碼使用了Java 1.1版不支援的API。

 

621-624頁程式

 

我們可以看到構建器使用利用自變數同名的方法,所以自變數被賦值,並且使用this來區分:

this.label = label;

paint()方法由簡單的開始:它用按鈕的顏色填充了一個“圓角矩形”,然後畫了一個黑線圍繞它。請注意size()的使用決定了元件的寬度和長度(當然,是畫素)。這之後,paint()看起來非常的複雜,因為有大量的預測去計算出怎樣利用“font metrics”集中按鈕的標籤到按鈕裡。我們能得到一個相當好的關於繼續關注方法呼叫的主意,它將程式中那些相當平凡的程式碼挑出,當我們想集中一個標籤到一些元件裡時,我們正好可以對它進行剪下和貼上。

您直到注意到AutoEvent類才能正確地理解keyDown(),keyUp()及其它方法的執行。這包含一個Hashtable(譯者注:散列表)去控制字串來描述關於事件處理的事件和TextField型別。當然,這些能被靜態的建立而不是放入Hashtable但我認為您會同意它是更容易使用和改變的。特別是,如果我們需要在AutoEvent中增加或刪除一個新的事件型別,我們只需要簡單地在事件列隊中增加或刪除一個字串——所有的工作都自動地完成了。

我們查出在keyDown(),keyup()及其它方法中的字串的位置回到myButton中。這些方法中的任何一個都用父控制代碼試圖回到父視窗。父類是一個AutoEvent,它包含Hashtable h和get()方法,當擁有特定的字串時,將對一個我們知道的TextField物件產生一個控制代碼(因此它被選派到那)。然後事件物件修改顯示在TextField中的字串陳述。從我們可以真正注意到舉出的例子在我們的程式中執行事件時以來,可以發現這個例子執行起來頗為有趣的。

 

13.14 程式片的侷限

出於安全緣故,程式片十分受到限制,並且有很多的事我們都不能做。您一般會問:程式片看起來能做什麼,傳聞它又能做什麼:擴充套件瀏覽器中WEB頁的功能。自從作為一個網上衝浪者,我們從未真正想了解是否一個WEB頁來自友好的或者不友好的站點,我們想要一些可以安全地行動的程式碼。所以我們可能會注意到大量的限制:

(1) 一個程式片不能接觸到本地的磁碟。這意味著不能在本地磁碟上寫和讀,我們不想一個程式片通過WEB頁面閱讀和傳送重要的資訊。寫是被禁止的,當然,因為那將會引起病毒的侵入。當數字簽名生效時,這些限制會被解除。

(2) 程式片不能擁有選單。(注意:這是規定在Swing中的)這可能會減少關於安全和關於程式簡化的麻煩。我們可能會接到有關程式片協調利益以作為WEB頁面的一部分的通知;而我們通常不去注意程式片的範圍。這兒沒有幀和標題條從選單處彈出,出現的幀和標題條是屬於WEB瀏覽器的。也許將來設計能被改變成允許我們將瀏覽器選單和程式片選單相結合起來——程式片可以影響它的環境將導致太危及整個系統的安全並使程式片過於的複雜。

(3) 對話方塊是不被信任的。在Java中,對話方塊存在一些令人難解的地方。首先,它們不能正確地拒絕程式片,這實在是令人沮喪。如果我們從程式片彈出一個對話方塊,我們會在對話方塊上看到一個附上的訊息框“不被信任的程式片”。這是因為在理論上,它有可能欺騙使用者去考慮他們在通過WEB同一個老顧客的本地應用程式交易並且讓他們輸入他們的信用卡號。在看到AWT開發的那種GUI後,我們可能會難過地相信任何人都會被那種方法所愚弄。但程式片是一直附著在一個Web頁面上的,並可以在瀏覽器中看到,而對話方塊沒有這種依附關係,所以理論上是可能的。因此,我們很少會見到一個使用對話方塊的程式片。

在較新的瀏覽器中,對受到信任的程式片來說,許多限制都被放寬了(受信任程式片由一個信任源認證)。

涉及程式片的開發時,還有另一些問題需要考慮:

■程式片不停地從一個適合不同類的單獨的伺服器上下載。我們的瀏覽器能夠快取程式片,但這沒有保證。在Java 1.1版中的一個改進是JAR(Java ARchive)檔案,它允許將所有的程式片元件(包括其它的類檔案、影象、聲音)一起打包到一個的能被單個伺服器處理下載的壓縮檔案。“數字簽字”(能校驗類建立器)可有效地加入每個單獨的JAR檔案。

■因為安全方面的緣故,我們做某些工作更加困難,例如訪問資料庫和傳送電子郵件。另外,安全限制規則使訪問多個主機變得非常的困難,因為每一件事都必須通過WEB伺服器路由,形成一個性能瓶頸,並且單一環節的出錯都會導致整個處理的停止。

■瀏覽器裡的程式片不會擁有同樣的本地應用程式執行的控制元件型別。例如,自從使用者可以開關頁面以來,在程式片中不會擁有一個形式上的對話方塊。當用戶對一個WEB頁面進行改變或退出瀏覽器時,對我們的程式片而言簡直是一場災難——這時沒有辦法儲存狀態,所以如果我們在處理和操作中時,資訊會被丟失。另外,當我們離開一個WEB頁面時,不同的瀏覽器會對我們的程式片做不同的操作,因此結果本來就是不確定的。

 

13.14.1 程式片的優點

如果能容忍那些限制,那麼程式片的一些優點也是非常突出的,尤其是在我們構建客戶/伺服器應用或者其它網路應用時:

■沒有安裝方面的爭議。程式片擁有真正的平臺獨立性(包括容易地播放聲音檔案等能力)所以我們不需要針對不同的平臺修改程式碼也不需要任何人根據安裝執行任何的“tweaking”。事實上,安裝每次自動地將WEB頁連同程式片一起,因此安靜、自動地更新。在傳統的客戶機/伺服器系統中,建立和安裝一個新版本的客戶端軟體簡直就是一場惡夢。

■因為安全的原因建立在核心Java語言和程式片結構中,我們不必擔心壞的程式碼而導致毀壞某人的系統。這樣,連同前面的優點,可使用Java(可從JavaScript和VBScript中選擇客戶端的WEB程式設計工具)為所謂的Intrant(在公司內部使用而不向Internet轉移的企業內部網路)客戶機/伺服器開發應用程式。

■由於程式片是自動同HTML整合的,所以我們有一個內建的獨立平臺檔案系統去支援程式片。這是一個很有趣的方法,因為我們慣於擁有程式檔案的一部分而不是相反的擁有檔案系統。

 

13.15 視窗化應用

出於安全的緣故,我們會看到在程式片我們的行為非常的受到限制。我們真實地感到,程式片是被臨時地加入在WEB瀏覽器中的,因此,它的功能連同它的相關知識,控制元件都