目錄
流是對數據傳輸的抽象,特性是進行數據的傳輸.
按照流向可以分爲輸入流和輸出流.
按數據的處理類型可以分爲字節流和字符流.
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);
}
}
}
#控制檯輸出的結果