NIO
流與塊
通道與緩沖區
緩沖區狀态變量
檔案 NIO 執行個體
選擇器
套接字 NIO 執行個體
記憶體映射檔案
NIO與IO對比
Path
Files
新的輸入/輸出 (NIO) 庫是在 JDK 1.4 中引入的,彌補了原來的 I/O 的不足,提供了高速的、面向塊的 I/O。
NIO核心元件:
通道(Channels)
緩沖區(Buffers)
選擇器(Selectors)
I/O 與 NIO 最重要的差別是資料打包和傳輸的方式,I/O 以流的方式處理資料,而 NIO 以塊的方式處理資料。
面向流的 I/O 一次處理一個位元組資料:一個輸入流産生一個位元組資料,一個輸出流消費一個位元組資料。 為流式資料建立過濾器非常容易,連結幾個過濾器,以便每個過濾器隻負責複雜處理機制的一部分。不利的一面是,面向流的 I/O 通常相當慢。
面向塊的 I/O 一次處理一個資料塊,按塊處理資料比按流處理資料要快得多。 但是面向塊的 I/O 缺少一些面向流的 I/O 所具有的優雅性和簡單性。
I/O 包和 NIO 已經很好地內建了,java.io. 已經以 NIO 為基礎重新實作了,是以現在它可以利用 NIO 的一些特性。 例如,java.io. 包中的一些類包含以塊的形式讀寫資料的方法,這使得即使在面向流的系統中,處理速度也會更快。
-
通道
通道 Channel 是對原 I/O 包中的流的模拟,可以通過它讀取和寫入資料。
通道與流的不同之處在于,流隻能在一個方向上移動(一個流必須是 InputStream 或者 OutputStream 的子類), 而通道是雙向的,可以用于讀、寫或者同時用于讀寫。
通道包括以下類型:
FileChannel:從檔案中讀寫資料;
DatagramChannel:通過 UDP 讀寫網絡中資料;
SocketChannel:通過 TCP 讀寫網絡中資料;
ServerSocketChannel:可以監聽新進來的 TCP 連接配接,對每一個新進來的連接配接都會建立一個 SocketChannel。
-
緩沖區
發送給一個通道的所有資料都必須首先放到緩沖區中,同樣地,從通道中讀取的任何資料都要先讀到緩沖區中。也就是說,不會直接對通道進行讀寫資料,而是要先經過緩沖區。
緩沖區實質上是一個數組,但它不僅僅是一個數組。緩沖區提供了對資料的結構化通路,而且還可以跟蹤系統的讀/寫程序。
緩沖區包括以下類型:
ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
capacity:最大容量;
position:目前已經讀寫的位元組數;
limit:還可以讀寫的位元組數。
狀态變量的改變過程舉例:
① 建立一個大小為 8 個位元組的緩沖區,此時 position 為 0,而 limit = capacity = 8。capacity 變量不會改變,下面的讨論會忽略它。
② 從輸入通道中讀取 5 個位元組資料寫入緩沖區中,此時 position 為 5,limit 保持不變。
③ 在将緩沖區的資料寫到輸出通道之前,需要先調用 flip() 方法,這個方法将 limit 設定為目前 position,并将 position 設定為 0。
④ 從緩沖區中取 4 個位元組到輸出緩沖中,此時 position 設為 4。
⑤ 最後需要調用 clear() 方法來清空緩沖區,此時 position 和 limit 都被設定為最初位置。
FileChannel的使用
開啟FileChannel
從FileChannel讀取資料/寫入資料
3.關閉FileChannel
public class FileChannelDemo {
public static void main(String[] args) throws IOException {
//1.建立一個RandomAccessFile(随機通路檔案)對象通過RandomAccessFile對象的getChannel()方法。
RandomAccessFile raf=new RandomAccessFile("demo6.txt","rw");
FileChannel fc=raf.getChannel();
//使用FileChannel的read()方法讀取資料:
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
int bys=fc.read(byteBuffer);
//使用FileChannel的write()方法寫入資料:
ByteBuffer byteBuffer2=ByteBuffer.allocate(1024);
byteBuffer2.put("hello".getBytes());
fc.write(byteBuffer2);
//3.關閉FileChannel
fc.close();
}
}
以下展示了使用 NIO 快速複制檔案的執行個體:
public class CopyFile {
public static void main(String[] args) throws IOException {
String srcFile="國旗歌.mp4";
String destFile="demo3.mp4";
long start = System.currentTimeMillis();
//copyFile(srcFile,destFile); //共耗時:75309毫秒
//copyFile2(srcFile,destFile); //共耗時:153毫秒
//copyFile3(srcFile,destFile);//共耗時:282毫秒
//copyFile4(srcFile,destFile);//共耗時:44毫秒
copyFile5(srcFile,destFile);//共耗時:共耗時:113毫秒
long end = System.currentTimeMillis();
System.out.println("共耗時:" + (end - start) + "毫秒");
}
/**
* 基本位元組流一次讀寫一個位元組
*/
public static void copyFile(String srcFile,String destFile) throws IOException {
FileInputStream fis=new FileInputStream(srcFile);
FileOutputStream fos=new FileOutputStream(destFile);
int by=0;
while((by=fis.read())!=-1){
fos.write(by);
}
fis.close();
fos.close();
}
/**
* 基本位元組流一次讀寫一個位元組數組
*/
public static void copyFile2(String srcFile,String destFile) throws IOException{
FileInputStream fis=new FileInputStream(srcFile);
FileOutputStream fos=new FileOutputStream(destFile);
int len=0;
byte[] bys=new byte[1024];
while((len=fis.read(bys))!=-1){
fos.write(bys,0,len);
}
fis.close();
fos.close();
}
/**
* 高效位元組流一次讀寫一個位元組
*/
public static void copyFile3(String srcFile,String destFile) throws IOException{
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(destFile));
int by=0;
while((by=bis.read())!=-1){
bos.write(by);
}
bis.close();
bos.close();
}
/**
* 高效位元組流一次讀寫一個位元組數組
*/
public static void copyFile4(String srcFile,String destFile) throws IOException{
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(destFile));
int len=0;
byte[] bys=new byte[1024];
while((len=bis.read(bys))!=-1){
bos.write(bys,0,len);
}
bis.close();
bos.close();
}
/**
* 使用FileChannel複制檔案
*/
public static void copyFile5(String srcFile,String destFile) throws IOException{
FileInputStream fis=new FileInputStream(srcFile);
//擷取輸入位元組流的檔案通道
FileChannel fcin=fis.getChannel();
FileOutputStream fos=new FileOutputStream(destFile);
//擷取輸出位元組流的檔案通道
FileChannel fcout=fos.getChannel();
//為緩沖區配置設定 1024 個位元組
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
while(true){
//從輸入通道中讀取資料到緩沖區中
int r = fcin.read(buffer);
// read() 傳回 -1 表示 EOF
if(r==-1){
break;
}
//切換讀寫
buffer.flip();
//把緩沖區的内容寫入輸出檔案中
fcout.write(buffer);
//清空緩沖區
buffer.clear();
}
}
SocketChannel和ServerSocketChannel的使用
SocketChannel用于建立基于TCP協定的用戶端對象,因為SocketChannel中不存在accept()方法, 是以,它不能成為一個服務端程式。 通過connect()方法,SocketChannel對象可以連接配接到其他TCP伺服器程式。
ServerSocketChannel允許我們監聽TCP協定請求,通過ServerSocketChannel的accept()方法建立一個SocketChannel對象使用者從用戶端讀/寫資料。
服務端:
通過ServerSocketChannel 綁定ip位址和端口号
通過ServerSocketChannel的accept()方法建立一個SocketChannel對象使用者從用戶端讀/寫資料
建立讀資料/寫資料緩沖區對象來讀取用戶端資料或向用戶端發送資料
關閉SocketChannel和ServerSocketChannel
public class Server {
public static void main(String[] args) throws IOException {
//通過ServerSocketChannel 的open()方法建立一個ServerSocketChannel對象
ServerSocketChannel ssc=ServerSocketChannel.open();
//1. 通過ServerSocketChannel 綁定ip位址和端口号
ssc.socket().bind(new InetSocketAddress(InetAddress.getByName("LAPTOP-D9966H06"),8888));
//2. 通過ServerSocketChannel的accept()方法建立一個SocketChannel對象使用者從用戶端讀/寫資料
SocketChannel sc=ssc.accept();
//3. 建立讀資料/寫資料緩沖區對象來讀取用戶端資料或向用戶端發送資料
//讀取用戶端發送的資料
ByteBuffer buffer=ByteBuffer.allocate(1024);
//從通道中讀取資料到緩沖區
sc.read(buffer);
StringBuffer sb=new StringBuffer();
buffer.flip();
while(buffer.hasRemaining()){
sb.append((char)buffer.get());
}
System.out.println(sb.toString());
ByteBuffer buffer2=ByteBuffer.allocate(1024);
//向用戶端發送資料
buffer2.put("data has been received.".getBytes());
buffer2.flip();
sc.write(buffer2);
//4. 關閉SocketChannel和ServerSocketChannel
sc.close();
ssc.close();
}
用戶端:
1.通過SocketChannel連接配接到遠端伺服器
2.建立讀資料/寫資料緩沖區對象來讀取服務端資料或向服務端發送資料
3.關閉SocketChannel
public class Client {
public static void main(String[] args) throws IOException {
//1.通過SocketChannel連接配接到遠端伺服器
SocketChannel sc=SocketChannel.open();
sc.connect(new InetSocketAddress(InetAddress.getByName("LAPTOP-D9966H06"),8888));
//2.建立讀資料/寫資料緩沖區對象來讀取服務端資料或向服務端發送資料
//向通道中寫入資料
ByteBuffer buffer=ByteBuffer.allocate(1024);
buffer.put("hello".getBytes());
buffer.flip();
sc.write(buffer);
//讀取從用戶端中擷取的資料
ByteBuffer buffer2=ByteBuffer.allocate(1024);
sc.read(buffer2);
StringBuffer sb=new StringBuffer();
buffer2.flip();
while(buffer2.hasRemaining()){
sb.append((char)buffer2.get());
}
System.out.println(sb.toString());
//3.關閉SocketChannel
sc.close();
}
DatagramChannel的使用
DataGramChannel,類似于java 網絡程式設計的DatagramSocket類; 使用UDP進行網絡傳輸, UDP是無連接配接,面向資料封包段的協定。
服務端:
public static void main(String[] args) throws IOException {
DatagramChannel dc= DatagramChannel.open();
dc.bind(new InetSocketAddress(InetAddress.getByName("LAPTOP-D9966H06"),8888));
//建立讀資料/寫資料緩沖區對象來讀取用戶端資料或向用戶端發送資料
//讀取用戶端發送的資料
ByteBuffer buffer=ByteBuffer.allocate(1024);
//從通道中讀取資料到緩沖區
dc.receive(buffer);
StringBuffer sb=new StringBuffer();
buffer.flip();
while(buffer.hasRemaining()){
sb.append((char)buffer.get());
}
System.out.println(sb.toString());
ByteBuffer buffer2=ByteBuffer.allocate(1024);
//向用戶端發送資料
buffer2.put("data has been received.".getBytes());
buffer2.flip();
dc.send(buffer2,new InetSocketAddress(InetAddress.getByName("LAPTOP-D9966H06"),9999));
dc.close();
}
public static void main(String[] args) throws IOException {
DatagramChannel dc= DatagramChannel.open();
dc.bind(new InetSocketAddress(InetAddress.getByName("LAPTOP-D9966H06"),9999));
//建立讀資料/寫資料緩沖區對象來讀取服務端資料或向服務端發送資料
//向通道中寫入資料
ByteBuffer buffer=ByteBuffer.allocate(1024);
buffer.put("hello".getBytes());
buffer.flip();
dc.send(buffer,new InetSocketAddress(InetAddress.getByName("LAPTOP-D9966H06"),8888));
//讀取從用戶端中擷取的資料
ByteBuffer buffer2=ByteBuffer.allocate(1024);
dc.receive(buffer2);
StringBuffer sb=new StringBuffer();
buffer2.flip();
while(buffer2.hasRemaining()){
sb.append((char)buffer2.get());
}
System.out.println(sb.toString());
dc.close();
}
通道之間的資料傳輸
在Java NIO中如果一個channel是FileChannel類型的,那麼他可以直接把資料傳輸到另一個channel。
transferFrom() :transferFrom方法把資料從通道源傳輸到FileChannel
transferTo() :transferTo方法把FileChannel資料傳輸到另一個FileChhannel
public static void copyFile6(String srcFile,String destFile) throws IOException {
FileInputStream fis = new FileInputStream(srcFile);
//擷取輸入位元組流的檔案通道
FileChannel fcin = fis.getChannel();
FileOutputStream fos = new FileOutputStream(destFile);
//擷取輸出位元組流的檔案通道
FileChannel fcout = fos.getChannel();
//fcin通道中讀出count bytes ,并寫入fcout通道中
//fcin.transferTo(0,fcin.size(),fcout);
//或者
fcout.transferFrom(fcin,0,fcin.size());
NIO 常常被叫做非阻塞 IO,主要是因為 NIO 在網絡通信中的非阻塞特性被廣泛使用。
NIO 實作了 IO 多路複用中的 Reactor 模型,一個線程 Thread 使用一個選擇器 Selector 通過輪詢的方式 去監聽多個通道 Channel 上的事件,進而讓一個線程就可以處理多個事件。
通過配置監聽的通道 Channel 為非阻塞,那麼當 Channel 上的 IO 事件還未到達時, 就不會進入阻塞狀态一直等待,而是繼續輪詢其它 Channel,找到 IO 事件已經到達的 Channel 執行。
因為建立和切換線程的開銷很大,是以使用一個線程來處理多個事件而不是一個線程處理一個事件, 對于 IO 密集型的應用具有很好地性能。
應該注意的是,隻有套接字 Channel 才能配置為非阻塞,而 FileChannel 不能, 為 FileChannel 配置非阻塞也沒有意義。
使用Selector的優點:
使用更少的線程來就可以來處理通道了, 相比使用多個線程, 避免了線程上下文切換帶來的開銷。
-
建立選擇器
Selector selector = Selector.open();
-
将通道注冊到選擇器上
ServerSocketChannel ssChannel = ServerSocketChannel.open();
ssChannel.configureBlocking(false);//通道必須配置為非阻塞模式
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
通道必須配置為非阻塞模式,否則使用選擇器就沒有任何意義了,因為如果通道在某個事件上被阻塞,那麼伺服器就不能響應其它事件,必須等待這個事件處理完畢才能去處理其它事件,顯然這和選擇器的作用背道而馳。
在将通道注冊到選擇器上時,還需要指定要注冊的具體事件,主要有以下幾類:
SelectionKey.OP_CONNECT
SelectionKey.OP_ACCEPT
SelectionKey.OP_READ
SelectionKey.OP_WRITE
它們在 SelectionKey 的定義如下:
public static final int OP_READ = 1 << 0;
public static final int OP_WRITE = 1 << 2;
public static final int OP_CONNECT = 1 << 3;
public static final int OP_ACCEPT = 1 << 4;
可以看出每個事件可以被當成一個位域,進而組成事件集整數。例如:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
- 監聽事件
- num = selector.select();
使用 select() 來監聽到達的事件,它會一直阻塞直到有至少一個事件到達。
-
擷取到達的事件
Set keys = selector.selectedKeys();
Iterator keyIterator = keys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
// ...
} else if (key.isReadable()) {
// ...
}
keyIterator.remove();
-
事件循環
因為一次 select() 調用不能處理完所有的事件,并且伺服器端有可能需要一直監聽事件,是以伺服器端處理事件的代碼一般會放在一個死循環内。
while (true) {
int num = selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = keys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
// ...
} else if (key.isReadable()) {
// ...
}
keyIterator.remove();
}
public class NIOServer {
public static void main(String[] args) throws IOException {
//1. 建立選擇器
Selector selector = Selector.open();
//2.将通道注冊到選擇器上
ServerSocketChannel ssChannel = ServerSocketChannel.open();
ssChannel.configureBlocking(false);
//通道必須配置為非阻塞模式,否則使用選擇器就沒有任何意義了
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
ServerSocket ss=ssChannel.socket();
ss.bind(new InetSocketAddress("127.0.0.1",8888));
while (true){
//3. 監聽事件
selector.select();
//4. 擷取到達的事件
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = keys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
ServerSocketChannel ssChannel1 = (ServerSocketChannel) key.channel();
// 伺服器會為每個新連接配接建立一個 SocketChannel
SocketChannel sChannel = ssChannel1.accept();
sChannel.configureBlocking(false);
// 這個新連接配接主要用于從用戶端讀取資料
sChannel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel sChannel = (SocketChannel) key.channel();
System.out.println(readDataFromSocketChannel(sChannel));
sChannel.close();
}
keyIterator.remove();
}
}
}
private static String readDataFromSocketChannel(SocketChannel sChannel) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(1024);
StringBuilder data = new StringBuilder();
while (true) {
buffer.clear();
int r = sChannel.read(buffer);
if (r == -1) {
break;
}
buffer.flip();
int limit = buffer.limit();
char[] dst = new char[limit];
for (int i = 0; i < limit; i++) {
dst[i] = (char) buffer.get(i);
}
data.append(dst);
buffer.clear();
}
return data.toString();
}
public class NIOClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8888);
OutputStream out = socket.getOutputStream();
String s = "hello world";
out.write(s.getBytes());
out.close();
}
記憶體映射檔案 I/O 是一種讀和寫檔案資料的方法,它可以比正常的基于流或者基于通道的 I/O 快得多。
向記憶體映射檔案寫入可能是危險的,隻是改變數組的單個元素這樣的簡單操作,就可能會直接修改磁盤上的檔案。修改資料與将資料儲存到磁盤是沒有分開的。
下面代碼行将檔案的前 1024 個位元組映射到記憶體中,map() 方法傳回一個 MappedByteBuffer,它是 ByteBuffer 的子類。是以,可以像使用其他任何 ByteBuffer 一樣使用新映射的緩沖區,作業系統會在需要時負責執行映射。
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
NIO 與普通 I/O 的差別主要有以下三點:
NIO 是非阻塞的;
NIO 面向塊,I/O 面向流。
NIO有選擇器,而I/O沒有。
Java7中檔案IO發生了很大的變化,專門引入了很多新的類來取代原來的 基于java.io.File的檔案IO操作方式。
建立一個Path
使用Paths工具類的get()方法建立Path對象
public class PathDemo {
public static void main(String[] args) {
//方式一
Path path=Paths.get("demo5.txt");
System.out.println(path);
//方式二
Path path2 = FileSystems.getDefault().getPath("demo5.txt");
System.out.println(path2);
}
File和Path之間的轉換,File和URI之間的轉換
public class PathDemo2 {
public static void main(String[] args) {
Path path=Paths.get("demo5.txt");
File file=path.toFile();
URI uri=path.toUri();
System.out.println(path);
System.out.println(file);
System.out.println(uri);
}
demo5.txt
file:///F:/Java_Review/05Java/JavaIO/demo5.txt
擷取Path的相關資訊
public class PathDemo3 {
public static void main(String[] args) {
Path path= Paths.get("demo3\\test3.txt");
System.out.println("檔案名:"+ path.getFileName());
System.out.println("名稱元素的數量:"+path.getNameCount());
System.out.println("父路徑:"+ path.getParent());
System.out.println("根路徑:"+ path.getRoot());
System.out.println("是否是絕對路徑:"+path.isAbsolute());
//startWith() 參數既可以是字元串,也可以是Path
System.out.println("是否是以路徑demo3開頭:"+path.startsWith(Paths.get("demo3")));
System.out.println("該路徑的字元串形式:"+path.toString());
}
檔案名:test3.txt
名稱元素的數量:2
父路徑:demo3
根路徑:null
是否是絕對路徑:false
是否是以路徑demo3開頭:true
該路徑的字元串形式:demo3test3.txt
移除Path中的備援項
.表示的是目前目錄
..表示父目錄或者說是上一級目錄
normalize() : 傳回一個路徑,該路徑是取出備援項的路徑。
toRealPath() : 可以看成,先進行toAbsolutePath()操作,然後進行normalize()操作
public class PathDemo4 {
public static void main(String[] args) throws IOException {
Path path= Paths.get("./demo3");
System.out.println("original :"+ path.toAbsolutePath());
System.out.println("after normalize:"+ path.toAbsolutePath().normalize());
System.out.println("after toRealPath:"+ path.toRealPath());
}
original :F:Java_Review05JavaJavaIO.demo3
after normalize:F:Java_Review05JavaJavaIOdemo3
after toRealPath:F:Java_Review05JavaJavaIOdemo3
public class PathDemo5 {
public static void main(String[] args) throws IOException {
Path path= Paths.get("../JavaIO");
System.out.println("original :"+ path.toAbsolutePath());
System.out.println("after normalize:"+ path.toAbsolutePath().normalize());
System.out.println("after toRealPath:"+ path.toRealPath());
}
original :F:Java_Review05JavaJavaIO..JavaIO
after normalize:F:Java_Review05JavaJavaIO
after toRealPath:F:Java_Review05JavaJavaIO
java.nio.file.Files類是和java.nio.file.Path相結合使用的
檢查給定的Path在檔案系統中是否存在
Files.exists():檢測檔案路徑是否存在
public class FilesDemo {
public static void main(String[] args) {
Path path = Paths.get("demo5.txt");
//LinkOptions.NOFOLLOW_LINKS:表示檢測時不包含符号連結檔案。
boolean isExist= Files.exists(path,new LinkOption[]{LinkOption.NOFOLLOW_LINKS});
System.out.println(isExist);
}
建立檔案/檔案夾
Files.createFile():建立檔案
Files.createDirectory(): 建立檔案夾
Files.createDirectories(): 建立檔案夾
public class FilesDemo2 {
public static void main(String[] args) throws IOException {
Path path= Paths.get("demo7.txt");
if(!Files.exists(path)){
Files.createFile(path);
}
Path path2=Paths.get("demo4");
if(!Files.exists(path2)){
Files.createDirectory(path2);
}
Path path3=Paths.get("demo5\\test");
if(!Files.exists(path3)){
Files.createDirectories(path3);
}
}
删除檔案或目錄
Files.delete():删除一個檔案或目錄
public class FilesDemo3 {
public static void main(String[] args) throws IOException {
Path path= Paths.get("demo7.txt");
Files.delete(path);
}
把一個檔案從一個位址複制到另一個位置
Files.copy():把一個檔案從一個位址複制到另一個位置
public class FilesDemo4 {
public static void main(String[] args) throws IOException {
Path srcPath= Paths.get("demo6.txt");
Path destPath=Paths.get("demo7.txt");
//Files.copy(srcPath,destPath);
//強制覆寫已經存在的目标檔案
Files.copy(srcPath,destPath, StandardCopyOption.REPLACE_EXISTING);
}
擷取檔案屬性
public class FilesDemo5 {
public static void main(String[] args) throws IOException {
Path path= Paths.get("demo7.txt");
System.out.println(Files.getLastModifiedTime(path));
System.out.println(Files.size(path));
System.out.println(Files.isSymbolicLink(path));
System.out.println(Files.isDirectory(path));
System.out.println(Files.readAttributes(path,"*"));
}
周遊一個檔案夾
public class FilesDemo6 {
public static void main(String[] args) throws IOException {
Path path= Paths.get("demo3\\demo2");
DirectoryStream<Path> paths=Files.newDirectoryStream(path);
for(Path p:paths){
System.out.println(p.getFileName());
}
}
周遊整個檔案目錄
FileVisitor需要調用方自行實作,然後作為參數傳入walkFileTree(); FileVisitor的每個方法會在周遊過程中被調用多次。
public class FilesDemo7 {
public static void main(String[] args) throws IOException {
Path path= Paths.get("demo3\\demo2");
List<Path> paths=new ArrayList<>();
Files.walkFileTree(path,new FileVisitor(paths));
System.out.println("paths:"+paths);
}
private static class FileVisitor extends SimpleFileVisitor<Path> {
private List<Path> paths;
public FileVisitor(List<Path> paths){
this.paths=paths;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if(file.toString().endsWith(".txt")){
paths.add(file.getFileName());
}
return super.visitFile(file, attrs);
}
}
輸出結果:
paths:[a.txt, test2.txt, test.txt, test3.txt]