Java7對NIO進行了大的改進,新增了許多功能:
- 對文件系統的訪問提供了全面的支持
- 提供了基於異步Channel的IO
這些新增的IO功能簡稱為 NIO.2,依然在java.nio包下。
早期的Java只提供了File類來操作文件、文件夾本身,功能有限,性能也不高。
NIO.2為解決這種缺陷,提供了Path接口,並提供了Paths、Files2個工具類,這2個工具類包含的方法都是靜態方法,Files類提供了大量的靜態方法來操作文件、文件夾。
Path接口、Paths工具類使用示例:
1 //獲取指定路徑的Path對象 2 Path path=Paths.get("D:/untitled/test/"); 3 //也可以這樣寫: Path path=Paths.get("D","untitled","test"); 參數個數可變 4 5 //返回文件/文件夾名,test 6 System.out.println(path.getFileName()); 7 //返回父目錄的路徑,D:\untitled 8 System.out.println(path.getParent()); 9 //判斷該路徑的最后一級是否是指定的文件/文件夾,true。和String的endsWith有點不同。當參數是最后一級的文件/文件夾名時,才返回true。 10 System.out.println(path.endsWith("test")); 11 //以上方法的操作對象是Path對象中的路徑,就是說路徑不必是真實存在的 12 13 //返回根路徑(盤符),D:\ 14 System.out.println(path.getRoot()); 15 //返會絕對路徑 16 System.out.println(path.toAbsolutePath()); 17 //以上獲取的路徑要是真實存在的
以前,如果要監控文件、目錄的變化(新建、修改、刪除),一般是啟動一條后台線程,這條線程每隔一段時間去遍歷指定目錄的文件,如果此次遍歷的結果與上次遍歷的結果不同,就認為文件發生了變化。這種方式很繁瑣,性能也低。
NIO.2的Path接口提供了一個方法來監聽文件變化:
register(WatchService watcher, 要監聽的事件類型1,要監聽的事件類型2,.......) //注冊一個監聽服務
比如“xx安全衛士提醒您:xx程序將創建xx文件,是否允許?”、“xx程序試圖修改xx文件,是否允許?”,就可使用此方法實現。
使用示例:
1 public class Test { 2 public static void main(String[] args) throws IOException, InterruptedException { 3 4 //創建一個文件系統的監聽服務 5 WatchService watchService= FileSystems.getDefault().newWatchService(); 6 7 Path path=Paths.get("D:/untitled/test"); 8 9 //為該文件夾注冊監聽,監聽新建、修改、刪除事件。只能為文件夾(目錄)注冊監聽,不能為單個文件注冊監聽 10 path.register(watchService,StandardWatchEventKinds.ENTRY_CREATE,StandardWatchEventKinds.ENTRY_MODIFY,StandardWatchEventKinds.ENTRY_DELETE); 11 12 //編寫事件處理 13 while (true){ //一直監聽 14 //拉取一個WatchKey。當觸發監聽的事件時,就會產生一個WatchKey,此WatchKey封裝了事件信息。 15 WatchKey watchKey=watchService.take(); 16 17 //使用循環是因為這一個WatchKey中可能有多個文件變化了,比如Ctrl+A全選,然后刪除,只觸發了一個WatchKey,但有多個文件變化了 18 for (WatchEvent event:watchKey.pollEvents()){ 19 System.out.println(event.context()+"發生了"+event.kind()+"事件!"); 20 /* 21 watchKey.pollEvents() 獲取此次WatchKey中所有變化了的文件的信息,以List(列表)形式返回,一個WatchEvent就是一個元素,封裝了一個變化了的文件的信息 22 event.context() 獲取文件名 23 event.kind() 獲取發生的事件類型 24 25 因為只能為文件夾注冊監聽,如果要監聽某些指定的文件,可以在增強的for循環中,先根據event.context()判斷是否是指定的文件,是才處理。 26 */ 27 } 28 29 //雖然是while()循環,但WatchKey和ByteBuffer一樣,使用完要重置狀態,才能繼續用。 30 watchKey.reset(); //如果不重置,WatchKey使用一次過后就不能再使用,即只能監聽到一次文件變化。 31 } 32 33 34 /* 35 測試:在D:/untitled/test目錄下 36 1、新建1.txt文件 1.txt.txt發生了ENTRY_CREATE事件! 37 2、打開1.txt,輸入"hello world!",並保存 1.txt.txt發生了ENTRY_MODIFY事件! 38 3、刪除“hello world!",並保存 1.txt.txt發生了ENTRY_MODIFY事件! 39 4、新建2.txt文件 2.txt發生了ENTRY_CREATE事件! 40 41 說明:編輯文件內容時,不算修改,保存時才算修改。 42 43 5、新建文件夾“圖片” 44 新建文件夾發生了ENTRY_CREATE事件! 45 新建文件夾發生了ENTRY_DELETE事件! 46 圖片發生了ENTRY_CREATE事件! 47 OS執行過程:新建了一個名為“新建文件夾”的文件夾,再把此文件夾重命名為指定的名稱。 48 重命名時,”新建文件夾“消失了,算作刪除,”圖片“出現了,算作新建。 49 50 6、在圖片目錄下,隨便新建一個文件或者文件夾 51 圖片發生了ENTRY_MODIFY事件! 52 說明監控的只是子文件、子文件夾,不會遞歸監控。 53 54 7、全選,刪除test下所有文件、子文件夾 55 圖片發生了ENTRY_DELETE事件! 56 1.txt.txt發生了ENTRY_DELETE事件! 57 2.txt發生了ENTRY_DELETE事件! 58 */ 59 60 61 } 62 }
拉取一個WatchKey有3種方法:
- WatchKey take() //阻塞式拉取,如果未拉取到,會一直等待,直到拉取到一個WatchKey。一般我們用這個。
- WatchKey poll() //非阻塞式拉取,試圖拉取一個WatchKey,拉取到了就返回拉取的WatchKey對象,沒有WatchKey發生就立即返回null。
- WatchKey poll(long timeout, TimeUnit unit) //非阻塞式拉取,如果沒拉取到,會再等待timeout毫秒,還沒拉取到才返回null。
