java 基础--NIO(4)
?
3.? ? Java NIO系統的核心在于:通道(Channel)和緩沖區 (Buffer)。通道表示打開到 IO 設備(例如:文件、 套接字)的連接。若需要使用 NIO 系統,
? ? ? ? ? ? ? ? ?需要獲取 用于連接 IO 設備的通道以及用于容納數據的緩沖 區。然后操作緩沖區,對數據進行處理。
?
4.? ? ?緩沖區(Buffer):一個用于特定基本數據類 型的容器。由 java.nio 包定義的,所有緩沖區 都是 Buffer 抽象類的子類。
? ? ? ? Java NIO 中的 Buffer 主要用于與 NIO 通道進行 交互,數據是從通道讀入緩沖區,從緩沖區寫 入通道中的。
?
5 .? Buffer 就像一個數組,可以保存多個相同類型的數據。根 據數據類型不同(boolean 除外) ,
? ? ? ? ? ? ?有以下 Buffer 常用子類:
? ? ? ? ? ? ? ? ? ByteBuffer
? ? ? ? ? ? ? ? ? CharBuffer
? ? ? ? ? ? ? ? ? ShortBuffer
? ? ? ? ? ? ? ? ? IntBuffer
? ? ? ? ? ? ? ? ? LongBuffer
? ? ? ? ? ? ? ? ? FloatBuffer
? ? ? ? ? ? ? ? ?? DoubleBuffer
? ? ? ? ? ? 上述 Buffer 類 他們都采用相似的方法進行管理數據,只是各自管理的數據類型不同而已。都是通過如下方法獲取一個 Buffer 對象:
? ? ? ? ? ? ? ? ?static XxxBuffer allocate(int capacity) : 創建一個容量為 capacity 的 XxxBuffer 對象
6.? 緩沖區的基本屬性
? ? ? ? ? ? ? ? ? ? Buffer 中的重要概念:
? ? ? ? ? ? ? ? ? ? ? ? 容量 (capacity) :表示 Buffer 最大數據容量,緩沖區容量不能為負,并且創 建后不能更改。
? ? ? ? ? ? ? ? ? ? ? ? 限制 (limit):第一個不應該讀取或寫入的數據的索引,即位于 limit 后的數據 不可讀寫。緩沖區的限制不能為負,并且不能大于其容量。
? ? ? ? ? ? ? ? ? ? ? ? 位置 (position):下一個要讀取或寫入的數據的索引。緩沖區的位置不能為 負,并且不能大于其限制
? ? ? ? ? ? ? ? ? ? ?? 標記 (mark)與重置 (reset):標記是一個索引,通過 Buffer 中的 mark() 方法 指定 Buffer 中一個特定的 position,之后可以通過調用 reset() 方法恢復到這 個 position.
? ? ? ? ? ? ? ? ? ? ?? 標記、位置、限制、容量遵守以下不變式: 0 <= mark <= position <= limit <= capacity
?
7.? 緩沖區的數據操作
? ? ? ? ?? Buffer 所有子類提供了兩個用于數據操作的方法:get() 與 put() 方法
? ? ? ? ? ? 獲取 Buffer 中的數據 get() :讀取單個字節 get(byte[] dst):批量讀取多個字節到 dst 中 get(int index):讀取指定索引位置的字節(不會移動 position)
? ? ? ? ? ?? 放入數據到 Buffer 中 put(byte b):將給定單個字節寫入緩沖區的當前位置 put(byte[] src):將 src 中的字節寫入緩沖區的當前位置 put(int index, byte b):
? ? ? ? ? 將指定字節寫入緩沖區的索引位置(不會移動 position)
8.? ? ? ? ? 直接與非直接緩沖區
? ? ? ? ? ? ? ? ?? 字節緩沖區要么是直接的,要么是非直接的。如果為直接字節緩沖區,則 Java 虛擬機會盡最大努力直接在 此緩沖區上執行本機 I/O 操作。也就是說,在每次調用基礎操作系統的一個本機 I/O? ? ? ?操作之前? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (或? 之后), 虛擬機都會盡量避免將緩沖區的內容復制到中間緩沖區中(或從中間緩沖區中復制內容)。
? ? ? ? ? ? ? ? ? 直接字節緩沖區可以通過調用此類的 allocateDirect() 工廠方法來創建。此方法返回的緩沖區進行分配和取消 分配所需成本通常高于非直接緩沖區。直接緩沖區的內容可以駐留在常規的垃圾回收堆之外,因? ? ? ? ? ? ? ? ? ? ? ? 此,它們對 應用程序的內存需求量造成的影響可能并不明顯。所以,建議將直接緩沖區主要分配給那些易受基礎系統的 本機 I/O 操作影響的大型、持久的緩沖區。一般情況下,最好僅在直接緩沖區能在程? ? ? ? ? ? ? ? ? ? ? ? 序性能方面帶來明顯好 處時分配它們。?
? ? ? ? ? ? ? ? ? 直接字節緩沖區還可以通過 FileChannel 的 map() 方法 將文件區域直接映射到內存中來創建。該方法返回 MappedByteBuffer 。Java 平臺的實現有助于通過 JNI 從本機代碼創建直接字節緩沖區。如果以上? ? ? ? ? ? ? ? ? ? ? ? ? ?這些緩沖區 中的某個緩沖區實例指的是不可訪問的內存區域,則試圖訪問該區域不會更改該緩沖區的內容,并且將會在 訪問期間或稍后的某個時間導致拋出不確定的異常。
? ? ? ? ? ? ? ? ? 字節緩沖區是直接緩沖區還是非直接緩沖區可通過調用其 isDirect() 方法來確定。提供此方法是為了能夠在 性能關鍵型代碼中執行顯式緩沖區管理。
9? ? 通道(Channel):由 java.nio.channels 包定義 的。Channel 表示 IO 源與目標打開的連接。 Channel 類似于傳統的“流”。只不過 Channel 本身不能直接訪問數據,Channel 只能與 Buffer 進行交互。
?
10.?Java 為 Channel 接口提供的最主要實現類如下:
? ? ? ? ? ? ? ? ?FileChannel:用于讀取、寫入、映射和操作文件的通道。
? ? ? ? ? ? ? ??DatagramChannel:通過 UDP 讀寫網絡中的數據通道。
? ? ? ? ? ? ? ?SocketChannel:通過 TCP 讀寫網絡中的數據。
? ? ? ? ? ? ? ??ServerSocketChannel:可以監聽新進來的 TCP 連接,對每一個新進來 的連接都會創建一個 SocketChannel。
?
package com.atguigu.nio;import java.nio.ByteBuffer;import org.junit.Test;/** 一、緩沖區(Buffer):在 Java NIO 中負責數據的存取。緩沖區就是數組。用于存儲不同數據類型的數據* * 根據數據類型不同(boolean 除外),提供了相應類型的緩沖區:* ByteBuffer* CharBuffer* ShortBuffer* IntBuffer* LongBuffer* FloatBuffer* DoubleBuffer* * 上述緩沖區的管理方式幾乎一致,通過 allocate() 獲取緩沖區* * 二、緩沖區存取數據的兩個核心方法:* put() : 存入數據到緩沖區中* get() : 獲取緩沖區中的數據* * 三、緩沖區中的四個核心屬性:* capacity : 容量,表示緩沖區中最大存儲數據的容量。一旦聲明不能改變。* limit : 界限,表示緩沖區中可以操作數據的大小。(limit 后數據不能進行讀寫)* position : 位置,表示緩沖區中正在操作數據的位置。* * mark : 標記,表示記錄當前 position 的位置。可以通過 reset() 恢復到 mark 的位置* * 0 <= mark <= position <= limit <= capacity* * 四、直接緩沖區與非直接緩沖區:* 非直接緩沖區:通過 allocate() 方法分配緩沖區,將緩沖區建立在 JVM 的內存中* 直接緩沖區:通過 allocateDirect() 方法分配直接緩沖區,將緩沖區建立在物理內存中。可以提高效率*/ public class TestBuffer {@Testpublic void test3(){//分配直接緩沖區ByteBuffer buf = ByteBuffer.allocateDirect(1024);System.out.println(buf.isDirect());}@Testpublic void test2(){String str = "abcde";ByteBuffer buf = ByteBuffer.allocate(1024);buf.put(str.getBytes());buf.flip();byte[] dst = new byte[buf.limit()];buf.get(dst, 0, 2);System.out.println(new String(dst, 0, 2));System.out.println(buf.position());//mark() : 標記 buf.mark();buf.get(dst, 2, 2);System.out.println(new String(dst, 2, 2));System.out.println(buf.position());//reset() : 恢復到 mark 的位置 buf.reset();System.out.println(buf.position());//判斷緩沖區中是否還有剩余數據if(buf.hasRemaining()){//獲取緩沖區中可以操作的數量 System.out.println(buf.remaining());}} /* ab 2 cd 4 2 3*/@Testpublic void test1(){String str = "abcde";//1. 分配一個指定大小的緩沖區ByteBuffer buf = ByteBuffer.allocate(1024);System.out.println("-----------------allocate()----------------");System.out.println(buf.position());System.out.println(buf.limit());System.out.println(buf.capacity());//2. 利用 put() 存入數據到緩沖區中 buf.put(str.getBytes());System.out.println("-----------------put()----------------");System.out.println(buf.position());System.out.println(buf.limit());System.out.println(buf.capacity());//3. 切換讀取數據模式 buf.flip();System.out.println("-----------------flip()----------------");System.out.println(buf.position());System.out.println(buf.limit());System.out.println(buf.capacity());//4. 利用 get() 讀取緩沖區中的數據byte[] dst = new byte[buf.limit()];buf.get(dst);System.out.println(new String(dst, 0, dst.length));System.out.println("-----------------get()----------------");System.out.println(buf.position());System.out.println(buf.limit());System.out.println(buf.capacity());//5. rewind() : 可重復讀 buf.rewind();System.out.println("-----------------rewind()----------------");System.out.println(buf.position());System.out.println(buf.limit());System.out.println(buf.capacity());//6. clear() : 清空緩沖區. 但是緩沖區中的數據依然存在,但是處于“被遺忘”狀態 buf.clear();System.out.println("-----------------clear()----------------");System.out.println(buf.position());System.out.println(buf.limit());System.out.println(buf.capacity());System.out.println((char)buf.get());} /*-----------------allocate()---------------- 0 1024 1024 -----------------put()---------------- 5 1024 1024 -----------------flip()---------------- 0 5 1024 abcde -----------------get()---------------- 5 5 1024 -----------------rewind()---------------- 0 5 1024 -----------------clear()---------------- 0 1024 1024 a*/ } TestBuffer package com.atguigu.nio;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Map; import java.util.Map.Entry; import java.util.Set;import org.junit.Test;/** 一、通道(Channel):用于源節點與目標節點的連接。在 Java NIO 中負責緩沖區中數據的傳輸。Channel 本身不存儲數據,因此需要配合緩沖區進行傳輸。* * 二、通道的主要實現類* java.nio.channels.Channel 接口:* |--FileChannel* |--SocketChannel* |--ServerSocketChannel* |--DatagramChannel* * 三、獲取通道* 1. Java 針對支持通道的類提供了 getChannel() 方法* 本地 IO:* FileInputStream/FileOutputStream* RandomAccessFile* * 網絡IO:* Socket* ServerSocket* DatagramSocket* * 2. 在 JDK 1.7 中的 NIO.2 針對各個通道提供了靜態方法 open()* 3. 在 JDK 1.7 中的 NIO.2 的 Files 工具類的 newByteChannel()* * 四、通道之間的數據傳輸* transferFrom()* transferTo()* * 五、分散(Scatter)與聚集(Gather)* 分散讀取(Scattering Reads):將通道中的數據分散到多個緩沖區中* 聚集寫入(Gathering Writes):將多個緩沖區中的數據聚集到通道中* * 六、字符集:Charset* 編碼:字符串 -> 字節數組* 解碼:字節數組 -> 字符串* */ public class TestChannel {//字符集 @Testpublic void test6() throws IOException{Charset cs1 = Charset.forName("GBK");//獲取編碼器CharsetEncoder ce = cs1.newEncoder();//獲取解碼器CharsetDecoder cd = cs1.newDecoder();CharBuffer cBuf = CharBuffer.allocate(1024);cBuf.put("尚硅谷威武!");cBuf.flip();//編碼ByteBuffer bBuf = ce.encode(cBuf);for (int i = 0; i < 12; i++) {System.out.println(bBuf.get());}//解碼 bBuf.flip();CharBuffer cBuf2 = cd.decode(bBuf);System.out.println(cBuf2.toString());System.out.println("------------------------------------------------------");Charset cs2 = Charset.forName("GBK");bBuf.flip();CharBuffer cBuf3 = cs2.decode(bBuf);System.out.println(cBuf3.toString());}@Testpublic void test5(){Map<String, Charset> map = Charset.availableCharsets();Set<Entry<String, Charset>> set = map.entrySet();for (Entry<String, Charset> entry : set) {System.out.println(entry.getKey() + "=" + entry.getValue());}}//分散和聚集 @Testpublic void test4() throws IOException{RandomAccessFile raf1 = new RandomAccessFile("1.txt", "rw");//1. 獲取通道FileChannel channel1 = raf1.getChannel();//2. 分配指定大小的緩沖區ByteBuffer buf1 = ByteBuffer.allocate(100);ByteBuffer buf2 = ByteBuffer.allocate(1024);//3. 分散讀取ByteBuffer[] bufs = {buf1, buf2};channel1.read(bufs);for (ByteBuffer byteBuffer : bufs) {byteBuffer.flip();}System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));System.out.println("-----------------");System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));//4. 聚集寫入RandomAccessFile raf2 = new RandomAccessFile("2.txt", "rw");FileChannel channel2 = raf2.getChannel();channel2.write(bufs);}//通道之間的數據傳輸(直接緩沖區) @Testpublic void test3() throws IOException{FileChannel inChannel = FileChannel.open(Paths.get("d:/1.mkv"), StandardOpenOption.READ);FileChannel outChannel = FileChannel.open(Paths.get("d:/2.mkv"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);// inChannel.transferTo(0, inChannel.size(), outChannel);outChannel.transferFrom(inChannel, 0, inChannel.size());inChannel.close();outChannel.close();}//使用直接緩沖區完成文件的復制(內存映射文件) @Testpublic void test2() throws IOException{//2127-1902-1777long start = System.currentTimeMillis();FileChannel inChannel = FileChannel.open(Paths.get("d:/1.mkv"), StandardOpenOption.READ);FileChannel outChannel = FileChannel.open(Paths.get("d:/2.mkv"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);//內存映射文件MappedByteBuffer inMappedBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());MappedByteBuffer outMappedBuf = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());//直接對緩沖區進行數據的讀寫操作byte[] dst = new byte[inMappedBuf.limit()];inMappedBuf.get(dst);outMappedBuf.put(dst);inChannel.close();outChannel.close();long end = System.currentTimeMillis();System.out.println("耗費時間為:" + (end - start));}//利用通道完成文件的復制(非直接緩沖區) @Testpublic void test1(){//10874-10953long start = System.currentTimeMillis();FileInputStream fis = null;FileOutputStream fos = null;//①獲取通道FileChannel inChannel = null;FileChannel outChannel = null;try {fis = new FileInputStream("d:/1.mkv");fos = new FileOutputStream("d:/2.mkv");inChannel = fis.getChannel();outChannel = fos.getChannel();//②分配指定大小的緩沖區ByteBuffer buf = ByteBuffer.allocate(1024);//③將通道中的數據存入緩沖區中while(inChannel.read(buf) != -1){buf.flip(); //切換讀取數據的模式//④將緩沖區中的數據寫入通道中 outChannel.write(buf);buf.clear(); //清空緩沖區 }} catch (IOException e) {e.printStackTrace();} finally {if(outChannel != null){try {outChannel.close();} catch (IOException e) {e.printStackTrace();}}if(inChannel != null){try {inChannel.close();} catch (IOException e) {e.printStackTrace();}}if(fos != null){try {fos.close();} catch (IOException e) {e.printStackTrace();}}if(fis != null){try {fis.close();} catch (IOException e) {e.printStackTrace();}}}long end = System.currentTimeMillis();System.out.println("耗費時間為:" + (end - start));}} TestChannel?
?
?
轉載于:https://www.cnblogs.com/ou-pc/p/9480787.html
總結
以上是生活随笔為你收集整理的java 基础--NIO(4)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: post提交的数据几种编码格式
- 下一篇: C# 连接数据库等