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)trueTrue(等價(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專家組的同意。