java.nio全称java non-blocking(非阻塞) IO(其实是 new io),是指jdk1.4 及以上版本里提供的新api(New IO) ,为全部的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它能够提供非阻塞式的高伸缩性网络。java
IO | NIO |
---|---|
面向流(Stream Oriented) | 面向缓冲区(Buffer Oriented) |
阻塞IO(Blocking IO) | 非阻塞(Non Blocking IO) |
无 | 选择器(Selectors) |
NIO系统的核心是:通道(Channel)和缓冲区(Buffer)api
位于 java.nio 包,全部缓冲区都是 Buffer 抽象类的子类,使用数组对数据进行缓冲。数组
除了 boolean 类型,Buffer 对每种基本数据类型都有针对的实现类:缓存
建立缓冲区经过 xxxBuffer.allocate(int capacity)方法网络
ByteBuffer buf1 = ByteBuffer.allocate(512); LongBuffer buf2 = LongBuffer.allocate(1024); ……
容量(capacity):表示缓冲区存储数据的最大容量,不能为负数,建立后不可修改。app
限制:第一个不能够读取或写入的数据的索引,即位于 limit 后的数据不能读写。不能为负数,不能大于容量。dom
位置(position):下一个要读取或写入的数据的索引,位置不能为负数,不能大于 limitpost
标记(mark):标记是一个索引,经过 Buffer 中的 mark() 方法指 Buffer 中一个特定的 position,以后能够经过 reset() 方法回到这个 postion。性能
方法名称 | 说明 |
---|---|
Buffer clear() | 清空缓冲区并返回对缓冲区的引用 |
Buffer flip() | 将缓冲区的 limit 设置为当前位置,并将当前位置重置为0 |
int capacity() | 返回 Buffer 的容量大小 |
boolean hasRemaining() | 判断缓冲区是否还有元素 |
int limit() | 返回 限制的位置 |
Buffer limit(int n) | 将设置缓冲区界限为 n,并返回一个具备新 limit 的缓冲区对象 |
Buffer mark() | 对缓冲区设置标记 |
int position() | 返回缓冲区的当前位置 position |
Buffer position(int n) | 将设置缓冲区的当前位置为 n,并返回修改后的 Buffer 对象 |
int remaining() | 返回 position 和 limit 之间的元素个数 |
Buffer reset() | 将位置 position 转到之前设置的 mark 所在的位置 |
Buffer rewind() | 将位置设置为 0,取消设置的 mark |
Buffer 全部子类提供了两个操做的数据的方法:get() 方法和 put() 方法code
缓冲区存取数据操做
package testnio; import java.nio.ByteBuffer; public class TestBuffer1 { public static void main(String[] args) { testuse(); } public static void testuse() { //1.分配一个指定大小的缓冲区 ByteBuffer buf=ByteBuffer.allocate(1024); System.out.println("---------------allocate()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //2.利用 put() 存入数据到缓冲区中 String str="hello"; //将字符串转为 byte 数组存入缓冲区 buf.put(str.getBytes()); System.out.println("---------------put()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //3.切换读取数据模式 buf.flip(); System.out.println("---------------flip()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //4.利用get() 读取缓冲区中的数据 byte[] data=new byte[buf.limit()]; System.out.println("---------------get()----------------"); buf.get(data); System.out.println(new String(data,0,data.length)); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //5.rewind() 重复读 buf.rewind(); System.out.println("---------------rewind()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //6.clear() 清空缓冲区,但缓冲区中的数据依然存在 buf.clear(); System.out.println("---------------clear()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); System.out.println((char)buf.get()); } }
使用 mark()方法标记
package testnio; import java.nio.ByteBuffer; public class TestBuffer2 { public static void main(String[] args) { testmark(); } public static void testmark() { String str="jikedaquan.com"; //建立缓冲区 ByteBuffer buf=ByteBuffer.allocate(1024); //存入数据 buf.put(str.getBytes()); //切换模式 buf.flip(); //临时数组用于接收缓冲区获取的数据,长度与缓冲区 limit 一值 byte[] data=new byte[buf.limit()]; //获取缓冲区的数据从0开始获取4个,存入 data 数组中 buf.get(data, 0, 4); //将数组转为字符串打印 System.out.println(new String(data,0,4)); //打印 position System.out.println(buf.position()); //标记 buf.mark(); System.out.println("---------------再次获取----------------"); //从索引4开始,获取6个字节(余下数据) buf.get(data, 4, 6); System.out.println(new String(data,4,6)); System.out.println(buf.position()); //恢复到标记位置 buf.reset(); System.out.println("---------------reset()----------------"); System.out.println(buf.position()); //判断缓冲区是是有还有剩余数据 if (buf.hasRemaining()) { //获取缓冲区中能够操做的数量 System.out.println("可操做数量:"+buf.remaining()); } } }
mark <= position <= limit <= capacity
虽然使用了缓冲区提升了必定的IO速度,但这样的效率仍然不是最高的。非直接缓冲区在与物理磁盘操做中须要通过内核地址空间copy操做,直接缓冲区不通过copy操做,直接操做物理内存映射文件,
使用直接缓冲区将大大提升效率。
直接缓冲区进行分配和取消分配所需成本工厂高于非直接缓冲区,通常状况下,最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们。
直接缓冲区能够经过调用此类的 allocateDirect()工厂方法建立
建立直接缓冲区
package testnio; import java.nio.ByteBuffer; public class TestBuffer3 { public static void main(String[] args) { testAllocateDirect(); } public static void testAllocateDirect() { //建立直接缓冲区 ByteBuffer buf=ByteBuffer.allocateDirect(1024); //是不是直接缓冲区 System.out.println(buf.isDirect()); } }
缓冲区仅是运载数据的容器,须要对数据读写还须要有一条通道,这二者是密不可分的。
Channel 接口的主要实现类:
支持通道的类:
使用通道和缓冲区实现文件读和写
package testnio; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class TestChannel { public static void main(String[] args) { FileInputStream fis=null; FileOutputStream fos=null; FileChannel inChannel=null; FileChannel outChannel=null; try { //建立输入流 fis=new FileInputStream("F:/1.jpg"); //建立输出流 fos=new FileOutputStream("F:/2.jpg"); //获取通道 inChannel=fis.getChannel(); outChannel=fos.getChannel(); //分配指定大小的缓冲区 ByteBuffer buf=ByteBuffer.allocate(1024); //将通道中的数据存入缓存区 while(inChannel.read(buf)!=-1) { //切换读取数据的模式 buf.flip(); //将读入的缓冲区存入写数据的管道 outChannel.write(buf); //清空缓存区(清空才能再次读入) buf.clear(); } } catch (IOException e) { e.printStackTrace(); }finally { if (fis!=null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if (fos!=null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(inChannel!=null) { try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if (outChannel!=null) { try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
package testnio; import java.io.IOException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; public class TestOpenAndMapped { public static void main(String[] args) { FileChannel inChannel=null; FileChannel outChannel=null; try { //经过open建立通道 inChannel = FileChannel.open(Paths.get("F:/a.jpg"), StandardOpenOption.READ); outChannel = FileChannel.open(Paths.get("F:/b.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); //内存映射文件 直接缓冲区 MappedByteBuffer inMappedBuf=inChannel.map(MapMode.READ_ONLY, 0, inChannel.size()); MappedByteBuffer outMappedBuf=outChannel.map(MapMode.READ_WRITE, 0, inChannel.size()); //直接对缓冲区进行数据的读写操做 byte[] data=new byte[inMappedBuf.limit()]; inMappedBuf.get(data);//读 outMappedBuf.put(data);//写 } catch (IOException e) { e.printStackTrace(); }finally { if (inChannel!=null) { try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if (outChannel!=null) { try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
JDK 1.7 新增的方法 open(),参数 path 一般表明一个依赖系统的文件路径,经过Paths.get()获取。
参数 StandardOpenOption 是一个枚举类型,经常使用值以下:
MappedByteBuffer:直接字节缓冲区,其内容是文件的内存映射区域。
将数据从源通道传输到其余 Channel 中,transferTo() 和 transferFrom()
package testnio; import java.io.IOException; import java.nio.channels.FileChannel; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; public class TestChannelTransfer { public static void main(String[] args) { FileChannel inChannel=null; FileChannel outChannel=null; try { //经过open建立管道 inChannel = FileChannel.open(Paths.get("F:/a.jpg"), StandardOpenOption.READ); outChannel = FileChannel.open(Paths.get("F:/b.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); //将inChannel中全部数据发送到outChannel //inChannel.transferTo(0, inChannel.size(), outChannel); outChannel.transferFrom(inChannel, 0, inChannel.size()); } catch (IOException e) { e.printStackTrace(); }finally { if (inChannel!=null) { try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if (outChannel!=null) { try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
Channel 负责传输,Buffer 负责存储