IO流整理及案例(文件複製),NIO的概念

目錄

1.IO流

1)數據傳輸類型分類

2)數據流向分類

3)按功能作用分類

2.文件複製案例(IO流與NIO管道)

1)IO流複製

2)NIO通道複製

3.NIO

1)NIO的核心

1) NIO示例


流是對數據傳輸的抽象,特性是進行數據的傳輸.
按照流向可以分爲輸入流和輸出流.
按數據的處理類型可以分爲字節流和字符流.

1.IO流

1)數據傳輸類型分類

字節流:傳輸的單位是字節,用於處理所有數據類型,字節流的操作不會經過緩衝區(內存)而是直接操作文本本身
字符流:傳輸的單位是字符,用於處理文本類型,字符流的操作會先經過緩衝區(內存)然後通過緩衝區再操作文件

2)數據流向分類

輸入流:InputStream,FileInputStream,DataInputStream,Reader,FileReader...
輸出流:OutputStream,FileOutputStream,DataOutputStream,Writer,FileWriter...

3)按功能作用分類

節點流:直接與數據相連,進行數據的讀寫;
->常用的是InputStream,FileInputStream,OutputStream,FileOutputStream

處理流:是指在節點流上套接了一層;
->BufferedInputStrean,BufferedOutputStream,BufferedReader,BufferedWriter

轉換流:將字節流轉換爲字符流;
->InputStreamReader,OutputStreamReader

2.文件複製案例(IO流與NIO管道)

1)IO流複製

package com.glperry.demo.io;

import org.junit.Test;
import java.io.*;

/**
 * 用於IO流的分析總結
 * 從IO流的體系來說,分爲字節流和字符流
 * ->字節流:以字節爲單位,處理所有類型的數據 byte. 最小
 * ->字符流:以字符爲單位,只能處理字符型數據
 * ->使用總結:只要是純文本,優先字符流,其他的都要使用字節流
 * 字節流->字符流 轉換可通過InputStreamReader/OutPutStreamWriter
 * 字符流->字節流 無法轉換
 * @Date: 2019-08-28
 */
public class IOAnalysis {

    /**
     * 字節流寫入文本/InputStream/OutputStream
     */
    @Test
    public void streamCopy() throws IOException {
        OutputStream os = new FileOutputStream("E:\\PerryWorkspace\\demo\\src\\main\\resources\\io\\1.txt");
        //輸入 iDoLoveJava
        os.write("iDoLoveJava".getBytes());
        os.flush();
        os.close();
    }


    /**
     * 字節流複製文本/InputStream/OutputStream
     */
    @Test
    public void streamCopyII() throws IOException {
        OutputStream os = new FileOutputStream("E:\\PerryWorkspace\\demo\\src\\main\\resources\\io\\1_bak.txt");
        File file = new File("E:\\PerryWorkspace\\demo\\src\\main\\resources\\io\\1.txt");
        FileInputStream is = new FileInputStream(file);
       byte[] bytes = new byte[1024];
       int len;
       while ((len=is.read(bytes))>0){
            os.write(bytes,0,len);
       }
       os.flush();
       os.close();
    }

    /**
     * 字節流複製圖片/InputStream/OutputStream
     */
    @Test
    public void streamCopyIII() throws IOException {
        OutputStream os = new FileOutputStream("E:\\PerryWorkspace\\demo\\src\\main\\resources\\io\\1_bak.jpg");
        File file = new File("E:\\PerryWorkspace\\demo\\src\\main\\resources\\io\\1.jpg");
        FileInputStream is = new FileInputStream(file);
        byte[] bytes = new byte[1024];
        int len;
        while ((len=is.read(bytes))>0){
            os.write(bytes,0,len);
        }
        os.flush();
        os.close();
    }

    /**
     * 字符流複製文本/InputStream/OutputStream
     */
    @Test
    public void readerCopy() throws IOException {
        FileWriter fw = new FileWriter("E:\\PerryWorkspace\\demo\\src\\main\\resources\\io\\1_bakII.txt");
        File file = new File("E:\\PerryWorkspace\\demo\\src\\main\\resources\\io\\1.txt");
        FileReader fr = new FileReader(file);
        char[] chs = new char[1024];
        int len = fr.read(chs);
        while (len!=-1){
            fw.write(chs,0,len);
            fw.flush();
            len = fr.read(chs);
        }
        fr.close();
        fw.close();
    }

    /**
     * 字符流複製圖片/Reader/Writer,字符流傳輸圖片出現圖片錯誤!!!
     */
    @Test
    public void readerCopyII() throws IOException {
        FileWriter fw = new FileWriter("E:\\PerryWorkspace\\demo\\src\\main\\resources\\io\\1_bakII.jpg");
        File file = new File("E:\\PerryWorkspace\\demo\\src\\main\\resources\\io\\1.jpg");
        FileReader fr = new FileReader(file);
        char[] chs = new char[1024];
        int len = fr.read(chs);
        while (len!=-1){
            fw.write(chs,0,len);
            fw.flush();
            len = fr.read(chs);
        }
        fr.close();
        fw.close();
    }
}

2)NIO通道複製

 /**
     * NIO(Java1.4後New IO,第二種IO)方式複製文件,循環讀取方式
     */
    @Test
    public void nioCopy() throws IOException {
        long start = System.currentTimeMillis();
        //定義輸入輸出路徑
        FileInputStream is = new FileInputStream("E:\\PerryWorkspace\\demo\\src\\main\\resources\\io\\14MB.jpg");
        FileOutputStream os = new FileOutputStream("E:\\PerryWorkspace\\demo\\src\\main\\resources\\io\\1_nio2.jpg");
        //操作文件不通過流而是通過管道
        FileChannel iChannel  = is.getChannel();
        FileChannel oChannel = os.getChannel();
        //定義緩衝區Buffer分配緩存空間
        ByteBuffer buff = ByteBuffer.allocate(1024);

        long end = 0;
        //循環讀取寫出數據
        while(iChannel.read(buff)!=-1){
            iChannel.read(buff);
            //讀寫文件時修改緩衝區的讀寫模式
            buff.flip();
            oChannel.write(buff);
            //刷新緩衝區
            buff.clear();
            end = System.currentTimeMillis();
        }
        System.out.println(end-start);
    }

    /**
     * NIO(Java1.4後New IO,第二種IO)方式複製文件,自帶方法
     */
    @Test
    public void nioCopyII() throws IOException {
        long start = System.currentTimeMillis();
        //定義輸入輸出路徑
        FileInputStream is = new FileInputStream("E:\\PerryWorkspace\\demo\\src\\main\\resources\\io\\14MB.jpg");
        FileOutputStream os = new FileOutputStream("E:\\PerryWorkspace\\demo\\src\\main\\resources\\io\\1_nio2.jpg");
        //操作文件不通過流而是通過管道
        FileChannel iChannel = is.getChannel();
        FileChannel oChannel = os.getChannel();
        oChannel.transferFrom(iChannel,0,iChannel.size());
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }

總結:通過複製文件NIO與IO對比,可以得出NIO自帶方法平均執行時間最短,只有IO流的一半. 文件複製應該選用NIO自帶方法.

3.NIO

標準的IO面向數據流,基於字節流和字符流進行操作的;

而NIO是面向緩衝區,數據從Channel讀取到Buffer緩衝區,隨後在Buffer中處理數據。基於通道(Channel)和緩衝區(Buffer)進行操作,數據總是從通道讀取到緩衝區中,或者從緩衝區寫入到通道中。

對於NIO是非阻塞,主要體現在SocketChannel上,channel設置爲非阻塞,channel的accept/讀寫方法是非阻塞的,且Selector使得單線程可以註冊多個channel,監聽多個通道的事件,採用輪詢的方式監聽selector上是否有需要處理的事件,如果有,則進行處理.
而 FileChannel用於文件傳輸複製,進行讀寫的時候是阻塞的,也未提供設置是否阻塞的方法.

補充下同步異步/阻塞非阻塞知識:

1)NIO的核心

#核心元素
Buffer:一塊緩存區,包含元素position,limit,capacity,mark
常用的Buffer類:ByteBuffer,CharBuffer,DoubleBuffer,FloatBuffer,IntBuffer,LongBuffer,ShortBuffer,MappedByteBuffer
   
Channel:通道,類似於IO中的流(Stream),雙向異步的
 常用的Channel類:FileChannel,DatagramChannel,SocketChannel,ServerSocketChannel
   
Selector:選擇處理器,通過註冊到Channel中,Selector用於單個線程註冊多個Channel,監聽多個通道的事件.
selectionKey是Channel在Selector中註冊的句柄/指針/標記.

1) NIO示例

上面有NIO使用Channel和Buffer的文件複製案例,下面將給出Channel,Buffer,Selector請求處理案例(客戶端發送數據,服務端獲取)

#服務端

package com.glperry.demo.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * 服務端,接收數據
 * @Date: 2019-08-29
 */
public class NioServer{

    public static void main(String[] args) throws IOException {
        //打開一個服務連接通道
        ServerSocketChannel channel = ServerSocketChannel.open();
        //創建服務連接地址
        InetSocketAddress socketAddress = new InetSocketAddress(8888);
        //通道綁定服務地址
        channel.bind(socketAddress);
        //設置服務端通道非阻塞
        channel.configureBlocking(false);
        //打開選擇處理器
        Selector selector = Selector.open();
        //模擬多個用戶,使用selector選擇器進行響應
        //首先第一步進來的所有連接都是爲了接入
        channel.register(selector,SelectionKey.OP_ACCEPT);
        //selector 處理接入
        while (selector.select()>0){
            //可能客戶到請求一次進來多個,進行判斷
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                //獲取其中的每個請求
                SelectionKey next = iterator.next();
                //判斷請求是屬於 連接過的還是未連接過的
                if(next.isAcceptable()){ //沒連接過,直接跟蹤,設置爲讀取狀態
                    SocketChannel accept = channel.accept();//通道允許接入請求
                    accept.configureBlocking(false);//設置非阻塞
                    accept.register(selector,SelectionKey.OP_READ); //設置爲讀取數據
                }
                if(next.isReadable()){ //連接過,直接讀取
                    SelectableChannel channel2 = next.channel();
                    channel2.configureBlocking(false);
                    //讀取通道信息
                    readMsg(channel2);
                }
                iterator.remove();
            }
        }
    }

    private static void readMsg(SelectableChannel channel2) throws IOException {
        SocketChannel channel = (SocketChannel) channel2;
        SocketAddress localAddress = channel.getLocalAddress();
        //設置緩存區
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //讀取數據
        int len;
        byte[] b = new byte[1024];
        while ((len = channel.read(buffer))>0){
            buffer.flip();//刷新緩存區
            buffer.get(b, 0, len);
            System.out.println("服務端接受到數據爲:"+new String(b,0,len));
        }
    }
}

#客戶端

package com.glperry.demo.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;

/**
 * 客戶端 發送數據
 * @Date: 2019-08-29
 */
public class NioClient {

    public static void main(String[] args) throws IOException {
        //聲明連接地址對象 nio框架使用tcp協議綁定ip和端口
        InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8888);
        //打開一個和服務端連接的通道
        SocketChannel channel = SocketChannel.open();

        channel.connect(socketAddress);
        //設置通道爲非阻塞
        channel.configureBlocking(false);
        //設置緩存區大小
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //控制檯的輸入數據輸出
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()){
            //清除緩存區的數據
            buffer.clear();
            //獲取控制檯的數據
            String data = scanner.nextLine();
            buffer.put(data.getBytes());
            //刷新 緩存區的數據,與IO流的flush類似
            buffer.flip();
            channel.write(buffer);
        }
    }
}

#控制檯輸出的結果


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章