程序读取数据模型不同传统IO和NIO特点面向流。单向(只读或只写)。读写程序直接与操作系统交互。读写线程被阻塞。NIO特性是面向缓冲区的。双向(读和写)。程序通过Channel进行读写,程序与Channel的交互通过缓冲区。读写线程不会阻塞。(但是选择器的select()会阻塞)。NIO读取数据模型NIO读取数据时,通过Channel将数据写入缓冲区,程序从缓冲区中读取数据。写入数据也是如此。NIO的四个核心组件NIO有四个核心组件,它们是:Buffer:用于存储数据以供读取或写入的容器。Channel:与一些组件建立连接,类似于JavaIO中的流,进行读或写操作,但与IO流不同的是,Channel是双向的。Charsets:包含字符集(charsets)、解码器(decoders)、编码器(encoders),可用于byte到unicode的转换。选择器:通过选择器,您可以使用多个通道。BufferBuffer是一个存储固定数据大小的容器,存储Java原始数据类型的数据。一个Buffer由以下部分组成:?Capacity:容器容量?Limit:不可读或不可写的索引?Position:要读或写的下一个元素索引?Flip:切换IO操作(读写操作)?Rewind:设置position=0,limit不变?Mark:标记Buffer中的一个位置?Reset:将position重置为标记的位置DirectbytebufferJava可以使用ByteBuffer的allocateDirect方法创建直接buffer:ByteBufferdirectBuffer=ByteBuffer.allocateDirect(1024);与非直接缓冲区相比。IO效率会更高。因为当我们使用非直接缓冲区时,操作系统会创建一个临时的直接缓冲区,将非直接缓冲区的内容读取到创建的临时直接缓冲区中,然后进行操作。单从这一点来看,使用directbuffers是IO的最佳选择。但是在Java中创建直接buffer是直接调用操作系统的API方法创建的,会绕过JVM的内存管理,而且JVM也针对buffer进行了优化,所以可能不是最好的使用方式直接缓冲区。向ByteBuffer写入数据publicclassByteBufferReadDemo{/***读取文件路径*/privatestaticfinalStringINPUT_FILE="./resources/input.txt";/***缓冲区大小*/privatestaticfinalintBYTE_BUFFER_LENGTH=1024;publicstaticvoidmain(String[]args){//通过IO流获取ChannelStringBuilder内容=newStringBuilder();int读取=0;//从通道读取数据(写入ByteBuffer)while((read=fileChannel.read(byteBuffer))!=-1){content.append(newString(byteBuffer.array(),0,read));//将ByteBuffer恢复到原来的状态:position=0,limit=capacity,discardmarkbyteBuffer.clear();}System.out.println(内容);}赶上(IOExceptione){thrownewRuntimeException("无法读取文件",e);}}}Buffer读写原理ByteBuffer继承了Buffer抽象类,三个重要的属性是:#读写的起始位置privateintposition=0;#限制读或写位置privateintlimit;#容量privateint容量;在新建一个容量为10的ByteBuffer对象时,它们的三个位置分别是:此时ByteBuffer处于写入模式,可以从position开始写入,limit是可以写入的最大位置,此时limit=capacityand然后我们将1234写入缓冲区,缓冲区的内部结构变为:position移动到最小可写位置5(position=5)。这时候如果我们要读取buffer数据,就需要切换到read模式(调用flip()方法):position移动到buffer中第一个可读的位置,limit移动到buffer中最后一个可读的位置缓冲。我们把所有的数据都读出来了,此时buffer里面变成了:position=limit此时,证明已经没有可读的数据了。这时候我们使用clear()来恢复ByteBuffer。这时候buffer回到write模式(默认是write模式):compact()和clear()的区别是clear()会直接把buffer恢复到刚刚创建的状态,而compact()将压缩未读取的数据。比如我们只读取1和2:此时调用compact(),buffer的内部结构就会变成:Channel可以通过Channel对Buffer进行读写。创建一个FileChannel://创建一个File对象finalFilefile=newFile(FileChannelReadExample.class.getClassLoader().getResource(path).getFile());//根据需要创建不同的FileChannels用于读取或写入returnfileOperation==FileOperation.READ?newFileInputStream(file).getChannel():newFileOutputStream(file).getChannel();Charsets包含字符集、解码器、编码器。unicode和byte之间的转换可以通过Charsets进行。编码与解码编码(Encoding):一个字符序列->字节。解码:字节->字符。Charsetuse//获取UTF-8字符集Charsetutf8Charset=Charset.forName("UTF-8");//编码字节缓冲区byteBuffer=utf8Charset.encode("zhangsan");//解码System.out.println(utf8Charset.decode(byteBuffer));SelectorSelector可以复用SelectableChannel,当SelectableChannel有IO事件时,Selector会通知我们的程序。这里的IO事件(SelectableChannel连接Selectors事件)包括:ConnectAcceptReadWriteSelectableChannel:一个可以多路复用连接Selecotr的通道。Server/ClientDemo使用JavaNIO实现一个简单的Server和Client示例:ServerpublicclassServer{protectedstaticfinalStringHOST="127.0.0.1";受保护的静态最终int端口=8899;publicstaticvoidmain(String[]args){try{ServerSocketChannelserverSocket=ServerSocketChannel.open();InetSocketAddresshostAddress=newInetSocketAddress(HOST,PORT);//绑定端口serverSocket.bind(hostAddress);//设置为非阻塞serverSocket.configureBlocking(false);//创建选择器Selectorselector=Selector.open();//通道注册到选择器(ACCEPT事件)serverSocket.register(selector,serverSocket.validOps(),null);while(true){//select阻塞intselectKeyNums=selector.select();如果(selectKeyNums>0){Set
