1. 程式人生 > >2018-09-09微服務筆記(五)之 NIO

2018-09-09微服務筆記(五)之 NIO

1.IO與NIO區別

NIO IO
面向流 面向緩衝
阻塞IO 非阻塞
選擇器

2.三個屬性

2.1 capacity:Buffer的記憶體固定的一個大小值,一般建立Buffer時初始化寫入

2.2 position在讀和寫情況的分析:
                 1)寫資料到Buffer中時,position表示寫入資料的當前位置。position的初始值為0.當一個byte、long等資料寫到Buffer後, position會向下移動到下一個可插入資料的Buffer單元

。position最大可為capacity – 1(因為position的初始值為0).
                   2)讀資料到Buffer中時,position表示讀入資料的當前位置,如position=2時表示已開始讀入了3個byte,或從第3個byte開始讀取。通過ByteBuffer.flip()切換到讀模式時position會被重置為0,當Buffer從position讀入資料後,position會下移到下一個可讀入的資料Buffer單元。

2.3 limit在讀和寫情況的分析:
                   1)寫資料時,limit表示可對Buffer最多寫入多少個數據。
                   2)讀資料時,limit表示Buffer裡有多少可讀資料(not null的資料),因此能讀到之前寫入的所有資料

2.4 demo

import java.nio.ByteBuffer;

public class Test01 {
	public static void main(String[] args) {
		//建立緩衝區,最大為1024,即capacity=1024
		ByteBuffer bb = ByteBuffer.allocate(1024);
		
		//輸出屬性
		System.out.println(bb.limit()); //實際可寫入大小
		System.out.println(bb.capacity()); //最大大小
		System.out.println(bb.position()); //當前處理位置
		
		//放入資料
		System.out.println("------------放入資料");
		bb.put("abcd1234".getBytes());
		//輸出屬性
		System.out.println(bb.limit()); //當寫入的時候表示最多可以寫入的資料量,此時為1024
		System.out.println(bb.capacity()); //最大大小
		System.out.println(bb.position()); //當前處理位置
		
		//輸出
		System.out.println("-------------輸出資料");
		bb.flip();//將position位置重置為0,下面會做比較
		byte[] b = new byte[bb.limit()]; //通過flip重置position位置後,此時的limit表示緩衝區可讀資料的長度,此時limit=8
		bb.get(b);
		System.out.println(new String(b,0,bb.limit()));
		
		//重複讀取緩衝區資料
		System.out.println("------------重複讀資料");
		bb.rewind();//重置下面會做比較
		byte[] b1 = new byte[bb.limit()];
		bb.get(b1);
		System.out.println(new String(b1,0,bb.limit()));
		
		//清空緩衝區
		System.out.println("--------清空緩衝區");
		bb.clear();
		System.out.println(bb.limit());
		System.out.println(bb.capacity());
		System.out.println(bb.position());
		System.out.println((char)bb.get());//仍可以讀出資料,應為clear方法只是單純的將三個屬性重置,資料並沒用清除
		
		
	}
}

2.5 clear、flip、rewind 區別

     2.5.1 clear原始碼

public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }

     clear方法將position重置為0,將可讀寫範圍limit設定為最大範圍capacity;

2.5.2  flip

public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

將可讀寫範圍設定為position的值,這樣在讀操作時,limit的值就是緩衝區中可以讀寫資料的大小。

將position的值重置。

2.5.3 rewind

public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }

rewind方法只是簡單的將position的值重置,這樣可以從緩衝區開始位置讀取資料。

 

2.6 mark()和reset()方法

2.6.1 mark():標記當前position的位置,reset()將position返回到標記的位置。

import java.nio.ByteBuffer;

public class Test02 {
	public static void main(String[] args) {
		ByteBuffer bb = ByteBuffer.allocate(10);
		bb.put("abcd".getBytes());
		
		bb.flip();
		byte[] b = new byte[2];
		bb.get(b,0,2);//取出兩個位元組
		System.out.println("position:"+bb.position());
		bb.mark();//標記當前position的位置
		System.out.println(new String(b,0,2));
		bb.get(b,0,2);//再取出兩個位元組
		System.out.println("再取出兩個位元組後的position:"+bb.position());
		bb.reset();//返回標記的position位置
		System.out.println("返回後的position:"+bb.position());
		
	}
}

 

2.7 直接緩衝區和非直接緩衝區複製檔案

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

import org.junit.Test;

public class Test03 {
	/**
	 * 通過管道複製檔案,--非直接緩衝區
	 * 思路是:
	 * 1.建立原始檔的輸入流和目標檔案的輸出流
	 * 2.分別衝輸入輸出流中建立各自的管道
	 * 3.建立緩衝區,通過緩衝區,從輸入管道中讀取原始檔內容到緩衝區中,再將緩衝區中的內容寫道輸出管道中
	 * 4.關閉相應的資源
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		//建立輸入流,讀取圖片
		FileInputStream is = new FileInputStream("1.jpg");
		//建立輸出流,輸出圖片
		FileOutputStream os = new FileOutputStream("2.jpg");
		
		//建立通道
		FileChannel inChannel = is.getChannel();
		FileChannel outChannel = os.getChannel();
		
		//通過緩衝區將資料從輸入通道,讀到輸出通道
		ByteBuffer bb = ByteBuffer.allocate(1024);
		while(inChannel.read(bb) != -1){
			bb.flip();//上面寫入緩衝區後,需要重置Position位置,使下面可以從頭開始讀緩衝區
			outChannel.write(bb);
			bb.clear();//上面讀後,需要重置,使下一次可以從頭寫入緩衝區
		}
		
		//關閉
		outChannel.close();
		inChannel.close();
		os.close();
		is.close();
	}
	
	/**
	 * 通過管道複製檔案  ----直接緩衝區
	 * 思路:
	 * 1.通過工廠方法建立原始檔和目標檔案的管道
	 * 2.建立各自的對映緩衝區
	 * 3.從讀緩衝區中讀取資料到寫入緩衝區中
	 * @throws IOException 
	 */
	@Test
	public void test() throws IOException{
		//直接建立檔案管道
		FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
		FileChannel outChannel = FileChannel.open(Paths.get("3.jpg"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
		
		//建立檔案對映
		MappedByteBuffer inmap = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
		MappedByteBuffer outmap = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
		
		//操作緩衝區
		byte[] b = new byte[inmap.limit()];
		inmap.get(b);
		outmap.put(b);
		
		//關閉資源
		outChannel.close();
		inChannel.close();
	}
}

 

2.8 多緩衝區讀取檔案

import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class Test04 {
	/**
	 * 多緩衝區讀取檔案
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		//建立檔案輸入流
		//RandomAccessFile file = new RandomAccessFile("test01.txt", "rw");
		FileInputStream file = new FileInputStream(new File("test01.txt"));
		
		//建立管道
		FileChannel channel = file.getChannel();
		
		//建立緩衝區
		ByteBuffer buf1 = ByteBuffer.allocate(100);
		ByteBuffer buf2 = ByteBuffer.allocate(1024);
		//緩衝區陣列
		ByteBuffer[] bs = new ByteBuffer[]{buf1,buf2};
		
		//從管道讀取資料到緩衝區中
		channel.read(bs);
		
		//讀出緩衝區內容
		System.out.println(new String(buf1.array(),0,buf1.limit()));
		System.out.println("------------------");
		System.out.println(new String(buf2.array(),0,buf2.limit()));
	}
}