Java 6.0標準版(Mustang)包含了大量使Java開發更為容易的特性。在本文中,我們將討論通過部分新特性來幫助你實現如下功能:

  · 設置文件和目錄許可權

  · 獲取分區上自由空間和可用空間數

  · 把Component對象添加到JTabbedPane的選項卡上

  · 在你的Java基礎類/Swing(JFC/Swing)應用程序中使用流行的SwingWorker類

  因此,如果JSR 270專家組同意采納這些特征,那么在Mustang的下一個發行版本中你就會看到這些特征。

  注意:為了運行本文中的源碼,你必須下載并安裝Mustang的最新版本。

  一、 設置文件和目錄權限

  現在,從Mustang build 31開始,你可以在本地文件系統中設置一個文件的可讀、可寫和可執行標志。這項功能已經被添加到java.io.File類中,并通過使用下列方法來實現:

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系統上工作過,那么你應該對這些方法非常熟悉-其實它們實現了chmod命令的一些功能。這些方法試圖設置由現在的File對象所描述的文件或目錄的適當權限。如果把第二個可選參數設置為true,那么該權限將僅應用于當前所有者標志。否則,這些方法將應用到所有用戶。注意,如果底層文件系統沒法區分該所有者和其他所有者的權限(在一些版本的Windows中就是這樣),那么這一權限將應用到每一個人,而不管傳遞的是什么值。

  如果你是一個使用NT文件系統的Windows用戶,那么你應該讀一下這個文檔,它解釋了如何使用各種不同的選項來控制不同用戶的文件存取權限問題。

  如你所想,如果用戶沒有權限來改變這個抽象路徑名的存取權限,那么第一個方法就會失敗(也就是說,返回false);而且,這些方法也會拋出一個java.lang.SecurityException異常-如果存在一個Java安全管理器并且它的checkRead()/checkWrite()/checkExecute()方法不允許存取該文件的話。

  下表1顯示了在多種文件系統上運行這些命令的典型結果,以及這些命令在不同目標操作系統上的可用性。

  表1.在常用OS文件系統上的java.io.File權限操作

命令在Windows XP系統上的返回值在Linux系統上的返回值在solaris系統上的返回值
setReadable(true)true True(等價于chmod+r)True(等價于chmod+r)
setReadable(false)False(在Windows中文件可讀性不能被設置為False)True(等價于chmod-r)True(等價于chmod-r)
setWritable(true)True(切換Windows的只讀文件屬性)True(等價于chmod+w)True(等價于chmod+w)
setWritable(false) true(切換Windows的只讀文件屬性)True(等價于chmod-w)True(等價于chmod-w)
setExecutable(true)trueTrue(等價于chmod+x)True(等價于chmod+x)
setExecutable(false)false(在Windows中文件可執行屬性不能被設置為False)True(等價于chmod-x)True(等價于chmod-x)

  決定是否文件是可讀,可寫或可執行的方法與這個平臺的前一個版本-Java 2平臺,標準版(J2SE)5.0-保持一致。

public boolean canRead();
public boolean canWrite();
public boolean canExecute();

  二、 取得硬盤分配空間

  除了允許你設置文件和目錄權限外,Mustang還為你提供了三個新方法來決定當前磁盤分區中的可用空間數,這是由一個java.io.File對象來描述的:

public long getTotalSpace();
public long getFreeSpace();
public long getUsableSpace();

  每一個這些方法返回要求的由java.io.File所描述的分區的字節大小,否則,如果從File對象中無法取得一個分區則返回值為0L。

  借助于getFreeSpace()和getUsableSpace()方法,未分配字節的返回數是(根據有關文檔):"這僅是一種提示而不是保證-有可能使用大多數或所有這些字節;但緊跟這個調用之后的未分配的字節數很可能是準確的,當然也有可能因某些外部I/O操作(包括在該虛擬機外面所作的系統調用)而導致不準確。"

  那么,在這個兩個方法之間有什么區別呢?getFreeSpace()方法返回分區的自由空間數量的一個即時數。而getUsableSpace()方法還包含了另外一些功能來檢查寫許可和其它操作系統限制,這將返回一個可用空間數的更好的估計值。如果你想決定在寫向一個文件之前是否你有足夠的磁盤空間,那么,典型情況下getUsableSpace()將給你一個更精確的估計值。注意,如果安裝了一個安全管理器并且它不允許對于RuntimePermission("getFileSystemAttributes")進行調用,那么這兩個方法都將拋出一個SecurityException異常。


 三、 使用Component描述JTabbedPane中的選項卡

  這是Swing的JtabbedPane中的一處微妙但是很有價值的改進。在以前的JtabbedPane中,你被限制僅用一個字符串,一個圖標(或二者的結合)來描述一個選項卡。另外,如果你想的話,你還可以把一個提示小窗加到該選項卡上去。從Mustang的build 39開始,現在有可能使用一個Component來描述JtabbedPane中的一個選項卡。盡管這可能帶來一系列的問題,但是,這種特性的最常用的方式是:添加一個Close按鈕-它將從JTabbedPane中刪除該選項卡。

  Sun程序員和Swing工程師Alexander Potochkin在他的最近的一個有關這個主題的博客日志中指出,這三個新方法已經被添加到JTabbedPane。

  你可以使用下列方法把Component設置為一個選項卡:

public void setTabComponentAt(int index, Component component)

  你可以使用下列方法得到這個組件:

public Component getTabComponentAt(int index)

  你可以使用下列這個方法來測試是否有組件被應用于這個JtabbedPane中:

public int indexOfTabComponent(Component tabComponent)

  下面是一個選項卡面板示例源代碼-它允許你從一個JTabbedPane中動態地添加和刪除選項卡。注意,在這個例子中我們創建了一個Jpanel,它包含兩個組件:一個位于面板左邊(BorderLayout.WEST)的JLabel和一個位于面板右邊(BorderLayout.EAST)的帶有一個ImageIcon的按鈕。這里所用的圖形是一個10x10像素大小的gif文件-它包含了一個小X。為了確保按鈕的尺寸小一些,我們把它的尺寸重置為圖標的寬度和高度各自加上2個像素。

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() {
  //創建選項卡面板
  tabbedPane = new JTabbedPane();
  //創建一個按鈕-用戶可用來添加一個選項卡到選項卡面板
  addTabButton = new JButton("Add Tab");
  addTabButton.addActionListener(this);
  //創建一個框架來包含這個選項卡面板
  frame = new JFrame();
  //創建一個圖像圖標'X'以實現在每一個選項卡上的關閉功能。加載的gif是一個10x10圖形(非黑色部分是透明的)
  closeXIcon = new ImageIcon("C:/CloseX.gif");
  //創建一個Dimension用來調整close按鈕的大小
  closeButtonSize = new Dimension(
   closeXIcon.getIconWidth()+2,
   closeXIcon.getIconHeight()+2);
   //所選項卡面板添加到圖形中央,把"Add Tab"按鈕置于南面。然后包裝它,調整其大小并顯示它。
  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();
  //創建一個描述該選項卡的面板并確保它是透明的
  JPanel tab = new JPanel();
  tab.setOpaque(false);
  //為該選項卡創建一個標簽和一個Close按鈕。一定要
  //把它的尺寸設置為幾乎該圖標的大小,并且
  //創建一個行為聽取器-它將定位該選項卡并且從選項卡面板上刪除它
  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);
  //把該選項卡添加到選項卡面板。注意,
  //第一個參數(它正常是一個描述選項卡標題的String
  //),為null.
  tabbedPane.addTab(null, content);
  //不是在選項卡上使用String/Icon的結合,
  //而是使用我們的面板。
  tabbedPane.setTabComponentAt(tabbedPane.getTabCount()-1, tab);
 }
 public static void main(String[] args) {
  TabbedPaneExample main = new TabbedPaneExample();
 }
}

  結果顯示于圖1中。

圖1.一個把多個JComponent用作選項卡的JTabbedPane

  注意,Alexander Potochkin的博客中提供了另外一種不同的方法,它子類化JButton-重載paintComponent()并且畫出它自己的("X")。如果你不想使用你的代碼發布一個gif文件,那么使用這種更為復雜的方法是非常有用的。


四、 SwingWorker現在包含到Mustang中

  大多數Swing程序員知道,無論什么時候編寫事件驅動的代碼,例如當一個按鈕按下時調用ActionListener,都需要快速處理事件。你永遠不需要花費比你必須處理事件驅動的線程更多的時間,否則,你的Swing GUI將成為不可響應并且不能有效地重繪它自己。在事件調度線程中實現一項較大的任務經常意味著你要從事件調度線程中"剔除"一個獨立工作者線程并且讓該線程運行于后臺。這樣以來,當使用Swing編寫一個多線程的應用程序時,程序員需要牢記下面兩條規則:

  · 如果把耗時的任務安排到一個調度線程中,那么這有可能導致應用程序具有不可響應性。因此,這樣的任務應該用一個工作者線程來專門實現。

  · 對Swing組件的更新應該僅安排給一個事件調度線程來完成。

  因為這意味著,至少有兩個線程在同時運行,因此創建一個處理線程間通訊的類是很有幫助的。有一個消息是,最新的Mustang發行版本中加入了對SwingWorker類(它是前一段時間Swing程序員使用的一種流行的解決方案)的支持。

  下面是來自于Javadoc的SwingWorker的正式聲明。

public abstract class SwingWorker<T,V> extends Object
implements RunnableFuture<T>

  注意,這里的聲明包含了兩個泛型類型變量:T和V。如果你還不熟悉泛型類型變量的話,那么你可以先讀一下有關泛型的基礎知識,這是在J2SE 5.0中引入的一種特性。下面是定義:

  · T:由這個SwingWorker的doInBackground()和get()方法返回的結果類型

  · V:被這個SwingWorker的publish()和process()方法用來執行中間結果的類型

  一會之后,我們再討論這些方法。然而,首先,讓我們介紹一下SwingWorker中所使用的線程架構。這里援引Javadoc的描述:在一個SwingWorker的生命周期中共包含三個線程:

  · 當前線程:execute()方法。它調度SwingWorker在一個工作者線程上的執行并且立即返回。你可以使用兩個get()方法之一來等待SwingWorker完成。

  · 工作者線程:這個線程上調用doInBackground()方法。這正是所有后臺活動發生的地方。為了通知PropertyChangeListeners關于綁定屬性的變化,你可以使用firePropertyChange和getPropertyChangeSupport()方法。默認情況下,有兩個綁定屬性可用-state和progress。

  · 事件調度線程:所有的Swing相關的活動都發生在這種線程中。SwingWorker調用process()和done()方法并且通知這個線程上的任何PropertyChangeListeners。

  典型地,你在其上實例化SwingWorker子類的當前線程是事件調度線程。這個SwingWorker通常響應下列一些事件:

  1. execute()方法被調用或運行于這個事件調度線程上。

  2. SwingWorker通知任何PropertyChangeListeners其狀態已經變為SwingWorker.StateValue.STARTED。

  3. doInBackground()方法在工作者線程上執行。

  4. 一旦doInBackground()方法完成,即在當前線程上執行done()方法。

  5. SwingWorker通知任何PropertyChangeListeners其狀態已經變為SwingWorker.StateValue.DONE。

  當在doInBackground()方法中時,你可以設置一個整型的progress屬性-它使用下列方法指示工作者線程的當前進度:

protected void setProgress(int progress);

  一旦調用這個方法,SwingWorker就向所有的已經登記的聽者激發一個屬性事件來通知它們已經得到更新的進度值。

  你可以設置或添加工作者線程的最后結果-通過使用下面兩個方法之一:

protected void process(V... chunks);
protected void publish(V... chunks);

  第二個方法,publish(),將使用一些中間類型V的一些可變個數的對象并且把它們發送到process()方法中進行處理。典型情況下,你將從doInBackground()線程中調用process()方法。這個process()方法應該總是被重載以接收輸入的V參數并且把這些中間對象以某種形式連接成一個T類型。當然,至于process()方法如何實現這一任務要依賴于參數類型-它在你的SwingWorker子類中指定。

  同時,在當前線程中,你可以調用兩個get()方法之一來檢索工作者線程的結果。第一個get()方法,在工作者線程完成其任務之前將會無限地阻塞。第二個方法在檢索已經被處理的結果寬之前將阻塞一段指定的時間。

public T get();
public T get(long timeout,TimeUnit unit);

  如果你希望取消這個工作者線程,那么在它完成執行之前,你可以從當前線程中調用cancel()方法。

public final boolean cancel(boolean mayInterruptIfRunning)

  這里的mayInterruptIfRunning參數指定,在試圖停止這項任務時是否執行該任務的線程應該被中斷。注意,調用cancel()方法將失敗-如果該任務已經完成,如果該任務已經被取消或如果它因某些理由可能無法被取消。然而,如果該方法調用返回true并且當調用cancel()方法時這項任務還沒有開始,那么SwingWorker永遠不應該執行。

  五、 結論

  盡管本文中介紹的這些特征基本上相互獨立,但是它們的確代表Mustang開發團隊希望滿足Java開發社區提出的一小部分實現請求。特別是,當創建Swing應用程序時,學習使用SwingWorker類是必須的-它可以把程序員從復雜的GUI線程問題中解脫出來。記住,和往常一樣,這些特征在最終成為Java SE 6的最后發行版本之前要征得JSR 270專家組的同意。