Java 6.0標(biāo)準(zhǔn)版(Mustang)包含了大量使Java開發(fā)更為容易的特性。在本文中,我們將討論通過部分新特性來幫助你實(shí)現(xiàn)如下功能:
· 設(shè)置文件和目錄許可權(quán)
· 獲取分區(qū)上自由空間和可用空間數(shù)
· 把Component對(duì)象添加到JTabbedPane的選項(xiàng)卡上
· 在你的Java基礎(chǔ)類/Swing(JFC/Swing)應(yīng)用程序中使用流行的SwingWorker類
因此,如果JSR 270專家組同意采納這些特征,那么在Mustang的下一個(gè)發(fā)行版本中你就會(huì)看到這些特征。
注意:為了運(yùn)行本文中的源碼,你必須下載并安裝Mustang的最新版本。
一、 設(shè)置文件和目錄權(quán)限 現(xiàn)在,從Mustang build 31開始,你可以在本地文件系統(tǒng)中設(shè)置一個(gè)文件的可讀、可寫和可執(zhí)行標(biāo)志。這項(xiàng)功能已經(jīng)被添加到j(luò)ava.io.File類中,并通過使用下列方法來實(shí)現(xiàn):
public boolean setReadable(boolean readable, boolean ownerOnly) public boolean setReadable(boolean readable) public boolean setWritable(boolean writable, boolean ownerOnly) public boolean setWritable(boolean writable) public boolean setExecutable(boolean executable, boolean ownerOnly) public boolean setExecutable(boolean executable) |
如果你曾某種UNIX系統(tǒng)上工作過,那么你應(yīng)該對(duì)這些方法非常熟悉-其實(shí)它們實(shí)現(xiàn)了chmod命令的一些功能。這些方法試圖設(shè)置由現(xiàn)在的File對(duì)象所描述的文件或目錄的適當(dāng)權(quán)限。如果把第二個(gè)可選參數(shù)設(shè)置為true,那么該權(quán)限將僅應(yīng)用于當(dāng)前所有者標(biāo)志。否則,這些方法將應(yīng)用到所有用戶。注意,如果底層文件系統(tǒng)沒法區(qū)分該所有者和其他所有者的權(quán)限(在一些版本的Windows中就是這樣),那么這一權(quán)限將應(yīng)用到每一個(gè)人,而不管傳遞的是什么值。
如果你是一個(gè)使用NT文件系統(tǒng)的Windows用戶,那么你應(yīng)該讀一下這個(gè)文檔,它解釋了如何使用各種不同的選項(xiàng)來控制不同用戶的文件存取權(quán)限問題。
如你所想,如果用戶沒有權(quán)限來改變這個(gè)抽象路徑名的存取權(quán)限,那么第一個(gè)方法就會(huì)失敗(也就是說,返回false);而且,這些方法也會(huì)拋出一個(gè)java.lang.SecurityException異常-如果存在一個(gè)Java安全管理器并且它的checkRead()/checkWrite()/checkExecute()方法不允許存取該文件的話。
下表1顯示了在多種文件系統(tǒng)上運(yùn)行這些命令的典型結(jié)果,以及這些命令在不同目標(biāo)操作系統(tǒng)上的可用性。
表1.在常用OS文件系統(tǒng)上的java.io.File權(quán)限操作
命令 | 在Windows XP系統(tǒng)上的返回值 | 在Linux系統(tǒng)上的返回值 | 在solaris系統(tǒng)上的返回值 |
setReadable(true) | true | True(等價(jià)于chmod+r) | True(等價(jià)于chmod+r) |
setReadable(false) | False(在Windows中文件可讀性不能被設(shè)置為False) | True(等價(jià)于chmod-r) | True(等價(jià)于chmod-r) |
setWritable(true) | True(切換Windows的只讀文件屬性) | True(等價(jià)于chmod+w) | True(等價(jià)于chmod+w) |
setWritable(false) | true(切換Windows的只讀文件屬性) | True(等價(jià)于chmod-w) | True(等價(jià)于chmod-w) |
setExecutable(true) | true | True(等價(jià)于chmod+x) | True(等價(jià)于chmod+x) |
setExecutable(false) | false(在Windows中文件可執(zhí)行屬性不能被設(shè)置為False) | True(等價(jià)于chmod-x) | True(等價(jià)于chmod-x) |
決定是否文件是可讀,可寫或可執(zhí)行的方法與這個(gè)平臺(tái)的前一個(gè)版本-Java 2平臺(tái),標(biāo)準(zhǔn)版(J2SE)5.0-保持一致。
public boolean canRead(); public boolean canWrite(); public boolean canExecute(); |
二、 取得硬盤分配空間 除了允許你設(shè)置文件和目錄權(quán)限外,Mustang還為你提供了三個(gè)新方法來決定當(dāng)前磁盤分區(qū)中的可用空間數(shù),這是由一個(gè)java.io.File對(duì)象來描述的:
public long getTotalSpace(); public long getFreeSpace(); public long getUsableSpace(); |
每一個(gè)這些方法返回要求的由java.io.File所描述的分區(qū)的字節(jié)大小,否則,如果從File對(duì)象中無法取得一個(gè)分區(qū)則返回值為0L。
借助于getFreeSpace()和getUsableSpace()方法,未分配字節(jié)的返回?cái)?shù)是(根據(jù)有關(guān)文檔):"這僅是一種提示而不是保證-有可能使用大多數(shù)或所有這些字節(jié);但緊跟這個(gè)調(diào)用之后的未分配的字節(jié)數(shù)很可能是準(zhǔn)確的,當(dāng)然也有可能因某些外部I/O操作(包括在該虛擬機(jī)外面所作的系統(tǒng)調(diào)用)而導(dǎo)致不準(zhǔn)確。"
那么,在這個(gè)兩個(gè)方法之間有什么區(qū)別呢?getFreeSpace()方法返回分區(qū)的
自由空間數(shù)量的一個(gè)即時(shí)數(shù)。而getUsableSpace()方法還包含了另外一些功能來檢查寫許可和其它操作系統(tǒng)限制,這將返回一個(gè)可用空間數(shù)的更好的估計(jì)值。如果你想決定在寫向一個(gè)文件之前是否你有足夠的磁盤空間,那么,典型情況下getUsableSpace()將給你一個(gè)更精確的估計(jì)值。注意,如果安裝了一個(gè)安全管理器并且它不允許對(duì)于RuntimePermission("getFileSystemAttributes")進(jìn)行調(diào)用,那么這兩個(gè)方法都將拋出一個(gè)SecurityException異常。
三、 使用Component描述JTabbedPane中的選項(xiàng)卡 這是Swing的JtabbedPane中的一處微妙但是很有價(jià)值的改進(jìn)。在以前的JtabbedPane中,你被限制僅用一個(gè)字符串,一個(gè)圖標(biāo)(或二者的結(jié)合)來描述一個(gè)選項(xiàng)卡。另外,如果你想的話,你還可以把一個(gè)提示小窗加到該選項(xiàng)卡上去。從Mustang的build 39開始,現(xiàn)在有可能使用一個(gè)Component來描述JtabbedPane中的一個(gè)選項(xiàng)卡。盡管這可能帶來一系列的問題,但是,這種特性的最常用的方式是:添加一個(gè)Close按鈕-它將從JTabbedPane中刪除該選項(xiàng)卡。
Sun程序員和Swing工程師Alexander Potochkin在他的最近的一個(gè)有關(guān)這個(gè)主題的博客日志中指出,這三個(gè)新方法已經(jīng)被添加到JTabbedPane。
你可以使用下列方法把Component設(shè)置為一個(gè)選項(xiàng)卡:
public void setTabComponentAt(int index, Component component) |
你可以使用下列方法得到這個(gè)組件:
public Component getTabComponentAt(int index) |
你可以使用下列這個(gè)方法來測(cè)試是否有組件被應(yīng)用于這個(gè)JtabbedPane中:
public int indexOfTabComponent(Component tabComponent) |
下面是一個(gè)選項(xiàng)卡面板示例源代碼-它允許你從一個(gè)JTabbedPane中動(dòng)態(tài)地添加和刪除選項(xiàng)卡。注意,在這個(gè)例子中我們創(chuàng)建了一個(gè)Jpanel,它包含兩個(gè)組件:一個(gè)位于面板左邊(BorderLayout.WEST)的JLabel和一個(gè)位于面板右邊(BorderLayout.EAST)的帶有一個(gè)ImageIcon的按鈕。這里所用的圖形是一個(gè)10x10像素大小的gif文件-它包含了一個(gè)小X。為了確保按鈕的尺寸小一些,我們把它的尺寸重置為圖標(biāo)的寬度和高度各自加上2個(gè)像素。
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class TabbedPaneExample implements ActionListener { private JFrame frame; private JTabbedPane tabbedPane; private JButton addTabButton; private ImageIcon closeXIcon; private Dimension closeButtonSize; private int tabCounter = 0; public TabbedPaneExample() { //創(chuàng)建選項(xiàng)卡面板 tabbedPane = new JTabbedPane(); //創(chuàng)建一個(gè)按鈕-用戶可用來添加一個(gè)選項(xiàng)卡到選項(xiàng)卡面板 addTabButton = new JButton("Add Tab"); addTabButton.addActionListener(this); //創(chuàng)建一個(gè)框架來包含這個(gè)選項(xiàng)卡面板 frame = new JFrame(); //創(chuàng)建一個(gè)圖像圖標(biāo)'X'以實(shí)現(xiàn)在每一個(gè)選項(xiàng)卡上的關(guān)閉功能。加載的gif是一個(gè)10x10圖形(非黑色部分是透明的) closeXIcon = new ImageIcon("C:/CloseX.gif"); //創(chuàng)建一個(gè)Dimension用來調(diào)整close按鈕的大小 closeButtonSize = new Dimension( closeXIcon.getIconWidth()+2, closeXIcon.getIconHeight()+2); //所選項(xiàng)卡面板添加到圖形中央,把"Add Tab"按鈕置于南面。然后包裝它,調(diào)整其大小并顯示它。 frame.add(tabbedPane, BorderLayout.CENTER); frame.add(addTabButton, BorderLayout.SOUTH); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setMinimumSize(new Dimension(300, 300)); frame.setVisible(true); } public void actionPerformed(ActionEvent e) { final JPanel content = new JPanel(); //創(chuàng)建一個(gè)描述該選項(xiàng)卡的面板并確保它是透明的 JPanel tab = new JPanel(); tab.setOpaque(false); //為該選項(xiàng)卡創(chuàng)建一個(gè)標(biāo)簽和一個(gè)Close按鈕。一定要 //把它的尺寸設(shè)置為幾乎該圖標(biāo)的大小,并且 //創(chuàng)建一個(gè)行為聽取器-它將定位該選項(xiàng)卡并且從選項(xiàng)卡面板上刪除它 JLabel tabLabel = new JLabel("Tab " + (++tabCounter)); JButton tabCloseButton = new JButton(closeXIcon); tabCloseButton.setPreferredSize(closeButtonSize); tabCloseButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int closeTabNumber = tabbedPane.indexOfComponent(content); tabbedPane.removeTabAt(closeTabNumber); } }); tab.add(tabLabel, BorderLayout.WEST); tab.add(tabCloseButton, BorderLayout.EAST); //把該選項(xiàng)卡添加到選項(xiàng)卡面板。注意, //第一個(gè)參數(shù)(它正常是一個(gè)描述選項(xiàng)卡標(biāo)題的String //),為null. tabbedPane.addTab(null, content); //不是在選項(xiàng)卡上使用String/Icon的結(jié)合, //而是使用我們的面板。 tabbedPane.setTabComponentAt(tabbedPane.getTabCount()-1, tab); } public static void main(String[] args) { TabbedPaneExample main = new TabbedPaneExample(); } } |
結(jié)果顯示于圖1中。
 圖1.一個(gè)把多個(gè)JComponent用作選項(xiàng)卡的JTabbedPane |
注意,Alexander Potochkin的博客中提供了另外一種不同的方法,它子類化JButton-重載paintComponent()并且畫出它自己的("X")。如果你不想使用你的代碼發(fā)布一個(gè)gif文件,那么使用這種更為復(fù)雜的方法是非常有用的。
四、 SwingWorker現(xiàn)在包含到Mustang中 大多數(shù)Swing程序員知道,無論什么時(shí)候編寫事件驅(qū)動(dòng)的代碼,例如當(dāng)一個(gè)按鈕按下時(shí)調(diào)用ActionListener,都需要快速處理事件。你永遠(yuǎn)不需要花費(fèi)比你必須處理事件驅(qū)動(dòng)的線程更多的時(shí)間,否則,你的Swing GUI將成為不可響應(yīng)并且不能有效地重繪它自己。在事件調(diào)度線程中實(shí)現(xiàn)一項(xiàng)較大的任務(wù)經(jīng)常意味著你要從事件調(diào)度線程中"剔除"一個(gè)獨(dú)立工作者線程并且讓該線程運(yùn)行于后臺(tái)。這樣以來,當(dāng)使用Swing編寫一個(gè)多線程的應(yīng)用程序時(shí),程序員需要牢記下面兩條規(guī)則:
· 如果把耗時(shí)的任務(wù)安排到一個(gè)調(diào)度線程中,那么這有可能導(dǎo)致應(yīng)用程序具有不可響應(yīng)性。因此,這樣的任務(wù)應(yīng)該用一個(gè)工作者線程來專門實(shí)現(xiàn)。
· 對(duì)Swing組件的更新應(yīng)該僅安排給一個(gè)事件調(diào)度線程來完成。
因?yàn)檫@意味著,至少有兩個(gè)線程在同時(shí)運(yùn)行,因此創(chuàng)建一個(gè)處理線程間通訊的類是很有幫助的。有一個(gè)消息是,最新的Mustang發(fā)行版本中加入了對(duì)SwingWorker類(它是前一段時(shí)間Swing程序員使用的一種流行的解決方案)的支持。
下面是來自于Javadoc的SwingWorker的正式聲明。
public abstract class SwingWorker<T,V> extends Object implements RunnableFuture<T> |
注意,這里的聲明包含了兩個(gè)泛型類型變量:T和V。如果你還不熟悉泛型類型變量的話,那么你可以先讀一下有關(guān)泛型的基礎(chǔ)知識(shí),這是在J2SE 5.0中引入的一種特性。下面是定義:
· T:由這個(gè)SwingWorker的doInBackground()和get()方法返回的結(jié)果類型
· V:被這個(gè)SwingWorker的publish()和process()方法用來執(zhí)行中間結(jié)果的類型
一會(huì)之后,我們?cè)儆懻撨@些方法。然而,首先,讓我們介紹一下SwingWorker中所使用的線程架構(gòu)。這里援引Javadoc的描述:在一個(gè)SwingWorker的生命周期中共包含三個(gè)線程:
· 當(dāng)前線程:execute()方法。它調(diào)度SwingWorker在一個(gè)工作者線程上的執(zhí)行并且立即返回。你可以使用兩個(gè)get()方法之一來等待SwingWorker完成。
· 工作者線程:這個(gè)線程上調(diào)用doInBackground()方法。這正是所有后臺(tái)活動(dòng)發(fā)生的地方。為了通知PropertyChangeListeners關(guān)于綁定屬性的變化,你可以使用firePropertyChange和getPropertyChangeSupport()方法。默認(rèn)情況下,有兩個(gè)綁定屬性可用-state和progress。
· 事件調(diào)度線程:所有的Swing相關(guān)的活動(dòng)都發(fā)生在這種線程中。SwingWorker調(diào)用process()和done()方法并且通知這個(gè)線程上的任何PropertyChangeListeners。
典型地,你在其上實(shí)例化SwingWorker子類的當(dāng)前線程是事件調(diào)度線程。這個(gè)SwingWorker通常響應(yīng)下列一些事件:
1. execute()方法被調(diào)用或運(yùn)行于這個(gè)事件調(diào)度線程上。
2. SwingWorker通知任何PropertyChangeListeners其狀態(tài)已經(jīng)變?yōu)镾wingWorker.StateValue.STARTED。
3. doInBackground()方法在工作者線程上執(zhí)行。
4. 一旦doInBackground()方法完成,即在當(dāng)前線程上執(zhí)行done()方法。
5. SwingWorker通知任何PropertyChangeListeners其狀態(tài)已經(jīng)變?yōu)镾wingWorker.StateValue.DONE。
當(dāng)在doInBackground()方法中時(shí),你可以設(shè)置一個(gè)整型的progress屬性-它使用下列方法指示工作者線程的當(dāng)前進(jìn)度:
protected void setProgress(int progress); |
一旦調(diào)用這個(gè)方法,SwingWorker就向所有的已經(jīng)登記的聽者激發(fā)一個(gè)屬性事件來通知它們已經(jīng)得到更新的進(jìn)度值。
你可以設(shè)置或添加工作者線程的最后結(jié)果-通過使用下面兩個(gè)方法之一:
protected void process(V... chunks); protected void publish(V... chunks); |
第二個(gè)方法,publish(),將使用一些中間類型V的一些可變個(gè)數(shù)的對(duì)象并且把它們發(fā)送到process()方法中進(jìn)行處理。典型情況下,你將從doInBackground()線程中調(diào)用process()方法。這個(gè)process()方法應(yīng)該總是被重載以接收輸入的V參數(shù)并且把這些中間對(duì)象以某種形式連接成一個(gè)T類型。當(dāng)然,至于process()方法如何實(shí)現(xiàn)這一任務(wù)要依賴于參數(shù)類型-它在你的SwingWorker子類中指定。
同時(shí),在當(dāng)前線程中,你可以調(diào)用兩個(gè)get()方法之一來檢索工作者線程的結(jié)果。第一個(gè)get()方法,在工作者線程完成其任務(wù)之前將會(huì)無限地阻塞。第二個(gè)方法在檢索已經(jīng)被處理的結(jié)果寬之前將阻塞一段指定的時(shí)間。
public T get(); public T get(long timeout,TimeUnit unit); |
如果你希望取消這個(gè)工作者線程,那么在它完成執(zhí)行之前,你可以從當(dāng)前線程中調(diào)用cancel()方法。
public final boolean cancel(boolean mayInterruptIfRunning) |
這里的mayInterruptIfRunning參數(shù)指定,在試圖停止這項(xiàng)任務(wù)時(shí)是否執(zhí)行該任務(wù)的線程應(yīng)該被中斷。注意,調(diào)用cancel()方法將失敗-如果該任務(wù)已經(jīng)完成,如果該任務(wù)已經(jīng)被取消或如果它因某些理由可能無法被取消。然而,如果該方法調(diào)用返回true并且當(dāng)調(diào)用cancel()方法時(shí)這項(xiàng)任務(wù)還沒有開始,那么SwingWorker永遠(yuǎn)不應(yīng)該執(zhí)行。
五、 結(jié)論 盡管本文中介紹的這些特征基本上相互獨(dú)立,但是它們的確代表Mustang開發(fā)團(tuán)隊(duì)希望滿足Java開發(fā)社區(qū)提出的一小部分實(shí)現(xiàn)請(qǐng)求。特別是,當(dāng)創(chuàng)建Swing應(yīng)用程序時(shí),學(xué)習(xí)使用SwingWorker類是必須的-它可以把程序員從復(fù)雜的GUI線程問題中解脫出來。記住,和往常一樣,這些特征在最終成為Java SE 6的最后發(fā)行版本之前要征得JSR 270專家組的同意。