摘要:緩沖區(qū)的容量不可能為負值,創(chuàng)建后不能改變界限界限第一個不應(yīng)該被讀寫或者寫入的緩沖區(qū)位置索引。當使用從中讀取數(shù)據(jù)時,的值恰好等于已經(jīng)讀到了多少數(shù)據(jù)。
NIO:New IO
Java新IO概述新IO采用內(nèi)存映射文件的方式來處理輸入/輸出,新IO文件或文件的一段區(qū)域映射到內(nèi)存中,這樣就可以訪問內(nèi)存一樣來訪問文件了(這種方式模擬了操作系統(tǒng)上的虛擬內(nèi)存的概念),通過這種方式來進行輸入/輸出比傳統(tǒng)的輸入/輸出要快得多
Java中NIO相關(guān)的包如下:
java.nio包:主要提供了一些和Buffer相關(guān)的類
java.nio.channels包:主要包括Channel和Selector相關(guān)的類
java.nio.charset包:主要包含和字符集相關(guān)的類
java.nio.channels.spi包:主要包含提供Channel服務(wù)的類
java.nio.charset.spi包:主要包含提供字符集服務(wù)的相關(guān)類
Channel(通道)和Buffer(緩沖)是新IO中的兩個核心對象,Channel是對傳統(tǒng)輸入/輸出系統(tǒng)中的模擬,在新IO系統(tǒng)中所有數(shù)據(jù)都需要通過通道傳輸;Channel與傳統(tǒng)的InputStream、OutputStream最大的區(qū)別在于它提供了一個map方法,通過該map方法可以直接將“一塊數(shù)據(jù)”映射到內(nèi)存中。如果說傳統(tǒng)的輸入/輸出系統(tǒng)是面向流的處理,而新IO則是面向塊的處理
Buffer可以被理解成一個容器,它的本質(zhì)是一個數(shù)組,發(fā)送到Channel中的所有對象都必須首先放到Buffer中,而從Channel中讀取的數(shù)據(jù)也必須先讀到Buffer中。此處的Buffer有點類似于前面我們介紹的“竹筒”,但該Buffer既可以像前面那樣一次、一次去Channel中取水,也允許使用Channel直接將文件的某塊數(shù)據(jù)映射成Buffer
除了Channel和Buffer之外,新IO還提供了用于將UNICODE字符串映射成字節(jié)序列以及逆映射操作的Charset類,還提供了用于支持非阻塞式輸入/輸出的Selector類
使用BufferBuffer類沒有提供構(gòu)造器,通過使用如下方法來得到一個Buffer對象:
static XxxBuffer allocate(int capacity):創(chuàng)建一個容量為capacity的XxxBuffer對象
使用較多的是ByteBuffer和CharBuffer。其中ByteBuffer類還有一個子類:MappedByteBuffer,它用于表示Channel將磁盤文件的部分或全部內(nèi)容映射到內(nèi)存中后得到的結(jié)果,通常MappedByteBuffer對象由Channel的map()方法返回
Buffer三個重要概念:容量(capacity)、界限(limit)、位置(position)
容量(capacity):緩沖區(qū)的容量(capacity)表示該Buffer的最大數(shù)據(jù)容量,即最多可以存儲多少數(shù)據(jù)。緩沖區(qū)的容量不可能為負值,創(chuàng)建后不能改變
界限(limit)
界限(limit):第一個不應(yīng)該被讀寫或者寫入的緩沖區(qū)位置索引。也就是說,位于limit后的數(shù)據(jù)既不可被讀,也不可被寫
位置(position):用于指明下一個可以被讀出或者寫入的緩沖區(qū)位置索引(類似于IO流中的記錄指針)。當使用Buffer從Channel中讀取數(shù)據(jù)時,position的值恰好等于已經(jīng)讀到了多少數(shù)據(jù)。當剛剛新建一個Buffer對象時,其position為0;如果從Channel中讀取了2個數(shù)據(jù)到該Buffer中,則position為2,指向Buffer中的第三個(第1個位置的索引為0)位置
標記(mark):Buffer里還支持一個可選的標記(mark,類似于傳統(tǒng)IO流中的mark),Buffer允許直接將position定位到該mark處。
0 <= mark <= position <= limit <= capacity
Buffer的主要作用就是裝入數(shù)據(jù),然后輸出數(shù)據(jù),開始時Buffer的position為0,limit為capacity,程序可通過put()方法向Buffer中放入一些數(shù)據(jù)(或從channel獲取數(shù)據(jù)),每放入一些數(shù)據(jù),position向后移動一些位置
當Buffer裝入數(shù)據(jù)結(jié)束后,調(diào)用filp()方法,該方法將limit設(shè)置為position所在位置,將position設(shè)置為0。這樣使得從Buffer中讀取數(shù)據(jù)總是從0開始。讀完所有裝入的數(shù)據(jù)即結(jié)束,也就是說,Buffer調(diào)用filp后,Buffer為輸出數(shù)據(jù)做好了準備
當Buffer輸出數(shù)據(jù)結(jié)束后,調(diào)用clear方法。將position置為0,將limit置為capacity,這樣為再次向Buffer中裝載數(shù)據(jù)做好準備
Buffer抽象類常用方法:
int capacity():返回Buffer的capacity大小
boolean hasRemaining():判斷當前位置(position)和界限(limit)之間是否有元素可供處理
int limit():返回Buffer的界限(limit)的位置
Buffer limit(int newLimit):重新設(shè)置界限(limit)的值,并返回一個具有新的limit的緩沖區(qū)對象
int position():返回Buffer的position值
Buffer position(new position):設(shè)置Buffer的position值,并返回position被修改后的Buffer對象
int remaining():返回當前位置和界限(limit)之間的元素個數(shù)
Buffer reset():將位置(position)轉(zhuǎn)到mark所在的位置
Buffer rewind():將位置(position)設(shè)置為0,取消設(shè)置的mark
put()和get()方法,用于向Buffer中放入數(shù)據(jù)和從Buffer中取出數(shù)據(jù)。當使用put()和get()。Buffer既支持對單個數(shù)據(jù)的訪問,也支持對批量數(shù)據(jù)的訪問(以數(shù)組作為參數(shù))
當使用put()和get()來訪問Buffer中的數(shù)據(jù)時,分為相對和絕對兩種:
相對(Relative):從Buffer當前位置讀取或?qū)懭霐?shù)據(jù),然后將位置(position)的值按處理元素個數(shù)增加
絕對(Absolute):直接根據(jù)索引來向Buffer中讀取或?qū)懭霐?shù)據(jù),使用絕對方式來訪問Buffer里的數(shù)據(jù),并不會影響position的值
import java.nio.*; public class BufferTest { public static void main(String[] args) { // 創(chuàng)建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()); // 調(diào)用flip()方法 buff.flip(); // ③ System.out.println("執(zhí)行flip()后,limit = " + buff.limit()); System.out.println("position = " + buff.position()); // 取出第一個元素 System.out.println("第一個元素(position=0):" + buff.get()); // ④ System.out.println("取出一個元素后,position = " + buff.position()); // 調(diào)用clear方法 buff.clear(); // ⑤ System.out.println("執(zhí)行clear()后,limit = " + buff.limit()); System.out.println("執(zhí)行clear()后,position = " + buff.position()); System.out.println("執(zhí)行clear()后,緩沖區(qū)內(nèi)容并沒有被清除:" + "第三個元素為:" + buff.get(2)); // ⑥ System.out.println("執(zhí)行絕對讀取后,position = " + buff.position()); } }
運行結(jié)果:
capacity: 8 limit: 8 position: 0 加入三個元素后,position = 3 執(zhí)行flip()后,limit = 3 position = 0 第一個元素(position=0):a 取出一個元素后,position = 1 執(zhí)行clear()后,limit = 8 執(zhí)行clear()后,position = 0 執(zhí)行clear()后,緩沖區(qū)內(nèi)容并沒有被清除:第三個元素為:c 執(zhí)行絕對讀取后,position = 0
代碼①:新分配的CharBuffer對象:
代碼②:向Buffer中放入3個對象后
代碼③:執(zhí)行Buffer的flip()方法后
代碼⑤:執(zhí)行clear()后的Buffer
使用ChannelChannel類似于傳統(tǒng)的流對象,但與傳統(tǒng)的流對象有兩個主要區(qū)別:
Channel可以直接將指定文件的部分或全部直接映射成Buffer
程序不能直接訪問Channel中的數(shù)據(jù),包括讀、寫入都不行,Channel只能與Buffer進行交互。也就是說,如果要從Channel中取得數(shù)據(jù),必須先用Buffer從Channel中取出一些數(shù)據(jù),然后讓程序從Buffer中取出這些數(shù)據(jù);如果要將程序中的數(shù)據(jù)寫入Channel,一樣先讓程序?qū)⒄l放入Buffer中,程序再將Buffer里的數(shù)據(jù)寫入Channel中
所有的Channel都不應(yīng)該通過構(gòu)造器來直接創(chuàng)建,而是通過傳統(tǒng)的節(jié)點InputStream、OutputStream的getChannel()方法來返回對應(yīng)的Channel,不同的節(jié)點流獲得的Channle不一樣
Channel中最常用的三類方法是map()、read()和write()
map()方法用于將Channel對應(yīng)的部分或全部數(shù)據(jù)映射成ByteBuffer;而read()或write()方法都有一系列重載形式,這些方法用于從Buffer中讀取數(shù)據(jù)或向Buffer里寫入數(shù)據(jù)
map方法的方法簽名為:MappedByteBuffer map(FileChannel.MapMode mode, long position, long size),第一個參數(shù)執(zhí)行映射時的模式,分別有只讀,讀寫模式,而第二個,第三個參數(shù)用于控制將Channel的哪些數(shù)據(jù)映射成ByteBuffer
以下是直接將FileChannel的全部數(shù)據(jù)映射成ByteBuffer的效果的代碼。使用FileInputStream、FileOutputStream來獲取FileChannel。代碼①直接將指定Channel中的全部數(shù)據(jù)映射成ByteBuffer,代碼②直接將整個ByteBuffer的全部數(shù)據(jù)寫入一個輸出FileChannel中,完成文件復(fù)制。使用Charset類和CharsetDecoder類將ByteBuffer轉(zhuǎn)換成CharBuffer
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( // 創(chuàng)建FileInputStream,以該文件輸入流創(chuàng)建FileChannel FileChannel inChannel = new FileInputStream(f).getChannel(); // 以文件輸出流創(chuàng)建FileBuffer,用以控制輸出 FileChannel outChannel = new FileOutputStream("a.txt").getChannel()) { // 將FileChannel里的全部數(shù)據(jù)映射成ByteBuffer MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, f.length()); // ① // 使用GBK的字符集來創(chuàng)建解碼器 Charset charset = Charset.forName("GBK"); // 直接將buffer里的數(shù)據(jù)全部輸出 outChannel.write(buffer); // ② // 再次調(diào)用buffer的clear()方法,復(fù)原limit、position的位置 buffer.clear(); // 創(chuàng)建解碼器(CharsetDecoder)對象 CharsetDecoder decoder = charset.newDecoder(); // 使用解碼器將ByteBuffer轉(zhuǎn)換成CharBuffer CharBuffer charBuffer = decoder.decode(buffer); // CharBuffer的toString方法可以獲取對應(yīng)的字符串 System.out.println(charBuffer); } catch (IOException ex) { ex.printStackTrace(); } } }
RandomAccessFile中也包含getChannel()方法,返回的FileChannel()讀寫類型取決于RandomAccessFile打開文件的模式。以下代碼將對a.txt文件的內(nèi)容進行復(fù)制,追加到該文件后面:
import java.io.*; import java.nio.*; import java.nio.channels.*; public class RandomFileChannelTest { public static void main(String[] args) throws IOException { File f = new File("a.txt"); try( // 創(chuàng)建一個RandomAccessFile對象 RandomAccessFile raf = new RandomAccessFile(f, "rw"); // 獲取RandomAccessFile對應(yīng)的Channel FileChannel randomChannel = raf.getChannel()) { // 將Channel中所有數(shù)據(jù)映射成ByteBuffer ByteBuffer buffer = randomChannel.map(FileChannel.MapMode.READ_ONLY, 0, f.length()); // 把Channel的記錄指針移動到最后 randomChannel.position(f.length()); // 將buffer中所有數(shù)據(jù)輸出 randomChannel.write(buffer); } } }
randomChannel.position(f.length()); 代碼將Channel的記錄指針移動到該Channel的最后,從而可以讓程序?qū)⒅付˙yteBuffer的數(shù)據(jù)追加到該Channel后面。每次運行上面程序,都會把a.txt文件的內(nèi)容復(fù)制一份,并將全部內(nèi)容追加到該文件的后面
使用map()方法一次將所有的文件內(nèi)容映射到內(nèi)存中引起性能下降,可以使用Channel和Buffer傳統(tǒng)的“用竹筒多次重復(fù)取水”的方式:
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( // 創(chuàng)建文件輸入流 FileInputStream fis = new FileInputStream("ReadFile.java"); // 創(chuàng)建一個FileChannel FileChannel fcin = fis.getChannel()) { // 定義一個ByteBuffer對象,用于重復(fù)取水 ByteBuffer bbuff = ByteBuffer.allocate(256); // 將FileChannel中數(shù)據(jù)放入ByteBuffer中 while( fcin.read(bbuff) != -1 ) { // 鎖定Buffer的空白區(qū) bbuff.flip(); // 創(chuàng)建Charset對象 Charset charset = Charset.forName("GBK"); // 創(chuàng)建解碼器(CharsetDecoder)對象 CharsetDecoder decoder = charset.newDecoder(); // 將ByteBuffer的內(nèi)容轉(zhuǎn)碼 CharBuffer cbuff = decoder.decode(bbuff); System.out.print(cbuff); // 將Buffer初始化,為下一次讀取數(shù)據(jù)做準備 bbuff.clear(); } } } }
Buffer提供了flip()和clear()兩個方法,每次讀取數(shù)后調(diào)用flip()方法將沒有數(shù)據(jù)的區(qū)域“封印”起來,避免程序從Buffer中取出null值;數(shù)據(jù)取出后立即調(diào)用clear()方法將Buffer的position設(shè)0,為下一次讀取數(shù)據(jù)做準備
字符集和Charset編碼Encode:把明文的字符序列換成計算機理解的二進制序列
解碼Decode:把二進制序列轉(zhuǎn)換成明文字符串
Java默認使用Unicode字符集,但很多操作系統(tǒng)并不使用Unicode字符集,那么當從系統(tǒng)中讀取數(shù)據(jù)到Java程序中時,就可能出現(xiàn)亂碼等問題
JDK1.4提供了Charset來處理字節(jié)序列和字符序列(字符串)之間的轉(zhuǎn)換關(guān)系,該類包含了用于創(chuàng)建編碼和解碼的的方法,還提供了獲取所有Charset所支持的字符集的方法,Charset類是不可變的
availableCharset()的靜態(tài)方法用于獲取當前JDK所支持的所有字符集
import java.nio.charset.*; import java.util.*; public class CharsetTest { public static void main(String[] args) { // 獲取Java支持的全部字符集 SortedMapmap = Charset.availableCharsets(); for (String alias : map.keySet()) { // 輸出字符集的別名和對應(yīng)的Charset對象 System.out.println(alias + "----->" + map.get(alias)); } } }
常用字符串別名:
GBK:簡體中文字符串
BIG5:繁體中文字符串
ISO-8859-1:ISO拉丁字母表No.1
UTF-8:8位UCS轉(zhuǎn)換格式
UTF-16BE:16位的UCS轉(zhuǎn)換格式,Big-endian(最低地址存放高位字節(jié))字節(jié)順序
UTF-16LE:16位的UCS轉(zhuǎn)換格式,Little-endian(最高地址存放低位字節(jié))字節(jié)順序
UTF-16:16位的UCS轉(zhuǎn)換格式,字節(jié)順序由可選的字節(jié)順序標記來標識
調(diào)用Charset的forName()方法創(chuàng)建字符串別名對應(yīng)的Charset對象,forName()方法的參數(shù)就是相應(yīng)字符集的別名:
Charset cs = Charset.forName("ISO-8859-1");
獲得Charset對象之后,通過該對象的newDecoder()、newEncoder()方法分別返回CharsetDecoder和CharsetEncoder對象,代表該Charset的解碼器和編碼器。調(diào)用CharsetDecoder的decode()方法可以將ByteBuffer(字節(jié)序列)轉(zhuǎn)換成CharBuffer(字符序列),調(diào)用CharsetEncoder的encode()方法可以將CharBuffer或String(字符序列)轉(zhuǎn)換成ByteBuffer(字節(jié)序列)
import java.nio.*; import java.nio.charset.*; public class CharsetTransform { public static void main(String[] args) throws Exception { // 創(chuàng)建簡體中文對應(yīng)的Charset Charset cn = Charset.forName("GBK"); // 獲取cn對象對應(yīng)的編碼器和解碼器 CharsetEncoder cnEncoder = cn.newEncoder(); CharsetDecoder cnDecoder = cn.newDecoder(); // 創(chuàng)建一個CharBuffer對象 CharBuffer cbuff = CharBuffer.allocate(8); cbuff.put("內(nèi)"); cbuff.put("馬"); cbuff.put("爾"); cbuff.flip(); // 將CharBuffer中的字符序列轉(zhuǎn)換成字節(jié)序列 ByteBuffer bbuff = cnEncoder.encode(cbuff); // 循環(huán)訪問ByteBuffer中的每個字節(jié) for (int i = 0; i < bbuff.capacity() ; i++) { System.out.print(bbuff.get(i) + " "); } // 將ByteBuffer的數(shù)據(jù)解碼成字符序列 System.out.println(" " + cnDecoder.decode(bbuff)); } }
Charset類提供了如下三個方法:
CharBuffer decode(ByteBuffer bb):將ByteBuffer中的字節(jié)序列轉(zhuǎn)換成字符序列的便捷方法
ByteBuffer encode(CharBuffer cb):將CharBuffer中的字節(jié)序列轉(zhuǎn)換成字符序列的便捷方法
ByteBuffer encode(String str):將String中的字節(jié)序列轉(zhuǎn)換成字符序列的便捷方法
獲取Charset對象后,如果僅僅需要進行簡單的編碼、解碼操作,實則無須創(chuàng)建CharsetDecoder和CharsetEncoder對象,直接調(diào)用Charset的encode()和decode()方法進行編碼、解碼即可
文件鎖如果多個運行的程序需要并發(fā)修改同一個文件時,程序之間需要某種機制來進行通信,使用文件鎖可以有效阻止多個進程并發(fā)修改同一個文件
Java提供了FileLock類支持文件鎖功能,在FileChannel中提供的lock()/tryLock()方法可以獲得文件鎖FileLock對象,從而鎖定文件
lock()方法和trylock()方法的區(qū)別是:當lock()試圖鎖定某個文件時,如果無法得到文件鎖,程序?qū)⒁恢弊枞?,而tryLock()方法時嘗試鎖定文件,它將直接返回而不是阻塞,如果獲得了文件鎖,該方法則返回該文件鎖,否則將返回null
lock(long position, long size, boolean shared):對文件從position開始,長度為size的內(nèi)容加鎖,該方法是阻塞式的
tryLock(long position, long size, boolean shared):非阻塞式的加鎖方法
當shared為true時,表明該鎖是一個共享鎖,它將允許多個進程來讀取文件,但不允許其他進程將其設(shè)為排他鎖;當shared為false的時,表明這是一個排他鎖,它將鎖住對該文件的讀寫
通過調(diào)用FileLock的isShared來判斷獲得的鎖是否為共享鎖
直接使用lock()或tryLock()方法獲取的文件鎖是排他鎖
處理完成之后,調(diào)用FileLock的release()方法釋放文件鎖
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(); } } }Java7的NIO2
NIO的改進主要包括如下方面:
提供了全面的文件IO和文件系統(tǒng)訪問支持
基于異步Channel的IO
Path、Paths和File核心APIPaths提供了get(String first, String... more)方法來獲取Path對象,Paths會將給定的多個字符串連綴成路徑,如Paths.get("D:", "java","code")將返回D:javacode路徑;getNameCount()返回Path路徑所包含的路徑名
import java.io.*; import java.net.*; import java.nio.file.*; public class PathTest { public static void main(String[] args) throws Exception { // 以當前路徑來創(chuàng)建Path對象 Path path = Paths.get("."); System.out.println("path里包含的路徑數(shù)量:" + path.getNameCount()); System.out.println("path的根路徑:" + path.getRoot()); // 獲取path對應(yīng)的絕對路徑 Path absolutePath = path.toAbsolutePath(); System.out.println(absolutePath); // 獲取絕對路徑的根路徑 System.out.println("absolutePath的根路徑:" + absolutePath.getRoot()); // 獲取絕對路徑所包含的路徑數(shù)量 System.out.println("absolutePath里包含的路徑數(shù)量:" + absolutePath.getNameCount()); System.out.println(absolutePath.getName(3)); // 以多個String來構(gòu)建Path對象 Path path2 = Paths.get("D:", "java","code"); System.out.println(path2); } }
運行結(jié)果:
path里包含的路徑數(shù)量:1 path的根路徑:null D:DevelopmenteclipseworkspaceCrazyJava. absolutePath的根路徑:D: absolutePath里包含的路徑數(shù)量:5 CrazyJava D:javacode
以下代碼示范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 { // 復(fù)制文件 Files.copy(Paths.get("FilesTest.java"), new FileOutputStream("a.txt")); // 判斷FilesTest.java文件是否為隱藏文件 System.out.println("FilesTest.java是否為隱藏文件:"+ Files.isHidden(Paths.get("FilesTest.java"))); // 一次性讀取FilesTest.java文件的所有行 List使用FileVisitor遍歷文件和目錄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 poem = new ArrayList<>(); poem.add("感時花濺淚"); poem.add("恨別鳥驚心"); // 直接將多個字符串內(nèi)容寫入指定文件中 Files.write(Paths.get("pome.txt"), poem, Charset.forName("gbk")); // 使用Java 8新增的Stream API列出當前目錄下所有文件和子目錄 Files.list(Paths.get(".")).forEach(path -> System.out.println(path)); // 使用Java 8新增的Stream API讀取文件內(nèi)容 Files.lines(Paths.get("FilesTest.java"), Charset.forName("gbk")).forEach(line -> System.out.println(line)); FileStore cStore = Files.getFileStore(Paths.get("C:")); // 判斷C盤的總空間,可用空間 System.out.println("C:共有空間:" + cStore.getTotalSpace()); System.out.println("C:可用空間:" + cStore.getUsableSpace()); } }
Files類提供了兩個方法來遍歷文件和子目錄
walkFileTree(Path start, FileVisitor super Path> visitor):遍歷start路徑下的所有文件和子目錄
walkFileTree(Path start, Set
FileVisitor代表一個文件訪問器,walkFileTree()方法會自動遍歷start路徑下的所有文件和子目錄,遍歷文件和子目錄都會觸發(fā)FileVisitor中相應(yīng)的方法。這四個方法在下面的代碼中出現(xiàn)。FileVisitor中定義了如下4個方法:
FileVisitResult postVisitDirectory(T dir, IOException exc):訪問子目錄之后觸發(fā)該方法
FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs):訪問子目錄之前觸發(fā)該方法
FileVisitResult visitFile(T file, BasicFileAttributes attrs):訪問file文件時觸發(fā)該方法
FileVisitResult visitFileFailed(T file, IOException exc):訪問file文件失敗時觸發(fā)該方法
FileVisitResult對象,它是一個枚舉類,代表訪問之后的后續(xù)行為,它有如下幾種后續(xù)行為:
CONTINUE:代表“繼續(xù)訪問”的后續(xù)行為
TERMINATE:代表“終止訪問”的后續(xù)行為
SKIP_SUBTREE:代表“繼續(xù)訪問“,但不訪問該目錄文件或目錄的子目錄樹
SKIP_SIBLINGS:代表“繼續(xù)訪問”,但不訪問該文件或目錄的兄弟文件或目錄
以下程序使用Files工具類的walkFileTree()方法遍歷g:publishcodes15目錄下的所有文件和子目錄,直到找到的文件以“FileVisitorTest.java”結(jié)尾,則程序停止遍歷。實現(xiàn)了對指定目錄進行搜索,直到找到指定文件為止
import java.io.*; import java.nio.file.*; import java.nio.file.attribute.*; public class FileVisitorTest { public static void main(String[] args) throws Exception { // 遍歷D:codingJava路徑CrazyJavacodes15目錄下的所有文件和子目錄 Files.walkFileTree(Paths.get("D:", "coding", "Java路徑", "CrazyJava", "codes", "15"), new SimpleFileVisitor使用WatchService監(jiān)控文件變化() { // 訪問文件時候觸發(fā)該方法 @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { System.out.println("正在訪問" + file + "文件"); // 找到了FileVisitorTest.java文件 if (file.endsWith("FileVisitorTest.java")) { System.out.println("--已經(jīng)找到目標文件--"); return FileVisitResult.TERMINATE; } return FileVisitResult.CONTINUE; } // 開始訪問目錄時觸發(fā)該方法 @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { System.out.println("正在訪問:" + dir + " 路徑"); return FileVisitResult.CONTINUE; } }); } }
register(WatchService watcher, WatchEvent.Kind> ... events):用watcher監(jiān)聽該path代表的目錄下文件變化。events參數(shù)指定要監(jiān)聽哪些類型的事件
WatchService代表一個文件系統(tǒng)監(jiān)聽服務(wù),它負責監(jiān)聽path代表的目錄下的文件變化。一旦使用register()方法完成注冊之后,可調(diào)用WatchService如下的三個方法來監(jiān)聽目錄的文件變化事件
WatchKey poll():獲取下一個WatchKey,如果沒有WatchKey發(fā)生就立即返回null
WatcheKey poll(long timeout, TimeUnit unit):嘗試等待timeout時間去獲取下一個WatchKey
WatchKey take():獲取下一個WatchKey,如果沒有WatchKey發(fā)生就一直等待
如果程序需要一直監(jiān)控,則應(yīng)該選擇使用take()方法,如果程序只需要監(jiān)控指定時間,則使用poll方法
import java.io.*; import java.nio.file.*; import java.nio.file.attribute.*; public class WatchServiceTest { public static void main(String[] args) throws Exception { // 獲取文件系統(tǒng)的WatchService對象 WatchService watchService = FileSystems.getDefault().newWatchService(); // 為C:盤根路徑注冊監(jiān)聽 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() +" 文件發(fā)生了 " + event.kind()+ "事件!"); } // 重設(shè)WatchKey boolean valid = key.reset(); // 如果重設(shè)失敗,退出監(jiān)聽 if (!valid) { break; } } } }訪問文件屬性
Java7的NIO.2在java.nio.file.attribute包下提供了大量工具類,可以簡單地讀取、修改文件屬性。分為如下兩類:
XxxAttributeView:代表某種文件屬性的“視圖”
XxxAttributes:代表某種文件屬性的“集合”,程序一般通過XxxAttributeView對象獲取XxxAttributes
FileAttributeView是其他XxxAttributeView的父接口
AclFileAttributeView: 為特定文件設(shè)置ACL(Access Control List)及文件所有者屬性。getAcl()方法返回List
BaseFileAttributeView:獲取或修改文件的基本屬性。readAttributes()方法返回一個BaseFileAttributeView對象,對文件夾基本屬性的修改是通過BaseFileAttributeView對象完成的
DosFileAttributeView:獲取或修改文件DOS相關(guān)屬性。readAttributes()方法返回一個DosFileAttributeView對象,對文件夾屬性的修改通過DosFileAttributeView對象完成的
FileOwnerAttributeView:獲取或修改文件的所有者。getOwner()方法返回一個UserPrincipal對象來代表文件所有者;也可調(diào)用setOwner(UserPrincipal owner)方法來改變文件的所有者
PosixFileAttributeView:獲取文件或修改POSIX(Portable Operating System Interface of INIX)屬性。readAttributes()方法返回一個PosixFileAttributeView對象,該對象用于獲取或修改文件的所有者、組所有者、訪問權(quán)限信息。僅在UNIX、Linux等系統(tǒng)上有用
UserDefinedFileAttributeView:開發(fā)者自定義文件屬性
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/66608.html
摘要:異步可以讓你異步的使用,例如當線程從通道讀取數(shù)據(jù)到緩沖區(qū)時,線程還是可以進行其他事情。當數(shù)據(jù)被寫入到緩沖區(qū)時,線程可以繼續(xù)處理它。因此,單個的線程可以監(jiān)聽多個數(shù)據(jù)通道。下面是系列文章的目錄概述通道之間的數(shù)據(jù)傳輸與原文譯者郭蕾校對方騰飛 Java NIO(New IO)是一個可以替代標準Java IO API的IO API(從Java 1.4開始),Java NIO提供了與標準IO不同的...
摘要:線程之間的切換對于操作系統(tǒng)來說是昂貴的。因此,單線程可以監(jiān)視多個通道中的數(shù)據(jù)。當方法返回后,線程可以處理這些事件。 一 NIO簡介 Java NIO 是 java 1.4 之后新出的一套IO接口,這里的的新是相對于原有標準的Java IO和Java Networking接口。NIO提供了一種完全不同的操作方式。 NIO中的N可以理解為Non-blocking,不單純是New。 它支持面...
摘要:從通道進行數(shù)據(jù)寫入創(chuàng)建一個緩沖區(qū),填充數(shù)據(jù),并要求通道寫入數(shù)據(jù)。三之通道主要內(nèi)容通道介紹通常來說中的所有都是從通道開始的。從中選擇選擇器維護注冊過的通道的集合,并且這種注冊關(guān)系都被封裝在當中停止選擇的方法方法和方法。 由于內(nèi)容比較多,我下面放的一部分是我更新在我的微信公眾號上的鏈接,微信排版比較好看,更加利于閱讀。每一篇文章下面我都把文章的主要內(nèi)容給列出來了,便于大家學習與回顧。 Ja...
摘要:簡介是由引進的異步由以下幾個核心部分組成和的對比和的區(qū)別主要體現(xiàn)在三個方面基于流而基于操作是阻塞的而操作是非阻塞的沒有概念而有概念基于與基于傳統(tǒng)的是面向字節(jié)流或字符流的而在中我們拋棄了傳統(tǒng)的流而是引入了和的概念在中我只能從中讀取數(shù)據(jù)到中或?qū)? 簡介 Java NIO 是由 Java 1.4 引進的異步 IO.Java NIO 由以下幾個核心部分組成: Channel Buffer Se...
摘要:學習和掌握技術(shù)已經(jīng)不是一個攻城獅的加分技能,而是一個必備技能。是雙向的,不僅可以讀取數(shù)據(jù)還能保存數(shù)據(jù),程序不能直接讀寫通道,只與緩沖區(qū)交互為了讓大家不被高并發(fā)與大量連接處理問題所困擾,動力節(jié)點推出了高效處理模型應(yīng)用教程。 大家肯定了解Java IO, 但是對于NIO一般是陌生的,而現(xiàn)在使用到NIO的場景越來越多,很多技術(shù)框...
摘要:后改良為用線程池的方式代替新增線程,被稱為偽異步。最大的問題是阻塞,同步。每次請求都由程序執(zhí)行并返回,這是同步的缺陷。這些都會被注冊在多路復(fù)用器上。多路復(fù)用器提供選擇已經(jīng)就緒狀態(tài)任務(wù)的能力。并沒有采用的多路復(fù)用器,而是使用異步通道的概念。 Netty是一個提供異步事件驅(qū)動的網(wǎng)絡(luò)應(yīng)用框架,用以快速開發(fā)高性能、高可靠的網(wǎng)絡(luò)服務(wù)器和客戶端程序。Netty簡化了網(wǎng)絡(luò)程序的開發(fā),是很多框架和公司...
閱讀 1144·2021-11-12 10:34
閱讀 1058·2021-09-30 09:56
閱讀 732·2019-08-30 15:54
閱讀 2681·2019-08-30 11:14
閱讀 1566·2019-08-29 16:44
閱讀 3274·2019-08-29 16:35
閱讀 2553·2019-08-29 16:22
閱讀 2515·2019-08-29 15:39