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