一.Buffer(缓冲区)
- 概念
缓冲区是包在一个对象内的基本数据元素数组。Buffer 类相比一个简单数组的优点是它将关于数据的数据内容和信息包含在一个单一的对象中。Buffer 类以及它专有的子类定义了一个用于处理数据缓冲区的 API。
所有的缓冲区都具有四个属性来提供关于其所包含的数据元素的信息。它们是:
- 容量(Capacity)
缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,并且永远不能被改变。
- 上界(Limit)
缓冲区的第一个不能被读或写的元素。或者说,缓冲区中现存元素的计数。
- 位置(Position)
下一个要被读或写的元素的索引。位置会自动由相应的 get( )和 put( )方法更新。
- 标记(Mark)
一个备忘位置。调用 mark( )来设定 mark = postion。调用 reset( )设定 position = mark。标记在设定前是未定义的(undefined)。
这四个属性之间总是遵循以下关系:
0 <= mark <= position <= limit <= capacity
下图展示了一个新创建的容量为 10的 ByteBuffer 逻辑视图。
buffer的API中很多方法是级联调用的:
buffer.mark( );
buffer.position(5);
buffer.reset( );
被简写为:
buffer.mark().position(5).reset( );
- 填充
让我们看一个例子。我们将代表“Hello”字符串的 ASCII 码载入一个名为 buffer 的ByteBuffer 对象中。
新建的缓冲区上执行以下代码后,缓冲区的结果状态如下图所示:
buffer.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put((byte)'o');
- 翻转
buffer.flip();
即: buffer.limit(buffer.position()).position(0);
- 创建Buffer
ByteBuffer buffer=ByteBuffer.allocate(1024);//1K大小的缓冲池
ByteBuffer buffer=ByteBuffer.wrap(数组);
- 视图缓冲区
即缓冲区的复制
我们来看一下 ByteBuffer 转换成CharBuffer:
二. Channel(通道)
通道(Channel)是java.nio的第二个主要创新。它们既不是一个扩展也不是一项增强,而是全新、极好的Java I/O示例,提供与I/O服务的直接连接。Channel用于在字节缓冲区和位于通道另一侧的实体(通常是一个文件或套接字)之间有效地传输数据。
常用的通道:一个FileChannel类和三个socket通道类:SocketChannel、ServerSocketChannel和DatagramChannel。
打开通道:
SocketChannel sc = SocketChannel.open( );
sc.connect (new InetSocketAddress ("somehost", someport));
ServerSocketChannel ssc = ServerSocketChannel.open( );
ssc.socket( ).bind (new InetSocketAddress (somelocalport));
DatagramChannel dc = DatagramChannel.open( );
RandomAccessFile raf = new RandomAccessFile ("somefile", "r");
FileChannel fc = raf.getChannel( );
一般用read(),或者write()来进行读写。
- ServerSocketChannel
ServerSocketChannel是一个基于通道的socket监听器。
它同我们所熟悉的java.net.ServerSocket执行相同的基本任务,不过它增加了通道语义,因此能够在非阻塞模式下运行。
用静态的open( )工厂方法创建一个新的ServerSocketChannel对象,将会返回同一个未绑定的java.net.ServerSocket关联的通道。该对等ServerSocket可以通过在返回的ServerSocketChannel上调用socket( )方法来获取。作为ServerSocketChannel的对等体被创建的ServerSocket对象依赖通道实现。这些socket关联的SocketImpl能识别通道。通道不能被封装在随意的socket对象外面。
- SocketChannel
Socket和SocketChannel类封装点对点、有序的网络连接,类似于我们所熟知并喜爱的TCP/IP网络连接。SocketChannel扮演客户端发起同一个监听服务器的连接。直到连接成功,它才能收到数据并且只会从连接到的地址接收。
每个SocketChannel对象创建时都是同一个对等的java.net.Socket对象串联的。静态的open( )方法可以创建一个新的SocketChannel对象,而在新创建的SocketChannel上调用socket( )方法能返回它对等的Socket对象;在该Socket上调用getChannel( )方法则能返回最初的那个SocketChannel。
ps:虽然每个SocketChannel对象都会创建一个对等的Socket对象,反过来却不成立。直接创建的Socket对象不会关联SocketChannel对象,它们的getChannel( )方法只返回null。
需要注意的是:SocketChannel的API中connect( )和finishConnect( )方法是互相同步的,并且只要其中一个操作正在进行,任何读或写的方法调用都会阻塞,即使是在非阻塞模式下。如果此情形下您有疑问或不能承受一个读或写操作在某个通道上阻塞,请用isConnected( )方法测试一下连接状态。
- DatagramChannel
正如SocketChannel模拟连接导向的流协议(如TCP/IP),DatagramChannel则模拟包导向的无连接协议(如UDP/IP)。
创建DatagramChannel的模式和创建其他socket通道是一样的:调用静态的open( )方法来创建一个新实例。新DatagramChannel会有一个可以通过调用socket( )方法获取的对等DatagramSocket对象。DatagramChannel对象既可以充当服务器(监听者)也可以充当客户端(发送者)。如果您希望新创建的通道负责监听,那么通道必须首先被绑定到一个端口或地址/端口组合上。
数据的实际发送或接收是通过send( )和receive( )方法来实现的。
- Channels(通道工具类)
方法 | 返回 | 描述 |
---|---|---|
newChannel (InputStream in) | ReadableByteChannel | 返回一个将从给定的输入流读取数据的通道。 |
newChannel (OutputStream out) | WritableByteChannel | 返回一个将向给定的输出流写入数据的通道。 |
newInputStream (ReadableByteChannel ch) | InputStream | 返回一个将从给定的通道读取字节的流。 |
newOutputStream (WritableByteChannel ch) | OutputStream | 返回一个将向给定的通道写入字节的流。 |
newReader (ReadableByteChannel ch, CharsetDecoder dec, intminBufferCap) | Reader | 返回一个reader,它将从给定的通道读取字节并依据提供的CharsetDecoder对读取到的字节进行解码。 |
newReader(ReadableByteChannel ch, String csName) | Reader | 返回一个reader,它将从给定的通道读取字节并依据提供的字符集名称将读取到的字节解码成字符。 |
newWriter (WritableByteChannel ch, CharsetEncoder dec, int minBufferCap) | Writer | 返回一个writer,它将使用提供的CharsetEncoder对象对字符编码并写到给定的通道中。 |
newWriter (WritableByteChannel ch, String csName) | Writer | 返回一个writer,它将依据提供的字符集名称对字符编码并写到给定的通道中。 |