Java NIO、NIO.2學習筆記


相關學習資料

http://www.molotang.com/articles/903.html
http://www.ibm.com/developerworks/cn/education/java/j-nio/j-nio.html

 

目錄

1. NIO、NIO.2簡介
2. NIO中的關鍵技術

 

1. NIO、NIO.2簡介

Java中的輸入流、輸出流都是阻塞式的輸入、輸出。不僅如此,傳統的輸入流、輸出流都是通過字節的移動來處理的(即使是字符流,在底層也是通過字節流來進行處理的),也就是說,面向流的輸入/輸出系統一次只能處理一個字節,因此面向流的輸入/輸出系統通常效率不高。

從JDK1.4開始,java提供了一系列改進的輸入/輸出處理的新功能,這些功能被統稱為新IO(New IO,簡稱NIO),新增了許多用於處理輸入/輸出的類,這些類都被放在java.nio包以及子包下,並且對原java.io包中的許多類都以NIO為基礎進行了改寫。

NIO和傳統的IO有相同的目的,都是用於進行輸入/輸出,但新IO采用內存映射文件的方式來處理輸入/輸出,新IO將文件或文件的一段區域映射到內存中,這樣就可以像訪問內存一樣來訪問文件了
(模擬了操作系統上的虛擬內存的概念),通過這種方式來進行輸入/輸出比傳統的輸入/輸出要快得多 http://baike.baidu.com/view/394293.htm

java.nio的包層次結構如下

1. Buffer
java.nio.Buffer: Buffer是一個對象,它包含一些要寫入或者剛讀出的數據。在NIO中加入Buffer對象,體現了NIO庫與傳統I/O的一個重要區別。在面向流的I/O中,我們將數據直接寫入或者
將數據直接讀到Stream對象中。而在NIO庫中,所有數據都是用緩沖區處理的。發送到Channel中的所有對象都必須首先放到Buffer中,而從Channel中讀取的數據也必須先放到Buffer中。任何
時候訪問NIO中的數據,我們都是將它放到緩沖區中。緩沖區本質上是一塊可以寫入數據,然后可以從中讀取數據的內存。這塊內存被包裝成NIO Buffer對象,並提供了一組方法,用來方便的訪問
該塊內存。
這些Buffer類都沒有提供構造器,而是通過具體子類: XxxBuffer.allocate(
int capaticy); 來創建一個容量為capacity的XxxBuffer對象 1.1) ByteBuffer: 字節數組 1.1.1) MappedByteBuffer: 用於表示Channel將磁盤文件的部分或者全部內容映射到內存后得到的結果 1.2) ByteOrder: 字節枚舉數組 1.3) CharBuffer: 字符數組 1.4) DoubleBuffer: Double數組 1.5) FloatBuffer: Float數組 1.6) IntBuffer: Int數組 1.7) LongBuffer: Long數組 1.8) ShortBuffer: Short數組 2. Channel Channel類似於傳統的流對象,但與傳統的流對象有兩個主要區別: 1) Channel可以直接將指定文件的部分或者全部直接映射成Buffer。2) 程序不能像訪問流那樣直接訪問Channel中的數據
(包括讀取、寫入),Channel只能和Buffer進行交互。即,如果要從Channel中取得數據,必須先使用Buffer從Channel中取出一些數據,然后讓程序再從Buffer中取得這些數據。寫入也一樣。 和Buffer一樣,所有的Channel都不應該通過構造器來直接創建嗎,而是通過傳統的節點InputStream、OutputStream的getChannel()方法來返回對應的Channel,不同的節點流獲得的
Channel是不一樣的。從這個角度理解,Channel也可以理解為對節點流的一種包裝流,這也是Java中著名的裝飾器設計模式的一種體現。 我們按照Channel的功能對Channel的類層次進行分類
1) 文件操作 1.1) FileChannel: 文件讀寫的通道 1.1.1) FileInputStream調用getChannel返回的是只能讀的FileChannel 1.1.2) FileOutputStream調用getChannel返回的是只能寫的FileChannel 1.2) AsynchronousFileChannel: NIO.2新增了異步文件操作方式 1.3) FileLock: 文件鎖、互斥讀寫 2) 非阻塞式輸入輸出 2.1) SelectableChannel 2.2) Selector 2.3) SelectionKey 3) java.nio.channels.Pipe: 線程通信 3.1) Pipe.SinkChannel: PipedInputStream調用getChanel()返回的是Pipe.SinkChannel 3.2) Pipe.SourceChannel: PipedOutputStream調用getChanel()返回的是Pipe.SourceChannel 4) NetworkChannel: 支持TCP網絡通信的Channel 4.1) ServerSocketChannel 4.2) AsynchronousServerSocketChannel 4.3) SocketChannel 4.4) AsynchronousSocketChannel 5) DatagramChannel: 支持UDP網絡通信的Channel 3. Charset 計算機底層是沒有文本文件的、圖片之分的,它只是記錄每個文件的二進制數據。當需要保存文本文件時,程序必須先把文件中的每個"字符"翻譯成二進制序列,當需要讀取文本文件時,
程序必須把二進制序列轉換成一個個的"字符"。java默認使用Unicode字符集,為了解決可能出現的亂碼等問題。JDK 1.4提供了Charset來處理字節序列和字符序列(字符串)之間的轉換關系
(單字節、雙字節),該類包含了用於創建解碼器、編碼器的方法。
1) Charset 一旦知道了字符集的別名之后,程序就可以調用Charset的forName()方法來創建對應的Charset對象,forName()方法的參數就是相應字符集的別名 2) CharsetEncoder 獲得了Charset對象之后,就可以通過該對象的newEncoder()方法來返回對應的編碼器,調用CharsetEncoder的encode()方法可以完成字符序列(CharBuffer、String)到字節序列
(ByteBuffer)的轉換
3) CharsetDecoder 獲得了Charset對象之后,就可以通過該對象的newDecoder()方法來返回對應的解碼器,調用CharsetDecoder的decode()方法可以完成字節序列(ByteBuffer)到字符序列
(CharBuffer、String)的轉換最常用的字符集對應的Charset對象
4) StandardCharsets java7新增了一個StandardCharsets類,該類里包含了ISO-8859-1、UTF-8、UTF-16等靜態Field,這些靜態Field代表了 4. java.nio.channels.spi 主要包含與Channel相關的服務提供者編程接口 5. java.nio.charset.spi包 包含與字符集相關的服務提供者編程接口 6. java.nio.file包 早期的Java只提供了一個File類來訪問文件系統,但File類的功能比較有限,它不能利用特定文件系統的特性,File所提供的方法的性能也不高。而且,其大多數方法在出錯時僅返回失敗,
並不會提供異常信息
1) Path: 代表了一個平台無關的路徑 2) Paths: 包含了2個返回Path的靜態工廠方法 2.1) Path get(String first, String... more): 將給定的多個字符串進行拼接 2.2) get(URI uri) 3) Files: 提供了大量的高性能方法來操作文件 4) FileVisitor 使用FileVisitor來遍歷文件和目錄,在編程時可以通過繼承SimpleFileVisitor來實現自己的"文件訪問器",這樣就可以根據需要,選擇性地重寫指定方法 5) java.nio.file.attribute 如果程序希望獲取關於文件、目錄的更多的和特定操作系統、磁盤個性有關的屬性,可以使用NIO.2提供的java.nio.file.attribute工具類來實現 5.1) FileAttribute 代表某種文件屬性的"集合",程序一般通過XxxAttributeView對象來獲取XxxAttribute 5.1.1) BasicFileAttribute 5.1.2) DosFileAttribute 5.1.3) PosixFileAttribute 5.2) FileAttributeView 代表某種文件屬性的"視圖" 5.2.1) AclFileAttributeView 為特定的文件設置ACL(Access Control List)、及"文件所有者屬性" 5.2.2) BasicFileAttributeView 獲取或修改文件的基本屬性,包括: 1) 最后修改時間 2) 最后訪問時間 3) 創建時間 4) 大小 5) 是否為目錄 6) 是否為符號鏈接等 5.2.3) DosFileAttributeView 獲取或修改文件DOS相關信息,比如: 1) 是否只讀 2) 是否隱藏 3) 是否為系統文件 4) 是否為存檔文件等 5.2.4) FileOwnerAttributeView 獲取或修改文件的所有者 5.2.5) FileStoreAttributeView 5.2.6) PosixFileAttributeView 獲取后修改POSIX(Portable Operation System Interface of INIX)屬性,用於修改: 1) 文件的所有者 2) 組所有者 3) 訪問權限信息(即UNIX的chmod命令負責干的事情) 這個View只在UNIX、Linux等系統上有用 5.2.7) UserDefinedFileAttributeView 可以讓開發者為文件設置一些自定義屬性

 

2. NIO中的關鍵技術

0x1: Buffer

Buffer中有幾個重要的概念:

1. 容量(capacity): 緩沖區的容量(capacity)表示該Buffer的最大數據容量,即最多可以存儲多少數據。緩沖區的容量不可能為負值,創建后不能改變
2. 界限(limit): 第一個不應該被讀出、或者寫入的緩沖區位置索引。即位於limit后的數據既不可被讀、也不可被寫
3. 位置(position): 用於指明下一個可以被讀出、或者寫入的緩沖區位置索引(類似IO流中的記錄指針)。當使用Buffer從Channel中讀取數據時,position的值等於已經讀取了多少數據。
4. Buffer里還支持一個可選的標記(mark,類似於傳統IO流中的mark),Buffer允許直接將position定位到該mark處

0 <= mark <= position <= limit <= capacity

Buffer類主要作用就是裝入數據、然后輸出數據,類中包含幾個重要的方法:

1. 開始時Buffer的position為0,limit為capacity,程序可以通過put()方法向Buffer中放入一些數據,每放入一些數據,Buffer的position相應地向后移動一些位置(記住,position
是一個記錄指針)
2. put() 1) 相對(Relative)寫入: 從Buffer的當前position處開始寫入數據,然后將位置(position)的值按處理元素的個數增加 2) 絕對(Absotute)寫入: 直接根據索引向Buffer中指定位置開始寫入數據,但並不影響位置(position)的值 3. get() 1) 相對(Relative)讀取: 從Buffer的當前position處開始讀取數據,然后將位置(position)的值按處理元素的個數增加 2) 絕對(Absotute)讀取: 直接根據索引向Buffer中指定位置開始讀取數據,但並不影響位置(position)的值 4. flip()
當Buffer裝入數據結束后,調用Buffer的flip()方法,該方法將limit設置為position所在的位置,並將position設置為0(這里要重點注意,相當於將整個Buffer收縮了)。也就是說,
Buffer調用flip()方法之后,Buffer就為輸出數據做好了准備
5. clear()
當Buffer輸出數據結束后,Buffer調用clear()方法,clear()方法不是清空Buffer的數據,它僅僅將position設置為0,將limit設置為capacity,這樣就為再次向Buffer中裝入數據
做好了准備(注意,此時Buffer中的數據被檫除了嗎?沒有) 我們可以類比思考: Buffer的這種數據讀取機制是一種典型的
"基於數據結構的數據操作方式",和操作系統的棧內存操作方式從原理上是一致的

code:

import java.nio.*;

public class BufferTest
{
    public static void main(String[] args)
    {
        // 創建Buffer
        CharBuffer buff = CharBuffer.allocate(8);    //
        System.out.println("capacity: "    + buff.capacity());
        System.out.println("limit: " + buff.limit());
        System.out.println("position: " + buff.position());
        // 放入元素
        buff.put('a');
        buff.put('b');
        buff.put('c');      //
        System.out.println("加入三個元素后,position = " + buff.position());
        // 調用flip()方法
        buff.flip();      //
        System.out.println("執行flip()后,limit = " + buff.limit());
        System.out.println("position = " + buff.position());
        // 取出第一個元素
        System.out.println("第一個元素(position=0):" + buff.get());  //
        System.out.println("取出一個元素后,position = " + buff.position());
        // 調用clear方法
        buff.clear();     //
        System.out.println("執行clear()后,limit = " + buff.limit());    
        System.out.println("執行clear()后,position = " + buff.position());
        System.out.println("執行clear()后,緩沖區內容並沒有被清除:" + "第三個元素為:" +  buff.get(2));    //
        System.out.println("執行絕對讀取后,position = " + buff.position());
    }
}

0x2: Channel

Channel中最常用的方法是:

1. map()
用於將Channel對應的部分、或全部數據映射成Buffer
MappedByteBuffer map(FileChannel.MapMode mode, long position, long size)  
    1) FileChannel.MapMode: 執行映射時的模式
        1.1) PRIVATE: private (copy-on-write,寫時復制) mapping
        1.2) READ_ONLY: Mode for a read-only mapping
        1.3) READ_WRITE: Mode for a read/write mapping
    2) position、long size決定哪些數據被映射成Buffer
2. read()
用於從Buffer中讀取數據
3. write()
用於向Buffer中寫入數據

code:

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;

public class FileChannelTest
{
    public static void main(String[] args)
    {
        File f = new File("FileChannelTest.java");
        try(
            // 創建FileInputStream,以該文件輸入流創建FileChannel
            FileChannel inChannel = new FileInputStream(f).getChannel();
            // 以文件輸出流創建FileBuffer,用以控制輸出
            FileChannel outChannel = new FileOutputStream("out.txt").getChannel())
        {
            // 將FileChannel里的全部數據映射成ByteBuffer
            MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY , 0 , f.length());   //// 使用GBK的字符集來創建解碼器
            Charset charset = Charset.forName("GBK");
            // 直接將buffer里的數據全部輸出
            outChannel.write(buffer);     //// 再次調用buffer的clear()方法,復原limit、position的位置
            buffer.clear();
            // 創建解碼器(CharsetDecoder)對象
            CharsetDecoder decoder = charset.newDecoder();
            // 使用解碼器將ByteBuffer轉換成CharBuffer
            CharBuffer charBuffer = decoder.decode(buffer);
            // CharBuffer的toString方法可以獲取對應的字符串
            System.out.println(charBuffer);
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
        }
    }
}

有時候,如果遇到Channel對應的文件太大,無法一次性全部Map到內存中、或者使用map()方法一次將所有的文件內容映射到內存中會引起性能下降,也可以使用Channel的Buffer的"分批逐段讀取"的方法

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;

public class ReadFile
{
    public static void main(String[] args) throws IOException
    {
        try(
            // 創建文件輸入流
            FileInputStream fis = new FileInputStream("ReadFile.java");
            // 創建一個FileChannel
            FileChannel fcin = fis.getChannel())
        {
            // 定義一個ByteBuffer對象,用於重復取水
            ByteBuffer bbuff = ByteBuffer.allocate(64);
            // 將FileChannel中數據放入ByteBuffer中
            while( fcin.read(bbuff) != -1 )
            {
                // 鎖定Buffer的空白區
                bbuff.flip();
                // 創建Charset對象
                Charset charset = Charset.forName("GBK");
                // 創建解碼器(CharsetDecoder)對象
                CharsetDecoder decoder = charset.newDecoder();
                // 將ByteBuffer的內容轉碼
                CharBuffer cbuff = decoder.decode(bbuff);
                System.out.print(cbuff);
                // 將Buffer初始化,為下一次讀取數據做准備
                bbuff.clear();
            }
        }
    }
}

可以看到,代碼每次讀取數據后調用flip()方法將沒有數據的區域"封印"起來,避免程序從Buffer中讀取null值

0x3: Charset

import java.nio.*;
import java.nio.charset.*;

public class CharsetTransform
{
    public static void main(String[] args) throws Exception
    {
        // 創建簡體中文對應的Charset
        Charset cn = Charset.forName("GBK");
        // 獲取cn對象對應的編碼器和解碼器
        CharsetEncoder cnEncoder = cn.newEncoder();
        CharsetDecoder cnDecoder = cn.newDecoder();
        // 創建一個CharBuffer對象
        CharBuffer cbuff = CharBuffer.allocate(8);
        cbuff.put('');
        cbuff.put('');
        cbuff.put('');
        cbuff.flip();
        // 將CharBuffer中的字符序列轉換成字節序列
        ByteBuffer bbuff = cnEncoder.encode(cbuff);
        // 循環訪問ByteBuffer中的每個字節
        for (int i = 0; i < bbuff.capacity() ; i++)
        {
            System.out.print(bbuff.get(i) + " ");
        }
        // 將ByteBuffer的數據解碼成字符序列
        System.out.println("\n" + cnDecoder.decode(bbuff));
    }
}

實際上,Charset類也提供了如下3個方法,無須創建相應的解碼、編碼器就可以直接進行編碼轉換,是否創建編碼、解碼器的差別我覺得和在進行和正則有關的編程的時候是否創建正則表達式的編譯后對象是一個原理,復用性、性能的原因

1. CharBuffer decode(ByteBuffer bb)  
將ByteBuffer中的字節序列轉換成字符序列的快捷方法
2. ByteBuffer encode(CharBuffer cb) 
將ByteBuffer中的字節序列轉換成字符序列的快捷方法
3. ByteBuffer encode(String str)  
將CharBuffer中的字符序列轉換成字節序列的快捷方法

0x4: FileLock

文件鎖在操作系統中是很平常的事情,如果多個程序需要並發修改同一個文件時,程序之間需要通過鎖機制來進行並發控制。從JDK 1.4的NIO開始,java開始提供文件鎖的支持。在NIO中,FileChannel調用lock()、tryLock()方法返回的FileLock可以支持文件鎖功能,這兩種鎖的特性如下

1. lock()
FileLock lock(long position, long size, boolean shared)  
    1) position、size表明對文件的一部分進行加鎖,這是一種細粒度的鎖機制
    2) 參數shared
        2.1) true: 表明是一個"共享鎖",它將允許多個進行來"讀取(注意: 只是讀取)",但阻止其他進程獲得對該文件的排它鎖
        2.2) false(默認值): 表明是一個"排它鎖",它將鎖住對該文件的讀寫
    程序可以通過FileLock的isShared來判斷它獲得的鎖是否為共享鎖
當調用lock()試圖鎖定某個文件時,如果無法獲得文件鎖,該方法將一直阻塞
2. tryLock()
tryLock()是"嘗試"鎖定文件,它將立即返回而不是阻塞,如果獲得了文件鎖,該方法返回文件鎖,否則返回null

code:

import java.io.*;
import java.nio.*;
import java.nio.channels.*;

public class FileLockTest
{
    public static void main(String[] args) throws Exception
    {
        
        try(
            // 使用FileOutputStream獲取FileChannel
            FileChannel channel = new FileOutputStream("a.txt").getChannel())
        {
            // 使用非阻塞式方式對指定文件加鎖
            FileLock lock = channel.tryLock();
            // 程序暫停10s
            Thread.sleep(10000);
            // 釋放鎖
            lock.release();
        }
    }
}

從以上代碼可以看到,在這10秒內,其他程序無法對a.txt進行修改

0x5: Path、Paths、Files

path

import java.io.*;
import java.net.*;
import java.nio.file.*;

public class PathTest
{
    public static void main(String[] args)
        throws Exception
    {
        // 以當前路徑來創建Path對象
        Path path = Paths.get(".");
        System.out.println("path里包含的路徑數量:" + path.getNameCount());
        System.out.println("path的根路徑:" + path.getRoot());
        // 獲取path對應的絕對路徑。
        Path absolutePath = path.toAbsolutePath();
        System.out.println(absolutePath);
        // 獲取絕對路徑的根路徑
        System.out.println("absolutePath的跟路徑:" + absolutePath.getRoot());
        // 獲取絕對路徑所包含的路徑數量
        System.out.println("absolutePath里包含的路徑數量:" + absolutePath.getNameCount());
        System.out.println(absolutePath.getName(3));
        // 以多個String來構建Path對象
        Path path2 = Paths.get("g:" , "publish" , "codes");
        System.out.println(path2);
    }
}

Files

import java.nio.file.*;
import java.nio.charset.*;
import java.io.*;
import java.util.*;

public class FilesTest
{
    public static void main(String[] args) throws Exception
    {
        // 復制文件
        Files.copy(Paths.get("FilesTest.java") ,new FileOutputStream("out.txt"));
        // 判斷FilesTest.java文件是否為隱藏文件
        System.out.println("FilesTest.java是否為隱藏文件:" + Files.isHidden(Paths.get("FilesTest.java")));
        // 一次性讀取FilesTest.java文件的所有行
        List<String> lines = Files.readAllLines(Paths.get("FilesTest.java"), Charset.forName("gbk"));
        System.out.println(lines);
        // 判斷指定文件的大小
        System.out.println("FilesTest.java的大小為:" + Files.size(Paths.get("FilesTest.java")));
        List<String> poem = new ArrayList<>();
        poem.add("水晶潭底銀魚躍");
        poem.add("清徐風中碧竿橫");
        // 直接將多個字符串內容寫入指定文件中
        Files.write(Paths.get("pome.txt") , poem, Charset.forName("gbk"));
        FileStore cStore = Files.getFileStore(Paths.get("C:"));
        // 判斷C盤的總空間,可用空間
        System.out.println("C:共有空間:" + cStore.getTotalSpace());
        System.out.println("C:可用空間:" + cStore.getUsableSpace());
    }
}

從代碼可以看出,Files類是一個高度封裝的工具類,它提供了大量的工具方法來完成文件復制、讀取文件內容、寫入文件內容等功能,簡化原本的文件IO代碼量

0x6: FileVisitor

在傳統的java.io.File文件類的模式下,如果我們想要遍歷指定目錄下的所有文件和目錄,則只能使用遞歸進行遍歷,有了NIO的Files類的幫助,程序員可以采用更優雅的方式來遍歷文件和子目錄,Files類提供了如下兩個方法來遍歷文件和子目錄

1. walkFileTree(Path start, FileVisitor<? super Path> visitor);
遍歷start路徑下的所有文件和子目錄
2. walkFileTree(Path start, Set<FileVisitOption> options, int maxDepth, FileVisitor<? super Path> visitor);
作用和1一樣,增加了控制遍歷深度的maxDepth參數

這兩個方法都需要FileVisitor參數,FileVisitor代表一個"文件訪問器",walkFileTree()方法會自動遍歷start路徑下的所有文件和子目錄,遍歷文件和子目錄都會"觸發"(回調的思想)FileVisitor中相應的方法

1. FileVisitResult postVisitDirectory(T dir, IOException exc) 
訪問子目錄之后觸發該方法
2. FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs) 
訪問子目錄前觸發該方法
3. FileVisitResult visitFile(T file, BasicFileAttributes attrs) 
訪問file文件時觸發該方法
4. FileVisitResult visitFileFailed(T file, IOException exc) 
訪問file文件失敗時觸發該方法

以上4個方法都返回一個FileVisitResult對象,它是一個枚舉類,代表了訪問之后的后續行為。FileVisitResult定義了如下幾種后續行為

1. CONTINUE 
代表"繼續訪問"的后續行為
2. SKIP_SIBLINGS 
代表"繼續訪問"的后續行為,但不訪問該文件或目錄的兄弟文件或目錄
3. SKIP_SUBTREE 
代表"繼續訪問"的后續行為,但不訪問該文件后目錄的子目錄樹
4. TERMINATE 
代表"終止訪問"的后續行為

code:

import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;

public class FileVisitorTest
{
    public static void main(String[] args) throws Exception
    {
        // 遍歷g:\publish\codes\15目錄下的所有文件和子目錄
        Files.walkFileTree(Paths.get("D:", "學習資料", "視頻學習資料", "JAVA學習", "瘋狂java", "code", "codes", "15"), new SimpleFileVisitor<Path>()
        {
            // 訪問文件時候觸發該方法
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
            {
                System.out.println("正在訪問" + file + "文件");
                // 找到了FileInputStreamTest.java文件
                if (file.endsWith("FileInputStreamTest.java"))
                {
                    System.out.println("--已經找到目標文件--");
                    return FileVisitResult.TERMINATE;
                }
                return FileVisitResult.CONTINUE;
            }
            // 開始訪問目錄時觸發該方法
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
            {
                System.out.println("正在訪問:" + dir + " 路徑");
                return FileVisitResult.CONTINUE;
            }
        });
    }
}

0x7: 使用WatchService來監控磁盤文件變動

NIO.2的Path類提供了如下的方法來監聽文件系統的變動

1. WatchKey register(WatchService watcher, WatchEvent.Kind<?>[] events, WatchEvent.Modifier... modifiers)  
    1) watcher: 代表一個文件系統監聽服務,它監聽該path代表的目錄下的文件變化
    2) events參數指定要監聽哪些類型的事件
        2.1) ENTRY_CREATE  
        2.2) ENTRY_DELETE 
        2.3) ENTRY_MODIFY 
        2.4) OVERFLOW  

一旦使用register()方法完成注冊之后,接下來就可以調用WatchService的方法來獲取被監聽目錄的文件變化事件

1. WatchKey poll()
獲取下一個WatchKey,如果沒有WatchKey發生就立即返回null
2. WatchKey poll(long timeout, TimeUnit unit)
嘗試等待timeout時間(最大延時)去獲取下一個WatchKey
3. WatchKey take()
獲取下一個WatchKey,如果沒有WatchKey發生就一直阻塞等待

code:

import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;

public class WatchServiceTest
{
    public static void main(String[] args) 
        throws Exception
    {
        // 獲取文件系統的WatchService對象
        WatchService watchService = FileSystems.getDefault().newWatchService();
        // 為C:盤根路徑注冊監聽
        Paths.get("C:/").register(watchService 
            , StandardWatchEventKinds.ENTRY_CREATE
            , StandardWatchEventKinds.ENTRY_MODIFY
            , StandardWatchEventKinds.ENTRY_DELETE);
        while(true)
        {
            // 獲取下一個文件改動事件
            WatchKey key = watchService.take();    //
            for (WatchEvent<?> event : key.pollEvents()) 
            {
                System.out.println(event.context() +" 文件發生了 " + event.kind()+ "事件!");
            }
            // 重設WatchKey
            boolean valid = key.reset();
            // 如果重設失敗,退出監聽
            if (!valid) 
            {
                break;
            }
        }
    }
}

0x8: 深度、高效、全面地訪問文件屬性

import java.io.*;
import java.util.*;
import java.nio.file.*;
import java.nio.*;
import java.nio.charset.*;
import java.nio.file.attribute.*;

public class AttributeViewTest
{
    public static void main(String[] args) throws Exception
    {
        // 獲取將要操作的文件
        Path testPath = Paths.get("AttributeViewTest.java");
        // 獲取訪問基本屬性的BasicFileAttributeView
        BasicFileAttributeView basicView = Files.getFileAttributeView(testPath , BasicFileAttributeView.class);
        // 獲取訪問基本屬性的BasicFileAttributes
        BasicFileAttributes basicAttribs = basicView.readAttributes();
        // 訪問文件的基本屬性
        System.out.println("創建時間:" + new Date(basicAttribs.creationTime().toMillis()));
        System.out.println("最后訪問時間:" + new Date(basicAttribs.lastAccessTime().toMillis()));
        System.out.println("最后修改時間:" + new Date(basicAttribs.lastModifiedTime().toMillis()));
        System.out.println("文件大小:" + basicAttribs.size());
        // 獲取訪問文件屬主信息的FileOwnerAttributeView
        FileOwnerAttributeView ownerView = Files.getFileAttributeView(testPath, FileOwnerAttributeView.class);
        // 獲取該文件所屬的用戶
        System.out.println(ownerView.getOwner());
        // 獲取系統中guest對應的用戶
        UserPrincipal user = FileSystems.getDefault().getUserPrincipalLookupService().lookupPrincipalByName("guest");
        // 修改用戶
        ownerView.setOwner(user);
        // 獲取訪問自定義屬性的FileOwnerAttributeView
        UserDefinedFileAttributeView userView = Files.getFileAttributeView(testPath, UserDefinedFileAttributeView.class);
        List<String> attrNames = userView.list();
        // 遍歷所有的自定義屬性
        for (String name : attrNames)
        {
            ByteBuffer buf = ByteBuffer.allocate(userView.size(name));
            userView.read(name, buf);
            buf.flip();
            String value = Charset.defaultCharset().decode(buf).toString();
            System.out.println(name + "--->" + value) ;
        }
        // 添加一個自定義屬性
        userView.write("發行者", Charset.defaultCharset().encode("瘋狂Java聯盟"));
        // 獲取訪問DOS屬性的DosFileAttributeView
        DosFileAttributeView dosView = Files.getFileAttributeView(testPath, DosFileAttributeView.class);
        // 將文件設置隱藏、只讀
        dosView.setHidden(true);
        dosView.setReadOnly(true);
    }
}

 

Copyright (c) 2014 LittleHann All rights reserved

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM