通道:
既不是一个扩展也不是一项增强,而是全新、极好的Java I/O示例,提供与I/O服务的直接连接。
Channel用于在字节缓冲区和位于通道另一侧的实体(通常是一个文件或套接字)之间有效地传输数据。
通道基础:与缓冲区不同,通道API主要由接口指定。
InterruptibleChannel是一个标记接口,当被通道使用时可以标示该通道是可以中断的(Interruptible)。
大多数但非全部的通道都是可以中断的。
从Channel接口引申出的其他接口都是面向字节的子接口,包括WritableByteChannel 和 ReadableByteChannel。
通道只能在字节缓冲区上操作。
尽管描述通道行为的接口都是在java.nio.channels包中定义的,不过具体的通道实现却都是从java.nio.channels.spi中的类引申来的。
打开通道:
通道是访问I/O服务的导管。
I/O可以分为广义的两大类别:File I/O和Stream I/O。相应地有两种类型的通道也就不足为怪了,它们是文件(file)通道和套接字(socket)通道。
一个FileChannel类和三个socket通道类:SocketChannel、ServerSocketChannel和DatagramChannel。
Socket通道有可以直接创建新socket通道的工厂方法。
FileChannel对象却只能通过在一个打开的RandomAccessFile、FileInputStream或FileOutputStream对象上调用getChannel( )方法来获取。
您不能直接创建一个FileChannel对象。
使用通道:
通道将数据传输给ByteBuffer对象或者从ByteBuffer对象获取数据进行传输。
通道可以是单向(unidirectional)或者双向的(bidirectional)。
实现定义read()方法的ReadableByteChannel接口
实现定义write()方法的WritableByteChannel接口
只实现一种方法则是单向的,实现两个方法是双向的。
ByteChannel的read() 和write()方法使用ByteBuffer对象作为参数。
两种方法均返回已传输的字节数,可能比缓冲区的字节数少甚至可能为零。缓冲区的位置也会发生与已传输字节相同数量的前移。
如果只进行了部分传输,缓冲区可以被重新提交给通道并从上次中断的地方继续传输。该过程重复进行直到缓冲区的hasRemaining( )方法返回false值。
通道可以以阻塞(blocking)或非阻塞(nonblocking)模式运行。
只有面向流的(stream-oriented)的通道,如sockets和pipes才能使用非阻塞模式。
socket通道类从SelectableChannel引申而来。
从SelectableChannel引申而来的类可以和支持有条件的选择(readiness selectio)的选择器(Selectors)一起使用。
关闭通道:
与缓冲区不同,通道不能被重复使用。
一个打开的通道即代表与一个特定I/O服务的特定连接并封装该连接的状态。当通道关闭时,那个连接会丢失。
可以通过isOpen( )方法来测试通道的开放状态。如果返回true值,那么该通道可以使用。如果返回false值,那么该通道已关闭。
当I/O操作被中断时总是关闭通道
Scatter/Gather:
是指在多个缓冲区上实现一个简单的I/O操作。
对于一个write操作而言,数据是从几个缓冲区按顺序抽取(称为gather)并沿着通道发送的。
该gather过程的效果就好比全部缓冲区的内容被连结起来,并在发送数据前存放到一个大的缓冲区中。
对于read操作而言,从通道读取的数据会按顺序被散布(称为scatter)到多个缓冲区,将每个缓冲区填满直至通道中的数据或者缓冲区的最大空间被消耗完。
文件通道:
文件通道总是阻塞式的,因此不能被置于非阻塞模式。
调用getChannel( )方法会返回一个连接到相同文件的FileChannel对象且该FileChannel对象具有与file对象相同的访问权限,
然后您就可以使用该通道对象来利用强大的FileChannel API了
FileChannel对象是线程安全(thread-safe)的。但并非所有的操作都是多线程的(multithreaded)。
访问文件:
FileChannel位置(position)是从底层的文件描述符获得的,
该position同时被作为通道引用获取来源的文件对象共享。
这也就意味着一个对象对该position的更新可以被另一个对象看到。
public abstract class FileChannel extends AbstractChannel implements ByteChannel, GatheringByteChannel, ScatteringByteChannel {
// This is a partial API listing
public abstract long position( ) //每个FileChannel都有一个叫“file position”的概念。
//这个position值决定文件中哪一处的数据接下来将被读或者写。
public abstract void position (long newPosition) //带一个long(长整型)参数并将通道的position设置为指定值。
//假如在将position设置为超出当前文件大小时实现了一个read( )方法,
//那么会返回一个文件尾(end-of-file)条件;
//倘若此时实现的是一个write( )方法则会引起文件增长以容纳写入的字节,
//具体行为类似于实现一个绝对write( )并可能导致出现一个文件空洞.
public abstract int read (ByteBuffer dst) //类似于缓冲区的get( ) 和put( )方法,当字节被read( )或write( )方法传输时,//文件position会自动更新。
public abstract int read (ByteBuffer dst, long position) //同样类似于缓冲区,也有带position参数的绝对形式的read( )和write( )方法。
//这种绝对形式的方法在返回值时不会改变当前的文件position。
public abstract int write (ByteBuffer src)
public abstract int write (ByteBuffer src, long position) //尝试在文件末尾之外的position进行一个绝对读操作,size( )方法会返回一个end-of-file。
//在超出文件大小的position上做一个绝对write( )会导致文件增加以容纳正在被写入的新字节。
public abstract long size( )
public abstract void truncate (long size) //truncate( )方法会砍掉您所指定的新size值之外的所有数据。如果size比旧size大,则内容无变化。
public abstract void force (boolean metaData)//要求文件的所有待定修改立即同步到磁盘。
//force( )方法的布尔型参数表示在方法返回值前文件的元数据(metadata)是否也要被同步更新到磁盘。
}
文件锁定:
与文件锁定有关的FileChannel API方法:
public abstract class FileChannel extends AbstractChannel implements ByteChannel, GatheringByteChannel, ScatteringByteChannel {
// This is a partial API listing
//不带参数的简单形式的lock( )方法是一种在整个文件上请求独占锁的便捷方法
public final FileLock lock( )
//调用带参数的Lock( )方法会指定文件内部锁定区域的开始position以及锁定区域的size。
//第三个参数shared表示您想获取的锁是共享的(参数值为true)还是独占的(参数值为false)。
//锁定区域的范围不一定要限制在文件的size值以内,锁可以扩展从而超出文件尾。
public abstract FileLock lock (long position, long size, boolean shared)
//是lock( )方法的非阻塞变体。这两个tryLock( )和lock( )方法起相同的作用,不过如果请求的锁不能立即获取到则会返回一个null
public final FileLock tryLock( )
public abstract FileLock tryLock (long position, long size, boolean shared)
}
FileLock类封装一个锁定的文件区域。FileLock对象由FileChannel创建并且总是关联到那个特定的通道实例。
您可以通过调用channel( )方法来查询一个lock对象以判断它是由哪个通道创建的。
一个FileLock对象创建之后即有效,直到它的release( )方法被调用或它所关联的通道被关闭或Java虚拟机关闭时才会失效。
以通过调用isShared( )方法来测试一个锁以判断它是共享的还是独占的。
可以通过调用overlaps( )方法来查询一个FileLock对象是否与一个指定的文件区域重叠。
内存映射文件:
FileChannel类提供了一个名为map( )的方法,该方法可以在一个打开的文件和一个特殊类型的ByteBuffer之间建立一个虚拟内存映射.
在FileChannel上调用map( )方法会创建一个由磁盘文件支持的虚拟内存映射(virtual memory mapping)并在那块虚拟内存空间外部封装一个MappedByteBuffer对象.
由map()方法返回的MappedByteBuffer对象的行为在多数方面类似一个基于内存的缓冲区,只不过该对象的数据元素存储在磁盘上的一个文件中。
调用get( )方法会从磁盘文件中获取数据,对映射的缓冲区实现一个put( )会更新磁盘上的那个文件.
通过内存映射机制来访问一个文件会比使用常规方法读写高效得多,甚至比使用通道的效率都高.
与文件锁的范围机制不一样,映射文件的范围不应超过文件的实际大小。如果您请求一个超出文件大小的映射,文件会被增大以匹配映射的大小。
同常规的文件句柄类似,文件映射可以是可写的或只读的。
所有的MappedByteBuffer对象都是直接缓存区,这意味着它们占用的内存空间位于Java虚拟机内存堆之外
MappedByteBuffer还定义了几个它独有的方法:
public abstract class MappedByteBuffer extends ByteBuffer {
// This is a partial API listing
public final MappedByteBuffer load( ) //load( )方法会加载整个文件以使它常驻内存。在一个映射缓冲区上调用load( )方法会是一个代价高的操作.
public final boolean isLoaded( ) //判断一个被映射的文件是否完全常驻内存了。
public final MappedByteBuffer force( ) //会强制将映射缓冲区上的更改应用到永久磁盘存储器上。
//如果映射是以MapMode.READ_ONLY或MAP_MODE.PRIVATE模式建立的,那么调用force( )方法将不起任何作用
}
Channel-to-Channel传输:
public abstract class FileChannel
extends AbstractChannel implements ByteChannel, GatheringByteChannel, ScatteringByteChannel {
// This is a partial API listing
public abstract long transferTo (long position, long count, WritableByteChannel target)
public abstract long transferFrom (ReadableByteChannel src,long position, long count)
}
FileChannel类的transferTo( )和transferFrom( )方法允许将一个通道交叉连接到另一个通道,而不需要通过一个中间缓冲区来传递数据。
只有FileChannel类有这两个方法,因此channel-to-channel传输中通道之一必须是FileChannel。
直接的通道传输不会更新与某个FileChannel关联的position值。请求的数据传输将从position参数指定的位置开始,传输的字节数不超过count参数的值。
Channel-to-channel传输是可以极其快速的,特别是在底层操作系统提供本地支持的时候。
Socket通道:
socket通道类可以运行非阻塞模式并且是可选择的。
全部socket通道类(DatagramChannel、SocketChannel和ServerSocketChannel)都是由位于java.nio.channels.spi包中的AbstractSelectableChannel引申而来。
全部socket通道类(DatagramChannel、SocketChannel和ServerSocketChannel)在被实例化时都会创建一个对等socket对象。
对等socket可以通过调用socket( )方法从一个通道上获取。
有条件的选择(readiness selection)是一种可以用来查询通道的机制,该查询可以判断通道是否准备好执行一个目标操作,如读或写。
ServerSocketChannel是一个基于通道的socket监听器。api:
public abstract class ServerSocketChannel extends AbstractSelectableChannel {
public static ServerSocketChannel open( ) throws IOException
public abstract ServerSocket socket( );
public abstract ServerSocket accept( ) throws IOException;
public final int validOps( )
}
静态的open( )工厂方法创建一个新的ServerSocketChannel对象。
该channel的对等ServerSocket可以通过在返回的ServerSocketChannel上调用socket( )方法来获取。
ServerSocketChannel和ServerSocket都有accept()方法,
如果在ServerSocket上调用accept(),总是阻塞并返回一个java.net.Socket对象。
如果在ServerSocketChannel上调用accept( ),返回的对象能够在非阻塞模式下运行.
Socket通道是线程安全的。
sockets是面向流的而非包导向的。它们可以保证发送的字节会按照顺序到达但无法承诺维持字节分组。
某个发送器可能给一个socket写入了20个字节而接收器调用read( )方法时却只收到了其中的3个字节。剩下的17个字节还是传输中。
DatagramChannel:
SocketChannel模拟连接导向的流协议(如TCP/IP)
DatagramChannel则模拟包导向的无连接协议(如UDP/IP)
数据的实际发送或接收是通过send( )和receive( )方法来实现的:
public abstract class DatagramChannel extends AbstractSelectableChannel
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel {
// This is a partial API listing
public abstract SocketAddress receive (ByteBuffer dst) throws IOException;
public abstract int send (ByteBuffer src, SocketAddress target)
}
DatagramChannel是无连接的。每个数据报(datagram)都是一个自包含的实体,拥有它自己的目的地址及不依赖其他数据报的数据净荷。
与面向流的的socket不同,DatagramChannel可以发送单独的数据报给不同的目的地址。
管道:
Pipe类创建一对提供环回机制的Channel对象。
这两个通道的远端是连接起来的,以便任何写在SinkChannel对象上的数据都能出现在SourceChannel对象上。
Pipe实例是通过调用不带参数的Pipe.open( )工厂方法来创建的。Pipe类定义了两个嵌套的通道类来实现管路。
这两个类是Pipe.SourceChannel(管道负责读的一端)和Pipe.SinkChannel(管道负责写的一端)。
这两个通道实例是在Pipe对象创建的同时被创建的,可以通过在Pipe对象上分别调用source( )和sink( )方法来取回。
管道可以被用来仅在同一个Java虚拟机内部传输数据。虽然有更加有效率的方式来在线程之间传输数据,但是使用管道的好处在于封装性。
管路所能承载的数据量是依赖实现的(implementation-dependent)。
唯一可保证的是写到SinkChannel中的字节都能按照同样的顺序在SourceChannel上重现。
通道工具类:
方法 返回 描述
newChannel (InputStream in) ReadableByteChannel 返回一个将从给定的输入流读取数据的通道。
newChannel (OutputStream out) WritableByteChannel 返回一个将向给定的输出流写入数据的通道。
newInputStream (ReadableByteChannel ch) InputStream 返回一个将从给定的通道读取字节的流。
newOutputStream (WritableByteChannel ch) OutputStream 返回一个将向给定的通道写入字节的流。
newReader (ReadableByteChannel ch,
CharsetDecoder dec, int minBufferCap) 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,它将依据提供的字符集名称对字符编码并写到给定的通道中。