一、java新IO概述
javaIO中的输入流和输出流都是通过字节的移动来处理的,面向流的输入输出系统一次只能处理一个字节,因此效率不高,而且传统的输入输出流是阻塞试的,也就是说当无法读到数据时,当前线程会被阻塞直到读取到有效数据才会继续运行。
java1.4之后提供了一系列改进的输入输出类与方法,并且以NIO为基础改写了java.io包中的类,新增了满足NIO的功能。
NIO采用内存映射文件的方式,java.nio中主要的包有:
java.nio ,主要包含于Buffer相关的类;
java.nio.charset,主要包含字符集相关的类;
java.nio.channels,主要包含Channel和Selector相关的类;
java.nio.channels.spi,主要包含与Channel相关的服务提供者编程接口;
java.nio.charset.spi,包含与字符集相关的服务提供者编程接口。
二、Buffer抽象类
其子类有ByteBuffer(最常用)、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer,这些类没有构造器,获取buffer对象使用如下静态方法:static XxxBuffer allocate(int capacity)。
Buffer的几个位置属性:
相应的两个方法:clear(),let limit = capacity and position = 0,这相当于是为再次将数据写入Buffer做好准备,
flip(),let limit = position and position = 0 ,这相当于是为从Buffer中取出数据做好准备。
package NIOTest; import java.nio.CharBuffer; public class BufferTest { public static void main(String[] args) { CharBuffer buffer = CharBuffer.allocate(8); System.out.println("capacity:" +buffer.capacity()); System.out.println("limit:" +buffer.limit()); System.out.println("position" + buffer.position()); buffer.put(‘a‘); buffer.put(‘b‘); buffer.put(‘c‘); System.out.println("加入三个元素后,position = " + buffer.position()); buffer.flip(); System.out.println("执行flip()后,limit = " + buffer.limit()); System.out.println("position = " + buffer.position()); //去除第一个元素 System.out.println("第一个元素(position=0):" + buffer.get()); System.out.println("取出第一个元素后,position = " + buffer.position()); //调用clear方法 buffer.clear(); System.out.println("执行clear()后,limit = " + buffer.limit()); System.out.println("执行clear()后,position = " + buffer.position()); System.out.println("执行clear()后,buffer内容并没有被清除:" + "第三个元素为:"+ buffer.get(2)); System.out.println("执行绝对读取后,position = " + buffer.position()); } }
上面代码尝试了一下Buffer的使用,输出为下
capacity:8 limit:8 position0 加入三个元素后,position = 3 执行flip()后,limit = 3 position = 0 第一个元素(position=0):a 取出第一个元素后,position = 1 执行clear()后,limit = 8 执行clear()后,position = 0 执行clear()后,buffer内容并没有被清除:第三个元素为:c 执行绝对读取后,position = 0
上面程序使用的buffer是heapbuffer,每个heapbuffer在新建时都会创建一个对应的directbuffer,直接buffer的读取效率高但是创建成本也高,具体buffer1的工作方式不在这里深究= =,因为深究了没有实际应用也会忘记。。
三、 Channel接口
Channel用于与Buffer交互,实现数据的IO。
java为Channel接口提供了DatagramChannel(支持UDP网络通信)、FileChannel(文件读写)、Pipe.SinkChannel和Pipe.SourceChannel(支持线程间通信的管道)、SelectableChannel(可选择阻塞与非阻塞的channel)、ServerSocketChannel和SocketChannel(支持TCP网络通信)等等。
Channel通过传统的流节点来返回对应的Channel,常用的方法有map()、read()、write(),下面来试一试
package NIOTest; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.CharBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; public class FileChannelTest { public static void main(String[] args) { File f = new File("./src/main/java/NIOTest/FileChannelTest.java"); try ( FileChannel inChannnel = new FileInputStream(f).getChannel(); FileChannel outChannel = new FileOutputStream("a.txt").getChannel(); ) { MappedByteBuffer buffer = inChannnel.map(FileChannel.MapMode.READ_ONLY, 0, f.length()); Charset charset = Charset.forName("GBK"); outChannel.write(buffer); buffer.clear(); CharsetDecoder decoder = charset.newDecoder(); CharBuffer charBuffer = decoder.decode(buffer); System.out.println(charBuffer); } catch (IOException ioe) { ioe.printStackTrace(); } } }
下面代码每次运行都会讲a.txt文件的内容复制一份并将全部内容追加到该文件的后面
package NIOTest; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class RandomFileChannelTest { public static void mian (String[] args){ File f= new File("a.txt"); try( RandomAccessFile raf = new RandomAccessFile(f,"rw"); FileChannel randomChannel = raf.getChannel(); ){ ByteBuffer buffer = randomChannel.map(FileChannel.MapMode.READ_ONLY,0,f.length()); //这里移动的是channel的position,可以在任意位置将数据写入channel。 randomChannel.position(f.length()); randomChannel.write(buffer); }catch (IOException ioe){ ioe.printStackTrace(); } } }
四、Charset类
字符集类主要用于文本格式数据编码与解码,具体用法在 三 中的第一个例子有所展示。