引言
在nio中,流的讀取和寫入都是依賴buffer的。jdk在nio包中提供了ByteBuffer、CharBuffer、ShortBuffer、LongBuffer、DoubleBuffer、FloatBuffer等。 6中類型的buffer還分爲兩種實現,緩存在jvm堆中和緩存在直接內存中。
Buffer
主要屬性
// Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
// Used only by direct buffers
// NOTE: hoisted here for speed in JNI GetDirectBufferAddress
long address;
主要方法
這些方法是用來控制buffer的讀寫。
jdk提供的buffer只有一個position指針,讀和寫都是從position的位置開始操作。
代碼的流程常常是這樣的:
buffer.put(1);
buffer.flip();
buffer.get();
用於多次讀取操作
public final Buffer mark() {
mark = position;
return this;
}
public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
假裝清空
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
翻轉指針
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
剩餘容量
public final int remaining() {
return limit - position;
}
獲取讀寫指針的方法
final int nextGetIndex() { // package-private
if (position >= limit)
throw new BufferUnderflowException();
return position++;
}
final int nextPutIndex() { // package-private
if (position >= limit)
throw new BufferOverflowException();
return position++;
}
ByteBuffer
主要屬性
// byte數組
final byte[] hb; // Non-null only for heap buffers
// offset 在派生的時候有用
final int offset;
實例化方法
創建指定容量的heapBuffer和directBuffer
public static ByteBuffer allocate(int capacity) {
if (capacity < 0)
throw new IllegalArgumentException();
return new HeapByteBuffer(capacity, capacity);
}
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
通過數組創建ByteBuffer
public static ByteBuffer wrap(byte[] array,
int offset, int length)
{
try {
return new HeapByteBuffer(array, offset, length);
} catch (IllegalArgumentException x) {
throw new IndexOutOfBoundsException();
}
}
put和get方法
public ByteBuffer put(byte x) {
// 獲取put的指針
hb[ix(nextPutIndex())] = x;
return this;
}
public byte get() {
// 獲取get的指針
return hb[ix(nextGetIndex())];
}
protected int ix(int i) {
return i + offset;
}
派生ByteBuffer
slice創建的Buffer,讀寫都是在數組的子序列上進行。依賴於Buffer的當前索引
// 共享數組,position=0 mark=-1 limit=cap,
public ByteBuffer slice() {
return new HeapByteBuffer(hb,
-1,
0,
this.remaining(),
this.remaining(),
this.position() + offset);
}
複製一個對象,共享數組,擁有相同數值的mark、position、limit、capacity和offset
public ByteBuffer duplicate() {
return new HeapByteBuffer(hb,
this.markValue(),
this.position(),
this.limit(),
this.capacity(),
offset);
}
把已經在本buffer寫的元素移動到0位置,position定位到剩餘容量起點,limit限制爲capacity
public ByteBuffer compact() {
System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
position(remaining());
limit(capacity());
discardMark();
return this;
}
常用代碼段
// 拷貝文件
public void copyFile(String copyFrom,String copyTo){
File file = new File(copyFrom);
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
FileChannel channel = randomAccessFile.getChannel();
File outFile = new File(copyTo);
RandomAccessFile outRAF = new RandomAccessFile(outFile, "rw");
FileChannel outChannel = outRAF.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
while (channel.read(byteBuffer) > 0){
while (byteBuffer.hasRemaining()){
byteBuffer.flip();
outChannel.write(byteBuffer);
}
}
}
// 從網絡中讀取文件
public void readNetworkFile(){
File outFile = new File("d://20200418220925641.png");
RandomAccessFile outRAF = new RandomAccessFile(outFile, "rw");
FileChannel outChannel = outRAF.getChannel();
// get stream from net
InputStream inputStream = new URL("https://img-blog.csdnimg.cn/20200418220925641.png").openConnection()
.getInputStream();
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
byte[] cache = new byte[2 * 1024];
ByteBuffer wrap = ByteBuffer.wrap(cache);
int len;
while ((len = bufferedInputStream.read(cache)) > 0){
wrap.position(0);
wrap.limit(len);
outChannel.write(wrap);
}
}
public void writeMsgToClient(){
ServerSocket serverSocket = new ServerSocket(9988);
// bad case
Socket accept = serverSocket.accept();
SocketChannel channel = accept.getChannel();
// do business logic and get a byte array
byte[] rlt = "businessLogicRlt".getBytes();
ByteBuffer wrap = ByteBuffer.wrap(rlt, 0, rlt.length);
channel.write(wrap);
}