重要的一个接口和两个抽象类
在NIO中,有一个接口和两个抽象类是我们需要重点了解的,Channel,Buffer,Selector
1.Channel
Channel跟stream差不多,但是Channel是双向的(可以通过transferTo()或transferFrom()来改变数据的流向)
接口源码
public interface Channel extends Closeable {
//Channel接口只有两个抽象方法
//需要实现功能:检查是否开启
public boolean isOpen();
//需要实现功能:关闭通道
public void close() throws IOException;
}
主要抽象类
这几个类依然是没有实现,它们的实现都是类名后加上Impl,如果想深入研究的话,就去看实现类吧,我这里就只简单说明一下它们的抽象类
-
FileChannel
这个就是文件通道了,上面的代码中也有使用到,创建方式就不多说了。它的主要功能就是与文件进行数据传输 -
DatagramChannel
Datagram就是数据报的意思,学过网络编程的应该都知道,数据报通道就是可以通过UDP在网络上读取和写入数据,它的创建方法:
DatagramChannel ch = DatagramChannel.open();
-
SocketChannel
Socket就是套接字,套接字通道可以通过TCP在网络上读取和写入数据,它的创建方法:
SocketChannel ch = SocketChannel.open();
//使用它还需要绑定一个ip地址
-
ServerSocketChannel
ServerSocketChannel是服务器套接字通道,能够监听TCP的连接,在注册到selector后,当selector的迭代器遍历到相应的ServerSocketChannel时,它会根据SelectionKey获取到ServerSocketChannel创建的SocketChannel,然后进行操作
2.Buffer
Buffer就是在进行NIO时开辟缓冲区的对象
抽象类源码
内容太多了,简单易懂的我就跳过了
public abstract class Buffer {
static final int SPLITERATOR_CHARACTERISTICS =
Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;
// Invariants: mark <= position <= limit <= capacity
//这四个数是整个Buffer的关键
private int mark = -1;//标记,可以通过这个标记回溯到标记的位置
private int position = 0;//当前位置,读和写的操作都是从这个位置开始的
private int limit;//界限,读和写都不能超过这个界限
private int capacity;//容量,能够写入的个数,以具体实现类为准,例如IntBuffer,最大就能写capacity个int
long address;//地址,这是指向缓冲区的地址
Buffer(int mark, int pos, int lim, int cap) {
if (cap < 0)
throw new IllegalArgumentException("Negative capacity: " + cap);
this.capacity = cap;
limit(lim);
position(pos);
if (mark >= 0) {
if (mark > pos)
throw new IllegalArgumentException("mark > position: ("+ mark + " > " + pos + ")");
this.mark = mark;
}
}
public final int capacity() {
return capacity;
}
public final int position() {
return position;
}
//这是重新定义position,如果大于limit或小于0则报错
//如果mark大于新的position,则丢去mark
public final Buffer position(int newPosition) {
if ((newPosition > limit) || (newPosition < 0))
throw new IllegalArgumentException();
position = newPosition;
if (mark > position) mark = -1;
return this;
}
public final int limit() {
return limit;
}
//重新定义limit,如果limit大于capacity或小于0,则报错
//如果position大于新的limit,则重新定位到limit
//如果mark大于新的limit,则丢弃
public final Buffer limit(int newLimit) {
if ((newLimit > capacity) || (newLimit < 0))
throw new IllegalArgumentException();
limit = newLimit;
if (position > limit) position = limit;
if (mark > limit) mark = -1;
return this;
}
public final Buffer mark() {
mark = position;
return this;
}
//将position重新定位到mark上
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;
}
//这个也可以用来切换到读模式,但是没有修改limit
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
public final int remaining() {
return limit - position;
}
public final boolean hasRemaining() {
return position < limit;
}
//需要实现的功能:
//返回这个buffer是否只可读
public abstract boolean isReadOnly();
//需要实现的功能:
//返回这个buffer的缓冲区,是否是一个可读的数组构成的
public abstract boolean hasArray();
//需要实现的功能:
//如果这个buffer缓冲区的数组不是只可读,就返回一个数组
public abstract Object array();
//需要实现的功能:
//如果这个buffer缓冲区的数组不是只可读,返回数组的地址
public abstract int arrayOffset();
//需要实现的功能:
//判断缓冲区是否是直接缓冲区,也就是是否存储在物理内存中,而不是存储在JVM中
public abstract boolean isDirect();
final int nextGetIndex() {
if (position >= limit)
throw new BufferUnderflowException();
return position++;
}
//将position后移nb个
final int nextGetIndex(int nb) {
if (limit - position < nb)
throw new BufferUnderflowException();
int p = position;
position += nb;
return p;
}
//仅是抛出的异常不同
//上面是下溢异常,下面是上溢异常
final int nextPutIndex() {
if (position >= limit)
throw new BufferOverflowException();
return position++;
}
final int nextPutIndex(int nb) {
if (limit - position < nb)
throw new BufferOverflowException();
int p = position;
position += nb;
return p;
}
//检查是否能够将position定位到i上
final int checkIndex(int i) {
if ((i < 0) || (i >= limit))
throw new IndexOutOfBoundsException();
return i;
}
//检查是否能够将position定位到i+nb上
final int checkIndex(int i, int nb) {
if ((i < 0) || (nb > limit - i))
throw new IndexOutOfBoundsException();
return i;
}
final int markValue() {
return mark;
}
//清空
final void truncate() {
mark = -1;
position = 0;
limit = 0;
capacity = 0;
}
final void discardMark() {
mark = -1;
}
//检查开始位置,长度,尺寸是否符合条件
static void checkBounds(int off, int len, int size) { // package-private
if ((off | len | (off + len) | (size - (off + len))) < 0)
throw new IndexOutOfBoundsException();
}
}
主要抽象类
主要的抽象类有七个,ByteBuffer、CharBuffe、DoubleBuffer、FloatBuffer、 IntBuffer、 LongBuffer、 ShortBuffer,
分别对应基本数据类型: byte, char, double, float, int, long, short。
它们也都是没有实现的,它们的实现类就更加多样化了,这里就不展开讲述了,功能基本上都是差不多的,只是对应的数据类型不一样
3.Selector
Selector被称为选择器,也被称为多路复用器,是整个NIO中最核心的组件(个人觉得,不适用Selector就没有必要使用NIO了)
Channel能够通过register()把自己注册到Selector中并设置一个SelectionKey,然后Selector根据SelectionKey的迭代器不断的循环,当有符合某个key时,就可以进行操作了,操作时根据SelectionKey就可以获取到对应的Channel了。
graph LR
A[selector] --> B((channel))
A --> C((channel))
A --> D((channel))
A --> E((channel))
抽象类源码
public abstract class Selector implements Closeable {
protected Selector() { }
//这是通过SelectorProvider的内部静态对象provider创建一个Selector的实现类实例
//最后的实例对象是WindowsSelectorImpl的实例
public static Selector open() throws IOException {
return SelectorProvider.provider().openSelector();
}
public abstract boolean isOpen();
//需要实现的功能:
//返回一个通道的创建者
public abstract SelectorProvider provider();
//需要实现的功能:
//返回选择器的键集,该键集不能被修改
public abstract Set<SelectionKey> keys();
//需要实现的功能:
//返回选择器的键集,该键集可以被修改
public abstract Set<SelectionKey> selectedKeys();
//需要实现的功能:
//开始监听通道,如果没有通道是已经准备好的,就返回0
public abstract int selectNow() throws IOException;
//需要实现的功能:
//开始监听通道,设置一个超时时间
public abstract int select(long timeout)
throws IOException;
//需要实现的功能:
//开始监听通道
public abstract int select() throws IOException;
//需要实现的功能:
//唤醒那些阻塞在select方法上的线程
//在实现类WindowsSelectorImpl中,有一个线程数组,应该是唤醒这个数组中的线程
public abstract Selector wakeup();
public abstract void close() throws IOException;
}
最终的实现类是WindowsSelectorImpl
——————————————————————————————
你知道的越多,不知道的越多。
如果本文章内容有问题,请直接评论或者私信我。如果觉得写的还不错的话,点个赞也是对我的支持哦
未经允许,不得转载!