1.Buffer類圖
新創建的ByteBuffer,四個屬性的位置
capacity:容量,指定緩衝區的大小。
ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE);
limit:第一個不應該讀取或寫入的元素的索引。緩衝區的限制不能爲負,並且不能大於其容量。
Position: 下一個要讀取或寫入的元素的索引。緩衝區的位置不能爲負,並且不能大於其限制。
遵循條件:0 <= 標記 <= 位置 <= 限制 <= 容量
不同的方法都是編輯buffer,移動這些標記。有四個方法需要重點關注
Buffer.clear();
Buffer.compact();
Buffer.flip();
Buffer.rewind();
clear();
-
使緩衝區爲一系列新的通道讀取或相對放置 操作做好準備:它將限制設置爲容量大小,將位置設置爲 0。 這意味着新數據會覆蓋舊數據。
compact(); The compact method moves the elements between the current position and the limit to the begging of the buffer.
flip(); The flip method need to be called before reading the data from the buffer.
-
使緩衝區爲一系列新的通道寫入或相對獲取 操作做好準備:它將限制設置爲當前位置,然後將位置設置爲 0。
rewind();
-
使緩衝區爲重新讀取已包含的數據做好準備:它使限制保持不變,將位置設置爲 0。
字節緩衝區要麼是直接的,要麼是非直接的。如果爲直接字節緩衝區,則 Java 虛擬機會盡最大努力直接在此緩衝區上執行本機 I/O 操作。也就是說,在每次調用基礎操作系統的一個本機 I/O 操作之前(或之後),虛擬機都會盡量避免將緩衝區的內容複製到中間緩衝區中(或從中間緩衝區中複製內容)。
直接字節緩衝區可以通過調用此類的 allocateDirect
工廠方法來創建。此方法返回的緩衝區進行分配和取消分配所需成本通常高於非直接緩衝區。
NonDirect: if you create a buffer that will not interact with native resource (ex. Just to store a String) you should use a NonDirect Buffer.
Adding to a Buffer: When adding data to a buffer you can use the wrap() method. Note that when a buffer is created by wrapping it are never direct.
/* * wraps a string inside an buffer.
*/
String string = "Text to be added";
CharBuffer charBuffer = CharBuffer.allocate(string.length());
charBuffer.wrap(string);
or you could wrap entire blocks of data in a form of an array:
/* * takes a byte array and wraps it into a buffer.
*/
byte[] data = “Text to be added”.getBytes(“UTF-8”);
ByteBuffer buffer1 = ByteBuffer.wrap(data);
Draining a Buffer: Buffers can be drained into any data type:
/* * uses the get() method to fill a string.
*/
String fromBuffer = “”;
while (buffer.hasRemaining()) {
fromBuffer += buffer.get();
}
Data Conversion: Data Conversion is an important aspect of buffers. You can use the factory methods to change a buffer from one type of another:
ByteBuffer byteBuffer = ByteBuffer.allocate(5);
IntBuffer intBuffer = byteBuffer.asIntBuffer();
flip方法:首先將限制設置爲當前位置,然後將位置設置爲 0。
2.Channel
通道只能在字節緩衝區上操作。
I/O可以分爲廣義的兩大類別:File I/O和Stream I/O。相應地有兩種類型的通道文件(file)通道和套接字(socket)通道。FileChannel類和三個socket通道類:SocketChannel、ServerSocketChannel和DatagramChannel。
通道可以以多種方式創建。Socket通道有可以直接創建新socket通道的工廠方法。但是一個FileChannel對象卻只能通過在一個打開的RandomAccessFile、FileInputStream或FileOutputStream對象上調用getChannel( )方法來獲取。您不能直接創建一個FileChannel對象。
- FileChannel對象是線程安全(thread-safe)的。多個進程可以在同一個實例上併發調用方法而不會引起任何問題,不過並非所有的操作都是多線程的(multithreaded)。影響通道位置或者影響文件大小的操作都是單線程的(single-threaded)。文件鎖定模型,鎖的對象是文件而不是通道或線程,這意味着文件鎖不適用於判優同一臺Java虛擬機上的多個線程發起的訪問。鎖與文件關聯,而不是與通道關聯。我們使用鎖來判優外部進程,而不是判優同一個Java虛擬機上的線程。
- Socket Channel。請注意DatagramChannel和SocketChannel實現定義讀和寫功能的接口而ServerSocketChannel不實現。ServerSocketChannel負責監聽傳入的連接和創建新的SocketChannel對象,它本身從不傳輸數據。
爲了加深理解,自己寫了一個小示例代碼實現本地文件拷貝,類似上傳:
package com.zhang.nio;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* 使用NIO上傳文件
* <p />
*
* @author Administrator
*/
public class UploadFile {
private final static int DEFAULT_CAPACITY = 8092;
private final static String SOURCE_FILE_PATH = "F:\\開發工具\\spring-framework-3.0.5.RELEASE-with-docs.zip";
private final static String DEST_PATH = "F:\\test\\test.zip";
/**
* @param args
*/
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocateDirect(DEFAULT_CAPACITY);
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
FileChannel inChannel = null;
FileChannel outChannel = null;
try {
inputStream = new FileInputStream(SOURCE_FILE_PATH);
outputStream = new FileOutputStream(DEST_PATH);
inChannel = inputStream.getChannel();
outChannel = outputStream.getChannel();
while (inChannel.read(buffer) != -1) {
buffer.flip();
outChannel.write(buffer);
buffer.clear();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
if (inChannel != null) {
inChannel.close();
}
if (outChannel != null) {
outChannel.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}