Mustang(也稱作Java SE 6),如今剛進入其第二個測試階段。本文作者將同你進一步探討這個新的發行版本中所提供的許多新的特征(從控制台I/O和存取權限控制方法到系統托盤API和表格排序和過濾等)。
一、引言
Mustang(也稱作Java SE 6),如今剛進入其第二個測試階段。本文中讓我們一起進一步探討這個新的發行版本中所提供的許多新的特征(從控制台I/O和存取權限控制方法到系統托盤API和表格排序和過濾等)。
在分析控制文件和目錄存取許可的若干新的文件方法後,本文將向你展示新的桌面集成API。然後,本文還要分析Mustang的新的編程存取網絡參數的能力。最後,本文將討論表格組件的新的排序與過濾能力。
【注意】我使用Java SE 6的第二個測試版本(build 86)開發並測試了本文的Java示例應用程序,OS平台是Windows 98 SE。
二、存取權限控制方法
從某種角度看,File類的一個實例其實是一個標識文件系統中文件或目錄對象的抽象路徑名。文件系統可以限制在這個對象上實現的讀、寫以及執行等操作。
讀、寫和執行限制統稱為“存取權限”。文件系統可以把多個存取權限集合關聯到單個對象。例如,一個集合可以用於對象的所有者而另一個集合可以用於所有的其他用戶。
前一個版本中提供的存取權限在直接用於存取對象時,有可能會導致File類的一些方法失敗。由於這個原因,Mustang為File類引入了六種新的方法以便讓你修改路徑名的存取權限:
①“public boolean setExecutable(boolean executable, boolean ownerOnly)”:設置所有者或每個人對於指定抽象路徑名的執行許可權。當executable為true時,允許執行操作;而傳遞給它的值為false時,則不允許執行。把true傳遞給參數ownerOnly僅允許該抽象路徑名的所有者擁有該許可權;當ownerOnly為false,則把該許可權授予每個人。如果底層文件系統無法區分所有者的執行許可與每個人的執行許可,那麼,該許可應用於每個人,而不管ownerOnly取值如何。
該方法在成功時返回true;否則,返回false。如果用戶無權改變抽象路徑名的存取權限或如果底層文件系統沒有實現一種執行許可並且executable為false,則方法調用失敗。
②“public boolean setExecutable(boolean executable)”:這個方法便於設置所有者對於給定抽象路徑名的執行權限。
③public oolean setReadable( oolean readable, oolean ownerOnly)”:設置所有者或每個人對於這個抽象路徑名的讀取許可權。參數readable為true時允許讀取操作;否則,不允許讀取。參數ownerOnly為true時僅允許該抽象路徑名的所有者擁有該許可權;當ownerOnly為false,則把該許可權授予每個人。如果底層文件系統無法區分所有者的讀取許可與每個人的讀取許可,那麼,該許可應用於每個人,而不管ownerOnly取值如何。
該方法在成功時返回true;否則,返回false。如果用戶無權改變抽象路徑名的存取權限或如果底層文件系統沒有實現一種讀取許可並且readable為false,則方法調用會失敗。
④“public boolean setReadable(boolean readable)”:這個方法便於設置所有者對於給定抽象路徑名的讀取權限。
⑤“public boolean setWritable(boolean writable,boolean ownerOnly)”:設置所有者或每個人對於這個抽象路徑名的寫許可權。參數writable為true時允許寫操作;否則,不允許寫操作。參數ownerOnly為true時僅允許該抽象路徑名的所有者擁有該許可權;當ownerOnly為false,則把該許可權授予每個人。如果底層文件系統無法區分所有者的寫許可與每個人的寫許可,那麼,該許可應用於每個人,而不管ownerOnly取值如何。
該方法在成功時這個方法返回true;否則,返回false。如果用戶無權改變抽象路徑名的存取權限,則方法調用會失敗。
⑥“public boolean setWritable(boolean writable)”:這個方法便於設置所有者對於給定抽象路徑名的寫權限。
【注意】如果存在一個安全管理器並且它的“public void checkWrite(String file)”方法不允許對文件進行寫操作的話,則上面列出的每一個方法都會拋出一個SecurityException異常。
File類還提供了如下對應的方法以幫助你獲得一個對象當前設置的讀、寫和執行權限:
①public boolean canRead();
②public boolean canWrite();
③public boolean canExecute()(在Mustang中新引入的)。
我創建了一個簡單的展示setWritable()和canWrite()方法用法的名為WritableDemo的應用程序。在這個應用程序中,你能夠使一個文件系統對象可寫或僅能讀取,而且還能查看這一許可的當前設置。本文示例源碼中的列表1完整地展示了這個文件WritableDemo.java(略)。
三、桌面集成
Sun的Java桌面開發小組引入了若干新的特征以進一步提高Java在桌面開發領域的影響。其中三個著名的特征是:Splash屏幕支持(它讓應用程序在啟動過程中顯示Splash屏幕),系統托盤支持(它讓應用程序把圖標,提示窗信息和彈出菜單添加到系統托盤),和一組新的桌面API。
現在,我們來討論桌面API,它有助於無縫地把Java應用程序與桌面集成到一起。該API支持Java應用程序使用一個特定的統一資源標識符(URI)啟動操作系統的缺省的浏覽器;啟動OS的缺省的電子郵件客戶端;以及啟動應用程序以打開、編輯或打印與該應用程序相關聯的文件。
桌面API使用OS的文件關聯機制來啟動關聯到特定的文件類型的應用程序。例如,.doc文件擴展經常與微軟的Word關聯。經由桌面API,一個Java應用程序能夠啟動Word以打開、打印或編輯與這個擴展名相關聯的文件。
在啟動浏覽器電子郵件客戶端或任何應用程序之前,你的Java應用程序必須決定你的OS是否支持該API。這一決定是通過調用java.awt.Desktop類的“public static boolean isDesktopSupported()”方法實現的。如果OS支持該API,這個方法返回true;否則,它返回false。
在調用isDesktopSupported()之後,該應用程序通過調用Desktop的“public static Desktop getDesktop()”方法繼續檢索Desktop對象。如果OS不支持鍵盤輸入、顯示器或鼠標,這個方法將拋出一個java.awt.Headless異常。如果OS不支持該桌面API,則拋出一個UnsupportedOperationException異常。
現在,既然該Java應用程序已經擁有了一個桌面實例,那麼,按下來,它就能夠調用各種方法以浏覽、發送郵件、打開、編輯或打印。在執行任何這些操作之前,該程序可以調用Desktop的“public boolean isSupported(Desktop.Action action)”方法,如果桌面支持該行為(被描述為適合的Desktop.Action枚舉實例),則這個方法返回true。這些Desktop.Action枚舉如下:
· BROWSE:這個枚舉實例描述OS的缺省浏覽器的浏覽行為。
· MAIL:這個枚舉實例描述OS的缺省電子郵件客戶端的郵件行為。
· OPEN:這個枚舉實例描述與打開一個特定的文件類型相關聯的一個應用程序執行的打開行為。
· EDIT:這個枚舉實例描述與編輯一個特定的文件類型相關聯的一個應用程序執行的編輯行為。
· PRINT:這個枚舉實例描述與打印一個特定的文件類型相關聯的一個應用程序執行的打印行為。
【注意】在調用相應行為的Desktop方法前,你不必調用“isSupported(Desktop.Action action)”來決定是否支持該行為:你可以直接調用相應的方法,但是之後,你必須處理該方法潛在地拋出的一個UnsupportedOperationException異常。可以從Desktop存取下列行為方法:
①“public void browse(URI uri)”:啟動用戶缺省的浏覽器以顯示一個URI—如果浏覽器能夠處理這個URI的話;否則,它啟動該URI缺省的處理器應用程序(這具體要依賴於在java.net.URI類中定義的協議和路徑)。
如果uri為null,則拋出一個NullPointerException異常。如果用戶的缺省浏覽器沒有找到或它沒能啟動或缺省的處理器應用程序沒能啟動,則拋出一個java.io.IOException異常。
②“public void edit(File file)”:啟動相關聯的編輯器應用程序並且打開一個文件進行編輯。
如果file為null,則拋出一個NullPointerException異常。如果指定的文件不存在,則拋出一個IllegalArgumentException異常。最後,如果指定的文件相關聯的應用程序沒能啟動,或這個文件沒有相關聯的編輯器,則拋出一個IOException異常。
③“public void mail()”:啟動用戶缺省的電子郵件客戶端的郵件編輯窗口。
如果用戶缺省的電子郵件客戶端沒有發現或啟動失敗,則拋出一個IOException異常。
④“public void mail(URI mailtoURI)”:啟動用戶缺省的電子郵件客戶端的郵件編輯窗口,填充由一個“mailto:”URI指定的消息域。這個URI能夠指定包括“cc”,“subject”和“body”在內的各種消息域。
如果mailtoURI為null,則拋出一個NullPointerException異常。如果URI的模式不是mailto,則拋出一個IllegalArgumentException異常。如果用戶缺省的電子郵件客戶端沒有發現或啟動失敗,則拋出一個IOException異常。
⑤“public void open(File file)”:啟動相關聯的應用程序以打開該文件。如果指定的文件是一個目錄,則啟動OS的文件管理器以打開它。
如果file為 null,則拋出一個NullPointerException異常。如果指定的文件不存在,則拋出一個IllegalArgumentException異常。最後,如果該指定的文件沒有相關聯的應用程序,或如果這個應用程序沒能啟動,則拋出一個IOException異常。
⑥“public void print(File file)”:使用相關聯的應用程序的打印命令並使用本地桌面打印設備打印一個文件。
如果file為null,則拋出一個NullPointerException異常。如果指定的文件不存在,則拋出一個IllegalArgumentException異常。如果指定的文件沒有相關聯的能夠用於打印其內容的應用程序,則拋出一個IOException異常。
【注意】如果一個安全管理器存在並且不允許執行要求的行為的話,上面列出的每一個方法都會拋出一個SecurityException異常。
為此,我創建了一個展示桌面API用法的DesktopDemo應用程序。這個應用程序創建了一個包含一系列浏覽,郵件,打開,編輯,和打印等單選按鈕的GUI界面;還有一個文本域用於輸入一個URI或文件名。在輸入一個URI或文件名之後,點擊適當的按鈕便可以從桌面上啟動一個應用程序。本文示例源碼中的列表2展示了這個文件DesktopDemo.java(略)。
在編譯和運行DesktopDemo後,在文本域輸入一個文件名並點擊Open,Edit或Print中的一個按鈕以啟動該文件相應的應用程序。或輸入一個URI並點擊“Browse”或“Mail”。圖1展示了我已經在文本域中輸入了一個郵件URI的屏幕快照。
圖1.在此,當輸入一個郵件URI時,我沒有指定“mailto:”前綴,因為我的電子郵件客戶端把這個前綴自動地加入到郵件編輯窗口中的接收者的電子郵件地址的前面。
這個郵件URI僅包含接收者的電子郵件地址和一個主題。當然,我也可以包括正文文本,這可以通過添加“&BODY=”,其後面再跟著一些文本的方法實現。在點擊“Mail”單選按鈕後,我的電子郵件客戶端的郵件編輯窗口中將在正確位置顯示接收者的電子郵件地址和主題,如圖2所示。
圖2.這裡所提供的郵件URI還能夠指定除主要接收者的電子郵件地址外的其它郵件地址。
四、以編程方式存取網絡參數
Mustang提供了以編程方式存取網絡參數的方法—這是通過在java.net.NetworkInterface類和新的java.net.InterfaceAddress類中提供的10個新的方法實現的。這些新引入的網絡接口方法列舉如下:
①“public byte[] getHardwareAddress()”:以一個字節數組形式返回這個網絡接口的硬件地址(通常是機器地址代碼,或MAC—也稱作以太網地址)。如果這個接口沒有一個硬件地址,或如果不能存取這個地址(因為該用戶沒有足夠的權限),則返回null。如果發生一個I/O錯誤,則拋出一個java.net.SocketException異常。
②“public List<InterfaceAddress> getInterfaceAddresses()”:返回一個java.util.List,它包含這個網絡接口的所有接口地址(作為InterfaceAddress實例)或其中一個子集。如果存在一個安全管理器,那麼將使用相應於每一個接口地址的java.net.InetAddress來調用它的一個checkConnect方法。注意,這個方法僅在列表中返回InterfaceAddresses;而且此時,checkConnect並不拋出一個SecurityException異常。
③“public int getMTU()”:返回這個網絡接口的最大傳輸單位(MTU)。該MTU指的是一個通訊協議層能夠傳遞到另一個層的最大包的大小(以字節為單位)。例如,以太網允許的最大MTU是1500字節。根據某一標准(例如以太網)或連接時間(在端到端串行連接時,經常有這種情況),該MTU能夠被設置為一個固定長度。如果發生一個I/O錯誤,則拋出一個SocketException異常。
④“public NetworkInterface getParent()”:如果這個網絡接口是一個子接口,則返回這個網絡接口的父網絡接口(作為一個NetworkInterface實例)。然而,如果這個網絡接口是一個物理(非虛擬的)接口或如果這個網絡接口是虛擬的並且沒有父接口,則返回null。
⑤“public Enumeration<NetworkInterface> getSubInterfaces()”:返回一個包含所有的子接口(也稱為虛擬接口)的java.util.Enumeration(作為NetworkInterface的實例)。例如,eth0:1是一個eth0的子接口(一個以太網網絡接口名)。
⑥“public boolean isLoopback()”:返回true,如果這個網絡接口是一個loopback接口(一種網絡接口,在該接口中,外發數據作為輸入數據被立即反射回該接口)。如果存在一個I/O問題,則拋出一個SocketException異常。
⑦“public boolean isPointToPoint()”:返回true,如果這個網絡接口是一個端到端的接口(例如一個通過調制解調器建立的PPP連接)。如果存在一個I/O問題,則拋出一個SocketException異常。
⑧“public boolean isUp()”:返回true,如果這個網絡接口是“up”並且已經“running”。“up”指示已經為這個接口建立起了路由入口。“running”指示要求的系統資源已經分配。如果存在一個I/O問題,則拋出一個SocketException異常。
⑨“public boolean isVirtual()”:返回true,如果這個網絡接口是一個虛擬接口(也稱作“子接口”)。如果存在一個I/O問題,則拋出一個SocketException異常。
⑩“public boolean supportsMulticast()”:返回true,如果這個網絡接口支持多點傳送(指一個服務器程序把一個消息的一個副本發送給多個客戶端程序的能力)。如果存在一個I/O問題,則拋出一個SocketException異常。
InterfaceAddress類描述了一個網絡接口地址。除了通常的實現獲得一個哈希代碼和獲得一個字符串描述等方法之外,這個類還提供了下列三個方法:
· “public InetAddress getAddress()”:返回給定接口地址:一個IP地址,一個子網掩碼和一個廣播地址(當該地址是IPv4時);或是一個IP地址和一個網絡前綴長度(針對一個IPv6地址)。
· “public InetAddress getBroadcast()”:返回給定接口地址的廣播地址(在IPv4網絡中作為一個InetAddress);在IPv6網絡中則返回null(此時它沒有廣播地址)。
· “public short getNetworkPrefixLength()”:返回給定接口地址的針對IPv6網絡的網絡前綴長度(或針對IPv4網絡的子網掩碼)。這個方法的返回值為一個短整型。
【注意】 典型的IPv6值是128(::1/128)或10(fe80::203:baff:fe27:1243/10)。針對IPv4的典型的值是8(255.0.0.0),16(255.255.0.0),或24(255.255.255.0)。
我創建了一個簡單的NetParmsDemo應用程序,它展示了NetworkInterface和InterfaceAddress類中提供的許多新的方法。列表3描述了這個應用程序的源代碼(見源碼文件中的NetParmsDemo.java)。
五、表格排序與過濾
Swing的表格組件在若干方面得到了增強。其中的一個改進是,支持對一個表格中的行數據進行按升/降序排序並且能夠過濾掉其中某些行(所有數據來自於表格模型),並最終顯示在組件的視圖中。
【請記住】排序和過濾僅對視圖有影響,而對模型無影響。
排序和過濾基於一個新概念—行排序器對象,它能夠對行數據進行排序(和過濾)。把一個行排序器加入到一個表格組件中的最簡單的方法是調用javax.swing.JTable中新引入的“public void setAutoCreateRowSorter(boolean autoCreateRowSorter)”方法,下面的代碼片斷演示了它的用法:
TableModel model = createTableModel ();
JTable table = new JTable (model);
table.setAutoCreateRowSorter (true);
在每次改變模型時,把true傳遞給setAutoCreateRowSorter()能夠使JTable安裝一個新的javax.swing.Table.TableRowSorter<M>實例作為行排序器。為了防止在以後改變模型時再創建新的行排序器,可以把false傳給一個隨後調用的方法。
注意,當你不想定制行排序器時,你也有可能調用setAutoCreateRowSorter()。但是,在調用這個方法後,你仍然能夠定制行排序器,這是通過首先調用JTable的新的“public RowSorter<? extends TableModel> getRowSorter()”方法以返回當前行排序器來實現的。
因為當你試圖把返回的行排序器的引用存儲到一個TableRowSorter時編譯器會顯示一個未檢查的警告消息,所以,你可能更喜歡由你自己創建表格行排序器並使用JTable的新的“public void setRowSorter(RowSorter<? extends TableModel> sorter)”方法來安裝它:
TableRowSorter<TableModel> sorter;
sorter = new TableRowSorter<TableModel> (model);
table.setRowSorter (sorter);
對TableRowSorter的定制還包括能夠使用它的“public void setRowFilter(RowFilter<? super M,? super I> filter)”方法安裝一個行過濾對象(它基於某個標准接收行數據)。這個方法接收一個javax.swing.RowFilter<M,I>參數,其相應的方法能夠返回不同種類的行過濾器。
有些行過濾器可以使用正規表達式。為了獲得這種行過濾器,可以調用“RowFilter public static <M,I> RowFilter<M,I> regexFilter(String regex, int... indices)”方法。例如,“sorter.setRowFilter (RowFilter.regexFilter ("^A"));”語句能夠創建一個行過濾器,它的“^A”正規表達式僅接受以A開始的行。
JTable還提供了其它一些與排序和行過濾有關的新方法。這其中的兩個是:“public int convertRowIndexToModel(int viewRowIndex)”和“public int convertRowIndexToView(int modelRowIndex)”,它們分別負責把一個行的索引(根據模型)映射到視圖和把一個行的索引(根據視圖)映射到模型。
為了向你說明僅是視圖為排序和過濾所影響,我使用了前面的TableSortFilterDemo演示應用程序中的一個“convert”方法。在改變行過濾器以後,除了把null傳遞給TableRowSorter的“public void setSortKeys(List<? extends RowSorter.SortKey> sortKeys)”方法以打亂視圖的排序外,列表4(見本文示例源程序)中其它的內容我們都已經討論過。
在編譯和運行這個應用程序後,通過點擊某一列的列頭部初始化一個排序。作為響應,所有的行按被點擊的列值重新升序或降序排列(每次點擊使之在升序與降序之間切換)。選擇的列和排序順序以相應列頭部的一個向上/向下的箭頭指示,如圖3所示。
圖3.一個向上箭頭表示現在是按升序排序。
除了排序之外,你還能夠安裝一個過濾器以決定在視圖中顯示哪些行。為此,只要在文本域中輸入一個正規表達式(例如^J或J),並且點擊“Set Filter”按鈕即可。作為響應,所有的匹配該正規表達式的行都被以非排序方式顯示(見圖4)。然後,你可以再對這些行進行排序。
圖4.點擊“Set Filter”對過濾的行建立一個未排序的視圖。
六、結論
除了上面所討論的新特征之外,Mustang還提供一個編譯器API,一個腳本庫,一個Java數據庫,在JDBC方面也作了新的改進,XML數字簽名,更好的國際化,等功能。其它特征還有待於讀者自己去探討。