很好的学习资料
http://tutorials.jenkov.com/java-nio/index.html
本文主要是对java.nio的全貌做个简单介绍 然后详细介绍Buffer方法 并测试Buffer的相关方法
Java nio (new io) 对于java io和 java networking而言,是一个可选的io api
核心组件有三个Buffers Channels Selectors
Buffers
缓存主要有
ByteBuffer CharBuffer ShortBuffer IntBuffer FloatBuffer LongBuffer DoubleBuffer
覆盖基本的数据类型 byte character short int float long double
Channels
通道主要有
FileChannel DatagramChannel ServerChannel ServerSocketChannel
包含file io、udp、tcp三类
Selectors
多路复用器或者称为选择器
可以使一个线程使用多路复用器处理多个通道
Buffer与Channel的关系如下
对于Scatter与Gather模式的描述
A "scattering read" reads data from a single channel into multiple buffers
A "gathering write" writes data from multiple buffers into a single channel.
两者的关系图
Channel与Selector的关系如下
下面是关于Buffer的各种方法的描述
Buffer中搞清楚 position limit capacity 的意思 在读写模式中的位置 理解Buffer的方法 就很简单了
Basic Buffer Usage
源文档
1. Write data into the Buffer
2. Call buffer.flip()
3. Read data out of the Buffer
4. Call buffer.clear() or buffer.compact()
源文档
Initially the position is 0,Position can maximally become capacity - 1
1. Write data from a Channel into a Buffer
2. Write data into the Buffer yourself, via the buffer's put() methods.
源文档
The flip() method switches a Buffer from writing mode to reading mode. Calling flip() sets the position back to 0, and sets the limit to where position just was.
3. Read data from the buffer into a channel.
4. Read data from the buffer yourself, using one of the get() methods.
The Buffer.rewind() sets the position back to 0, so you can reread all the data in the buffer. The limit remains untouched, thus still marking how many elements (bytes, chars etc.) that can be read from the Buffer.
clear() and compact()
If you call clear() the position is set back to 0 and the limit to capacity. In other words, the Buffer is cleared. The data in the Buffer is not cleared. Only the markers telling where you can write data into the Buffer are.
compact() copies all unread data to the beginning of the Buffer. Then it sets position to right after the last unread element. The limit property is still set to capacity, just like clear() does. Now the Buffer is ready for writing, but you will not overwrite the unread data.
mark() and reset()
You can mark a given position in a Buffer by calling the Buffer.mark() method. You can then later reset the position back to the marked position by calling the Buffer.reset() method
equals() and compareTo()
equals()
Two buffers are equal if:
5. They are of the same type (byte, char, int etc.)
6. They have the same amount of remaining bytes, chars etc. in the buffer.
7. All remaining bytes, chars etc. are equal.
compareTo()
The compareTo() method compares the remaining elements (bytes, chars etc.) of the two buffers, for use in e.g. sorting routines. A buffer is considered "smaller" than another buffer if:
8. The first element which is equal to the corresponding element in the other buffer, is smaller than that in the other buffer.
9. All elements are equal, but the first buffer runs out of elements before the second buffer does (it has fewer elements).
源文档
上面都是每个方法比较重要的描述
Buffer的读模式与写模式的 position limit capacity 三者间的关系图
最后一一些单元测试 都加了代码注释
package com.undergrowth;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class BasicChannel {
RandomAccessFile randomAccessFile,randomAccessFile2;
FileChannel fileChannel,fileChannel2;
ByteBuffer buffer, buffer2;
/**
* 测试之前打开文件 分配buffer
*
* @throws FileNotFoundException
*/
@Before
public void before() throws FileNotFoundException {
randomAccessFile = new RandomAccessFile(ClassLoader.getSystemResource(
"channel.xml").getFile(), "rw");
randomAccessFile2 = new RandomAccessFile(ClassLoader.getSystemResource(
"channel2.xml").getFile(), "rw");
fileChannel = randomAccessFile.getChannel();
fileChannel2 = randomAccessFile2.getChannel();
buffer = ByteBuffer.allocate(1024);
buffer2 = ByteBuffer.allocate(512);
}
/**
* 测试完成后 关闭文件
*
* @throws IOException
*/
@After
public void after() throws IOException {
fileChannel.close();
fileChannel2.close();
randomAccessFile.close();
randomAccessFile2.close();
}
/**
* 测试读写
*/
@Test
public void testRead() {
// TODO Auto-generated method stub
try {
System.out.println(readBuffer(fileChannel, buffer));
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
/**
* 测试标记与重置 You can mark a given position in a Buffer by calling the
* Buffer.mark() method. You can then later reset the position back to the
* marked position by calling the Buffer.reset() method
*
* @throws IOException
*/
@Test
public void testMarkReset() throws IOException {
int byteRead = fileChannel.read(buffer);
if (byteRead != -1) {
// 反转buffer模式
buffer.flip();
while (buffer.hasRemaining()) {
char tmp = (char) buffer.get();
// 当出现9字符的时候 进行标注
if (tmp == '9')
buffer.mark();
System.out.print(tmp);
}
// 将buffer的position置为9字符的位置
buffer.reset();
System.out.println();
System.out.println("置为9字符的起始位置开始读取数据");
while (buffer.hasRemaining())
System.out.print((char) buffer.get());
}
}
/**
* 测试equals和compareTo 这两个方法都是比较剩余元素 equals() Two buffers are equal if: 5.
* They are of the same type (byte, char, int etc.) 6. They have the same
* amount of remaining bytes, chars etc. in the buffer. 7. All remaining
* bytes, chars etc. are equal.
*
* compareTo() The compareTo() method compares the remaining elements
* (bytes, chars etc.) of the two buffers, for use in e.g. sorting routines.
* A buffer is considered "smaller" than another buffer if: 8. The first
* element which is equal to the corresponding element in the other buffer,
* is smaller than that in the other buffer. 9. All elements are equal, but
* the first buffer runs out of elements before the second buffer does (it
* has fewer elements).
*
* @throws IOException
*/
@Test
public void testEqualsCompare() throws IOException {
int byteRead = fileChannel.read(buffer);
buffer2.put((byte) 'q');
int byteRead2 = fileChannel.read(buffer2);
if (byteRead != -1 && byteRead2 != -1) {
buffer.flip();
buffer2.flip();
System.out.println("剩下元素");
System.out.println(buffer.remaining());
System.out.println(buffer2.remaining());
// 比较两个buffer是否相等
System.out.println();
System.out.println(buffer.equals(buffer2));
System.out.println(buffer.compareTo(buffer2));
}
}
/**
* 测试重置方法 The Buffer.rewind() sets the position back to 0, so you can reread
* all the data in the buffer. The limit remains untouched, thus still
* marking how many elements (bytes, chars etc.) that can be read from the
* Buffer.
*
* @throws IOException
*/
@Test
public void testRewind() throws IOException {
int byteRead = fileChannel.read(buffer);
if (byteRead != -1) {
// 反转buffer模式
buffer.flip();
while (buffer.hasRemaining())
System.out.print((char) buffer.get());
// 重新读取buffer
System.out.println();
System.out.println("重新读取buffer");
// 将buffer的positon置为0
buffer.rewind();
while (buffer.hasRemaining())
System.out.print((char) buffer.get());
}
}
/**
* 将文件通道的内容读入到缓存中 从缓存中读出数据 返回 If you call clear() the position is set back
* to 0 and the limit to capacity. In other words, the Buffer is cleared.
* The data in the Buffer is not cleared. Only the markers telling where you
* can write data into the Buffer are.
*
*
* compact() copies all unread data to the beginning of the Buffer. Then it
* sets position to right after the last unread element. The limit property
* is still set to capacity, just like clear() does. Now the Buffer is ready
* for writing, but you will not overwrite the unread data.
*
* @param fileChannel
* @param buffer
* @return
* @throws IOException
*/
public String readBuffer(FileChannel fileChannel, ByteBuffer buffer)
throws IOException {
StringBuilder builder = new StringBuilder();
// 1、读入数据到缓存中
int byteRead = fileChannel.read(buffer);
while (byteRead != -1) {
// 2、转换缓存的模式
buffer.flip();
// 3、从缓存中读出数据
while (buffer.hasRemaining())
builder.append((char) buffer.get());
// 4、清除缓冲区
buffer.clear();
byteRead = fileChannel.read(buffer);
}
// System.out.println(builder.toString());
return builder.toString();
}
@Test
public void testWriteBuffer() throws IOException{
buffer.put((byte)'1');
buffer.put((byte)'2');
buffer.put((byte)'3');
buffer.put((byte)'4');
System.out.println(fileChannel.write(buffer));
}
/**
* Scatter和Gather模式
* Scatter Read支持从一个通道读取到多个缓存区
* Gather Write支持从多个缓存区将数据写入到一个通道中
* @throws IOException
*/
@Test
public void testScatterGather() throws IOException{
//构建buffer数组
ByteBuffer[] buffers={buffer,buffer2};
//读数据到多个缓存中
System.out.println(fileChannel.read(buffers));;
//转换缓存模式
buffer.flip();
buffer2.flip();
//读取缓存数据
System.out.println("缓存1");
while(buffer.hasRemaining()) System.out.print((char)buffer.get());
buffer.rewind();
System.out.println();
System.out.println("缓存1和2");
while(buffer.hasRemaining()) System.out.print((char)buffer.get());
while(buffer2.hasRemaining()) System.out.print((char)buffer2.get());
buffer.rewind();
buffer2.rewind();
System.out.println();
System.out.println("字符集输出");
System.out.println(Charset.defaultCharset().decode(buffer));
buffer.rewind();
buffer2.rewind();
//写缓存数据
fileChannel2.write(buffers);
}
/**
* 从一个通道转换到另一个通道
* @throws IOException
*/
@Test
public void testChannerTransfer() throws IOException{
System.out.println(fileChannel2.transferFrom(fileChannel, 0, fileChannel.size()));;
}
}