Java NIO、NIO.2學習筆記

Java NIO、NIO.2學習筆記

目錄

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可以直接將指定文件的部分或者全部直接映射成Buffer2) 程序不能像訪問流那樣直接訪問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中寫入數據
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
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

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);
    }
}

轉自: http://www.2cto.com/kf/201405/299576.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章