Java面試必問通訊框架NIO,原理詳解

NIO
流與塊
通道與緩衝區
緩衝區狀態變量
文件 NIO 實例
選擇器
套接字 NIO 實例
內存映射文件
NIO與IO對比
Path
Files
NIO
新的輸入/輸出 (NIO) 庫是在 JDK 1.4 中引入的,彌補了原來的 I/O 的不足,提供了高速的、面向塊的 I/O。java

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. 包中的一些類包含以塊的形式讀寫數據的方法,這使得即便在面向流的系統中,處理速度也會更快。併發

通道與緩衝區app

  1. 通道

通道 Channel 是對原 I/O 包中的流的模擬,能夠經過它讀取和寫入數據。dom

通道與流的不一樣之處在於,流只能在一個方向上移動(一個流必須是 InputStream 或者 OutputStream 的子類), 而通道是雙向的,能夠用於讀、寫或者同時用於讀寫。socket

通道包括如下類型:分佈式

FileChannel:從文件中讀寫數據;
DatagramChannel:經過 UDP 讀寫網絡中數據;
SocketChannel:經過 TCP 讀寫網絡中數據;
ServerSocketChannel:能夠監聽新進來的 TCP 鏈接,對每個新進來的鏈接都會建立一個 SocketChannel。

  1. 緩衝區

發送給一個通道的全部數據都必須首先放到緩衝區中,一樣地,從通道中讀取的任何數據都要先讀到緩衝區中。也就是說,不會直接對通道進行讀寫數據,而是要先通過緩衝區。

緩衝區實質上是一個數組,但它不只僅是一個數組。緩衝區提供了對數據的結構化訪問,並且還能夠跟蹤系統的讀/寫進程。

緩衝區包括如下類型:

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 都被設置爲最初位置。

文件 NIO 實例
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 class Server {

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 class Client {

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的優勢:

使用更少的線程來就能夠來處理通道了, 相比使用多個線程, 避免了線程上下文切換帶來的開銷。

  1. 建立選擇器

Selector selector = Selector.open();

  1. 將通道註冊到選擇器上

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;

  1. 監聽事件

int num = selector.select();
使用 select() 來監聽到達的事件,它會一直阻塞直到有至少一個事件到達。

  1. 獲取到達的事件

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

}

  1. 事件循環

由於一次 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();
}

}
套接字 NIO 實例
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與IO對比
NIO 與普通 I/O 的區別主要有如下三點:

NIO 是非阻塞的;
NIO 面向塊,I/O 面向流。
NIO有選擇器,而I/O沒有。
Path
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
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
Files
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]
免費Java高級資料須要本身領取,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高併發分佈式等教程,一共30G。
傳送門:https://mp.weixin.qq.com/s/Jz...

相關文章
相關標籤/搜索