? ? ? ? ? ? ? ? JAVA NIO基礎
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?原創者:文思
一、NIO介紹
來源于1.4,改進于1.7
NIO:New IO。互聯網環境下可以理解成Non Bolcking IO,非阻塞IO
傳統IO:面向流(單向的)、阻塞
NIO:面向緩沖(雙向)、非阻塞(針對網絡通信)、選擇器(針對網絡通信)
NIO的核心:通道+緩沖區,通道表示打開倒IO設備的連接,若需要使用NIO系統需要獲取用于連接IO設備的通道以及用于容納數據的緩沖區。
與傳統IO區別:
NIO和IO最大的區別是數據打包和傳輸方式。IO是以流的方式處理數據,而NIO是以塊的方式處理數據。
面向流的IO一次一個字節的處理數據,一個輸入流產生一個字節,一個輸出流就消費一個字節。為流式數據創建過濾器就變得非常容易,鏈接幾個過濾器,以便對數據進行處理非常方便而簡單,但是面向流的IO通常處理的很慢。
面向塊的IO系統以塊的形式處理數據。每一個操作都在一步中產生或消費一個數據塊。按塊要比按流快的多,但面向塊的IO缺少了面向流IO所具有的有雅興和簡單性。
以前的IO可以理解成接一個水管道,然后送水,送水結束后才斷開。現在的NIO可以理解成火車車廂(緩沖區)在鐵道上運行傳輸(管道)。
在Java
API中提供了兩套NIO,一套是針對標準輸入輸出NIO,另一套就是網絡編程NIO
Buffer(緩沖區)和Channel(通道)是標準NIO中的核心對象(網絡NIO中還有個Selector選擇器核心對象)
Channel是對原IO中流的模擬,任何來源和目的數據都必須通過一個Channel對象。
Buffer實質上是一個容器對象,發給Channel的所有對象都必須先放到Buffer中,同樣的,從Channel中讀取的任何數據都要讀到Buffer中。
在NIO中,所有數據都是Buffer處理的,Buffer實質上是一個數組,通常是一個字節數據,也可是其它類型,。但一個緩沖區不僅僅是一個數組,重要的是它提供了對數據的結構化訪問,可跟蹤讀寫進程。
查看Buffer類:
public abstractclassBuffer {
??SPLITERATOR_CHARACTERISTICS =
??????? Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;
// Invariants: mark <= position <= limit<= capacity
???privateintmark= -1;
???privateintposition= 0;
???privateintlimit;
? ?private int capacity;
可以發現緩沖區的四個核心屬性:
capactiy:緩沖區最大存儲數據的容量,一旦聲明無法改變
limit:界線,緩沖區中可以操作的數據的大小,limit后面的數據不能讀寫
position:當前數據位置。Position<=limit<=capactiy
mark:標記
新建NIO工程測試Buffer:
/**
?*一、緩沖區:在java NIO中負責數據的存取,可以看成數組,用于存儲不同類型的數據
?*根據數據類型不同,提供了相應類型的緩沖區(boolean類型除外):
?* ByteBuffer
?* CharBuffer
?* ShortBuffer
?* IntBuffer
?* LongBuffer
?* FlatBuffer
?* DoubleBuffer
?*上述緩沖區都是通過allocate()獲取緩沖區
?*二、緩沖區存取數據的兩個方法:put()、get()
?*三、flip()從寫數據切換成讀數據模式
?*四、rewind()可重復讀
?*五、clear()清空緩沖區
?*/
public classTestBuffer {
??? public void testBuffer1(){
?????? Strings = "abcde";
?????? //第1步:分配指定大小的緩沖區
?????? ByteBufferbuf= ByteBuffer.allocate(1024);
?????? System.out.println("----------第1步allocate()--------");
?????? System.out.println(buf.position());
?????? System.out.println(buf.limit());
?????? System.out.println(buf.capacity());
?????? //第2步:利用put()方法存入數據到緩沖區
?????? buf.put(s.getBytes());
?????? System.out.println("----------第2步put()--------");
?????? System.out.println(buf.position());
?????? System.out.println(buf.limit());
?????? System.out.println(buf.capacity());??????
?????? //第3步:切換成讀取數據的模式
?????? buf.flip();
?????? System.out.println("----------第3步flip()--------");
?????? System.out.println(buf.position());
?????? System.out.println(buf.limit());
?????? System.out.println(buf.capacity());???
?????? //第4步:利用get()讀取緩沖區數據
?????? byte[] b = new byte[buf.limit()];
?????? buf.get(b);
?????? System.out.println("----------第4步get()--------");
?????? System.out.println(new String(b,0,b.length));
?????? System.out.println(buf.position());
?????? System.out.println(buf.limit());
?????? System.out.println(buf.capacity());
?????? //第5步:rewind():可重復度
?????? buf.rewind();
?????? System.out.println("----------第5步rewind()--------");
?????? System.out.println(buf.position());
?????? System.out.println(buf.limit());
?????? System.out.println(buf.capacity());
?????? //第6步:clear():清空緩沖區
?????? System.out.println("----------第6步clear()--------");
?????? buf.clear();
?????? System.out.println(buf.position());
?????? System.out.println(buf.limit());
?????? System.out.println(buf.capacity());
//第7步:測試clear()后的清空結果
?????? System.out.println("-------第7步:測試clear()后的清空結-----");
?????? System.out.println((char)buf.get());;
??? }
??? public static void main(String[] args){
?????? TestBuffert = new TestBuffer();
?????? t.testBuffer1();
??? }
運行結果:
----------第1步allocate()--------
0
1024
1024
----------第2步put()--------
5
1024
1024
----------第3步flip()--------
0
5
1024
----------第4步get()--------
abcde
5
5
1024
----------第5步rewind()--------
0
5
1024
----------第6步clear()--------
0
1024
1024
----------第7步:測試clear()后的清空結果--------
a
1
1024
1024
由此可見clear()并不是刪除數據,是清空緩沖區操作,但緩沖區的數據依然存在,但是處于“被遺忘狀態”。被遺忘數據是否有潛在影響待進一步分析。
測試mark():
/**
??? ?*測試mark()
??? ?*/
??? public void testMark(){
?????? String s = "abcde";
?????? ByteBuffer buf= ByteBuffer.allocate(1024);
?????? buf.put(s.getBytes());
?????? buf.flip();
?????? byte[] b = new byte[buf.limit()];
?????? buf.get(b,0,2);//緩沖區讀取兩位長度內容
?????? System.out.println("讀取的兩位長度內容的顯示:"+new String(b,0,2));//讀取的兩位長度內容的顯示
?????? System.out.println("讀取數據后的當前位置:"+buf.position());//讀取數據后的當前位置
?????? buf.mark();
?????? buf.get(b,2,2);//緩沖區從位置2處再讀取兩位長度內容
?????? System.out.println("從位置2處再讀取兩位長度內容的顯示:"+new String(b,2,2));//從位置2處再讀取兩位長度內容的顯示
?????? System.out.println("mark的當前位置:"+buf.position());//mark的當前位置
?????? buf.reset();
?????? System.out.println("重置恢復到mark的當前位置:"+buf.position());//重置恢復到mark的當
??? }
運行結果:
讀取的兩位長度內容的顯示:ab
讀取數據后的當前位置:2
從位置2處再讀取兩位長度內容的顯示:cd
mark的當前位置:4
重置恢復到mark的當前位置:2
其它API:hasRemaining()緩沖區是否還有剩余數據,remaining()獲取緩沖區中剩余數據的數量。
直接緩沖區與非直接緩沖區
非直接緩沖區:通過allocate()方法分配緩沖區,將緩沖區建立在JVM的內存中
應用程序發起讀請求,物理磁盤將內容讀到內核地址空間,copy內核地址內容到用戶地址,程序讀取。前面的例子都是適用的非直接緩沖區。如上圖看到一些copy操作,和下圖比較在性能上應該處于劣勢。
直接緩沖區:通過allocateDirect()方法分配直接緩沖區,將緩沖區建立在物理內存中。
直接緩沖區通過調用此類的allocateDirect() 工廠方法來創建。此方法返回的緩沖區進行分配和取消分配所需成本通常高于非直接緩沖區。直接緩沖區的內容可以駐留在常規的垃圾回收堆之外,因此,它們對應用程序的內存需求量造成的影響可能并不明顯,但任何事情都是矛盾的,直接操作物理內存,則對內存消耗比較大,而且寫到物理內存后物理內存中的數據不歸程序管理了,何時寫到磁盤則由操作系統控制了,應用程序對內存的引用的回收依靠垃圾回收機制來控制,這樣就存在風險。
從兩圖可以看出,直接緩沖區比非直接緩沖區效率要高。
使用選擇:建議將直接緩沖區主要分配給那些易受基礎系統的本機I/O 操作影響的大型、持久的緩沖區。一般情況下,最好僅在直接緩沖區能在程序性能方面帶來明顯好處時分配它們。直接緩沖區還是非直接緩沖區可通過調用其isDirect()方法來確定。直接操作物理內存的事一般情況下敢玩嗎?所以常用的還是非直接緩沖區方式.
直接緩沖區示例:
/**
??? ?*測試直接緩沖區
??? ?*/
??? public void testDirect(){
?????? ByteBuffer? buf= ByteBuffer.allocateDirect(1024);
?????? System.out.println(buf.isDirect());
??? }
運行結果:true
通道Channel是一個對象,可以通過它讀取和寫入數據。可以把它看做IO中的流。但是它和流相比還有一些不同:
1.? ?Channel是雙向的,既可以讀又可以寫,而流是單向的
2.? ?Channel可以進行異步的讀寫
3.? Channel 本身不能直接訪問數據,對Channel的讀寫必須通過buffer對象
最開始的操作系統都是cpu直接負責IO接口,這樣當有大量的IO請求時cpu的負荷壓力變大,影響整個操作系統。后來演變成內存+DMA來代理控制IO請求,DMA向cpu申請權限,然后IO操作全權由DMA總線來負責,這也是傳統的IO流的方式。但是當有大量的流請求時,DMA總線會過多,易造成總線沖突,也會影響性能。最后就演變成上圖的模式,通道的方式,通道是完全獨立的處理器(雖然仍附屬于cpu)可獨立負責IO操作。
利用通道完成文件的復制:
/**
?*通道:用于源節點與目標節點的連接。在java NIO中負責緩沖區中數據的傳輸。
?*主要實現類java.nio.channels.Channel接口
?*??????????|FileChannel本地文件通道,用于讀取、寫入、映射和操作文件的通道
?*??????????|SocketChannel網絡相關,通過TCP 讀寫網絡中的數據
?*??????????|ServerScocketChannel可以監聽新進來的TCP 連接,對每一個新進來的連接都會創建一個SocketChannel
?*??????????|DatagramChannel通過UDP讀寫網絡中的數據通道
?*獲取通道:
?*1、java針對支持通道的類提供了getChannel()方法
?*FileInputStream/FileOutputStrem,RandonAccdessFile
?*網絡IO:Socket,ServderSocket,DatagramSocket
?*2、jdk1.7中的NIO2針對各通道提供了靜態方法open()
?*3、jdk1.7中的NIO2的Files工具類的newByteChannel()
?*/
public classTestChannel {
??? //1利用通道完成文件的賦值
??? public void test1() throws IOException{
?????? FileInputStream? fis = new??FileInputStream("1.jpg");
?????? FileOutputStream? fos = new??FileOutputStream("2.jpg");
?????? //1得到通道
?????? FileChannel? c1 = fis.getChannel();
?????? FileChannel? c2 = fos.getChannel();
?????? //2分配緩沖區
?????? ByteBuffer? buf = ByteBuffer.allocate(1024);
?????? //3將數據寫入緩沖區
?????? while(c1.read(buf) != -1){//數據寫入緩沖區
?????????? //4將緩沖區寫入通道
?????????? buf.flip();
?????????? c2.write(buf);//將緩沖區寫入通道
?????????? buf.clear();
?????? }
?????? c2.close();c1.close();fos.close();fis.close();
??? }
//使用直接緩沖區完成文件復制(內存直接映射文件)
??? public void test2() throws IOException{
?????? FileChannel? c1 = FileChannel.open(Paths.get("1.jpg"),StandardOpenOption.READ);
?????? FileChannel? c2 = FileChannel.open(Paths.get("2.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE_NEW);//因為寫文件時也需要先讀,所以這里也要配置StandardOpenOption.READ
?????? //內存映射文件
?????? MappedByteBuffer? inMbuf = c1.map(MapMode.READ_ONLY,0,c1.size());
??? ??? MappedByteBuffer? outMbuf = c2.map(MapMode.READ_WRITE,0,c1.size());
?????? //直接對緩沖區進行數據的讀寫操作
?????? byte[] dst = new byte[inMbuf.limit()];
?????? inMbuf.get(dst);
?????? outMbuf.put(dst);
?????? c1.close();c2.close();
??? }
//通道之間的數據傳輸(直接緩沖區)
??? public void test3() throws IOException{
?????? FileChannel? c1 = FileChannel.open(Paths.get("1.jpg"),StandardOpenOption.READ);
?????? FileChannel? c2 = FileChannel.open(Paths.get("2.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE_NEW);
?????? c1.transferTo(0, c1.size(), c2);//從c1通道將數據傳至c2
??? // c2.transferFrom(c1,0,c1.size());//從方法的命名可推出transferFrom的用法。
?????? c1.close();c2.close();??
??? }
分散與聚集:
分散讀取:從channel中讀取的數據分散到多個buffer中
聚集寫入:將多個buffer中的數據聚集到channel
//分散與聚集
??? public void test4() throws IOException{
?????? //分散讀取
?????? RandomAccessFile? raf = new??RandomAccessFile("1.txt","rw");
?????? FileChannel? channel = raf.getChannel();
?????? ByteBuffer? buf1 = ByteBuffer.allocate(100);
?????? ByteBuffer? buf2 = ByteBuffer.allocate(1600);
?????? ByteBuffer[]? bufs = {buf1,buf2};
?????? channel.read(bufs);
?????? //聚集寫入
?????? for(ByteBuffer buf : bufs){
?????????? buf.flip();
?????? }
?????? RandomAccessFile? raf2 = new??RandomAccessFile("1.txt","rw");
?????? FileChannel? channel2 = raf2.getChannel();
?????? ByteBuffer? bufWrite = ByteBuffer.allocate(1800);
?????? channel2.write(bufs);???
??? }
二、阻塞與非阻塞式網絡通信(重點)
傳統的IO 流都是阻塞式的。也就是說,當一個線程調用read() 或write() 時,該線程被阻塞,直到有一些數據被讀取或寫入,該線程在此期間不能執行其他任務。因此,在完成網絡通信進行IO 操作時,由于線程會阻塞,所以服務器端必須為每個客戶端都提供一個獨立的線程進行處理,當服務器端需要處理大量客戶端時,性能急劇下降。
阻塞式網絡通信示例:
/**
?*使用NIO完成網絡通信的三個核心:
?* 1、通道(Channel):負責連接
?* java.nio.channels.Channel接口:
?*??????? |--SelecttableChannel
?*?????????? |--SocketChannel
?*?????????? |--ServerSocketChannel
?*?????????? |--DatagramChannel
?*?????????? |--Pipe.SinkChannel
?*?????????? |--Pipe.SourceChannel
?* 2、緩沖區(Buffer):負責數據的存取
?* 3、選擇器(Selector):是SelectableChannel的多路復用器,用于監控SelectableChannel的IO狀況和事件
?*/
public class TestBlockingNIO {
??? /**客戶端*/
??? public void client() throws IOException{
?????? //1獲取網絡通道
?????? SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));
?????? //2創建獲取本地通道用于讀取要發送的文件
?????? FileChannel inLocalChannel = FileChannel.open(Paths.get("test.jpg"),StandardOpenOption.READ);
?????? //3分配制定緩沖
?????? ByteBuffer buf = ByteBuffer.allocate(1024);
?????? //4讀取本地文件,并發送到服務器
?????? while(inLocalChannel.read(buf)!=-1){
?????????? buf.flip();
?????????? socketChannel.write(buf);
??? ?????? buf.clear();
?????? }
?????? //5接收服務端的反饋
?????? int len = 0;
?????? while((len = socketChannel.read(buf))!=-1){
?????????? buf.flip();
?????????? System.out.println(new String(buf.array(),0,len));
?????????? buf.clear();
?????? }
?????? inLocalChannel.close();
?????? socketChannel.close();
??? }
??? /**服務端
??? ?*@throws IOException?
? ?*/
??? public void server() throws IOException{
?????? //1建立獲取網絡通道并綁定監聽端口
?????? ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
?????? serverSocketChannel.bind(new InetSocketAddress(9898));
?????? //2激活添加對客戶端通道的監聽
?????? SocketChannel socketChannel = serverSocketChannel.accept();
?????? //3建立本地通道用于寫文件
?????? FileChannel? outLocalChannel = FileChannel.open(Paths.get("des.jpg"), StandardOpenOption.WRITE,StandardOpenOption.CREATE_NEW);
?????? //4建立緩沖區
?????? ByteBuffer buf = ByteBuffer.allocate(1024);
?????? //5接收讀取網絡文件
?????? while(socketChannel.read(buf)!=-1){
?????????? buf.flip();
?????????? outLocalChannel.write(buf);
?????????? buf.clear();
?????? }
?????? //6發送反饋給客戶端
?????? buf.put("服務端接收數據成功".getBytes());
?????? buf.flip();
?????? socketChannel.write(buf);
??? }
}
Java NIO 是非阻塞模式的。當線程從某通道進行讀寫數據時,若沒有數據可用時,該線程可以進行其他任務。線程通常將非阻塞IO 的空閑時間用于在其他通道上執行IO 操作,所以單獨的線程可以管理多個輸入和輸出通道。因此,NIO 可以讓服務器端使用一個或有限幾個線程來同時處理連接到服務器端的所有客戶端。
java NIO的服務端只需啟動一個專門的線程來處理所有的 IO 事件。java NIO采用了雙向通道(channel)進行數據傳輸,而不是單向的流(stream),在通道上可以注冊我們感興趣的事件。一共有以下四種事件:
服務端接收客戶端連接事件SelectionKey.OP_ACCEPT(16)
客戶端連接服務端事件SelectionKey.OP_CONNECT(8)
讀事件SelectionKey.OP_READ(1)
寫事件SelectionKey.OP_WRITE(4)
服務端和客戶端各自維護一個管理通道的對象,我們稱之為selector(選擇器),該對象能檢測一個或多個通道(channel) 上的事件。Selector可以同時監控多個SelectableChannel的IO 狀況,也就是說利用Selector可使一個單獨的線程管理多個Channel。Selector 是非阻塞IO 的核心。若注冊時不止監聽一個事件,則可以使用“位或”操作符連接,例如: int interestSet = SelectionKey.OP_READ| SelectionKey.OP_WRITE
非阻塞網絡通信示例(重點):
public classTestNoBlockingNIO{
??? //客戶端
??? public void client() throws IOException{
?????? //1獲取客戶端通道
?????? SocketChannel? socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));
?????? //2切換成非阻塞模式
?????? socketChannel.configureBlocking(false);
?????? //3分配緩沖區
?????? ByteBuffer buf = ByteBuffer.allocate(1024);
?????? //4發送數據給服務端
?????? buf.put(new Date().toString().getBytes());
?????? Scanner scan = new Scanner(System.in);
?????? while(scan.hasNext()){
?????????? String str = scan.next();
?????????? buf.put(("\n"+str).getBytes());
?????????? buf.flip();
?????????? socketChannel.write(buf);
?????????? buf.clear();
?????? }
?????? socketChannel.close();
??? }
??? //服務端
??? public void server() throws IOException{
?????? //1獲取通道
?????? ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
?????? //2切換成非阻塞模式
?????? serverSocketChannel.configureBlocking(false);
?????? //3綁定連接
?????? serverSocketChannel.bind(new InetSocketAddress(9898));
?????? //4獲取選擇器
?????? Selector selector = Selector.open();
?????? //5將通道注冊到選擇器上,并且指定"監聽接收事件"
?????? //這里的ACCEPT是靠注冊進來的監聽,所以可以推論出此方式為非阻塞
???? serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);//SelectionKey選擇鍵
?????? //6輪詢式的獲取選擇器上已經"準備就緒"的事件
?????? while(selector.select()>0){
?????????? //獲取當前選擇器中所有注冊的"選擇鍵(已就緒的監聽事件)"
?????????? Iterator it = selector.selectedKeys().iterator();
?????????? //迭代獲取選擇器并根據不同的選擇鍵做不同的操作
?????????? while(it.hasNext()){
????????????? SelectionKey sk = it.next();
????????????? if(sk.isAcceptable()){
????????????????? SocketChannel socketChannel = serverSocketChannel.accept();
????????????????? socketChannel.configureBlocking(false);
????????????????? socketChannel.register(selector,SelectionKey.OP_READ);
????????????? }else if(sk.isReadable()){
????????????????? SocketChannel socketChannel = (SocketChannel) sk.channel();
????????????????? ByteBuffer buf= ByteBuffer.allocate(1024);
????????????????? int len = 0;
????????????????? while((len=socketChannel.read(buf))>0){
???????????????????? buf.flip();
???????????????????? System.out.println(new String(buf.array(),0,len));?? ?????????????
???????????????????? buf.clear();
????????????????? //? socketChannel.close();
????????????????? }
????????????? }
????????????? //取消選擇鍵
????????????? it.remove();
?????????? }
?????? }
??? }
測試:
首先運行服務端:
TestNoBlockingNIO t = newTestNoBlockingNIO();
t.server();
再次運行客戶端
TestNoBlockingNIO t = newTestNoBlockingNIO();
t.client();
客戶端輸入hello,服務端顯示hello:
三、NIO.2–Path、Paths、Files
隨著JDK7 的發布,Java對NIO進行了極大的擴展,增強了對文件處理和文件系統特性的支持,以至于我們稱他們為NIO.2
Path 與Paths
?java.nio.file.Path 接口代表一個平臺無關的平臺路徑,描述了目錄結構中文件的位置。
?Paths 提供的get() 方法用來獲取Path 對象:
?Path? get(String first, String …more) : 用于將多個字符串串連成路徑。
?Path常用方法:
?boolean endsWith(String path) : 判斷是否以path 路徑結束
?boolean startsWith(String path) : 判斷是否以path 路徑開始
?boolean isAbsolute() : 判斷是否是絕對路徑
?Path getFileName() : 返回與調用Path 對象關聯的文件名
?Path getName(int idx) : 返回的指定索引位置idx 的路徑名稱
?int getNameCount() : 返回Path 根目錄后面元素的數量
?Path getParent() :返回Path對象包含整個路徑,不包含Path 對象指定的文件路徑
?Path getRoot() :返回調用Path 對象的根路徑
?Path resolve(Path p) :將相對路徑解析為絕對路徑
?Path toAbsolutePath() : 作為絕對路徑返回調用Path 對象
?String toString() :返回調用Path 對象的字符串表示形式
java.nio.file.Files 用于操作文件或目錄的工具類。
?Files常用方法:
?Path copy(Path src, Path dest,CopyOption … how) : 文件的復制
?PathcreateDirectory(Path path,FileAttribute … attr) : 創建一個目錄
?Path createFile(Path path,FileAttribute … arr) : 創建一個文件
?void delete(Path path) : 刪除一個文件
?Path move(Path src, Path dest,CopyOption…how) : 將src 移動到dest位置
?long size(Path path) : 返回path 指定文件的大小
Files常用方法:用于判斷
?boolean exists(Path path,
LinkOption … opts) : 判斷文件是否存在
?boolean isDirectory(Path path,LinkOption … opts) : 判斷是否是目錄
?boolean isExecutable(Path path) : 判斷是否是可執行文件
?boolean isHidden(Path path) : 判斷是否是隱藏文件
?boolean isReadable(Path path) : 判斷文件是否可讀
?boolean isWritable(Path path) : 判斷文件是否可寫
?boolean notExists(Path path,LinkOption … opts) : 判斷文件是否不存在
?public static BasicFileAttributes> A readAttributes(Path path,Class type,LinkOption... options) : 獲取與path 指定的文件相關聯的屬性。
?Files常用方法:用于操作內容
?SeekableByteChannel? newByteChannel(Path path, OpenOption…how) : 獲取與指定文件的連接,how 指定打開方式。
?DirectoryStream? newDirectoryStream(Path path) : 打開path 指定的目錄
?InputStream newInputStream(Path path, OpenOption…how):獲取InputStream 對象
?OutputStream newOutputStream(Path path, OpenOption…how) : 獲取OutputStream 對象