Java NIO 简介

NIO简介

Java NIO 与传统 IO 相比,具有同样的作用和目的都是进行文件的读写操作,但是使用的方式完全不同,NIO支持面向缓冲区的、基于通道的IO操作。

Java NIO 与 IO 的主要区别:

IONIO
面向流(Stream Oriented)面向缓冲区(Buffer Oriented)
阻塞IO(Blocking IO)非阻塞IO(Non Blocking IO)
(无)选择器(Selectors)

Java NIO的核心在于通道(Channel)和缓冲区(Buffer),通道表示打开到 IO 设备(例如:文件、套接字)的连接。若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。在NIO中,数据总是在channel中读取到缓冲区,或者从缓冲区写入到channel。

简而言之,Channel 负责传输, Buffer 负责运输。


缓冲区(Buffer)

Buffer就像一个数组,在Java NIO 中负责数据的存取。可以保存多个相同类型的数据。根据数据类型不同(boolean 除外) ,有以下 Buffer 常用子类:

  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer
    这些缓冲区的管理方式几乎一致,都是通过allocate()获取缓冲区。
缓冲区存取数据的两个核心方法:
  • put():存入数据到缓冲区中
  • get():获取缓冲区中的数据
缓冲区中的四个核心属性
  • capacity:容量。表示缓冲区中最大存储数据的容量,一旦声明不可改变。
  • limit:界限。表示缓冲区中可以操作数据的大小。(表示limit后的数据不可进行读写。)
  • position:位置。表示缓冲区中正在操作数据的位置。

规律: mark <= position <= limit <= capacity
其中,mark表示标记,记录当前position的位置,可以通过reset()恢复到mark的位置。

  1. 分配一个指定大小的缓冲区

    ByteBuffer buf = ByteBUffer.allocate(1024);
    

    缓冲区中 position,capacity,limit的示意图如下:
    声明

  2. 缓冲区写数据

    String str = "abcde";
    ByteBuffer buf = ByteBUffer.allocate(1024);
    buf.put(str.getBytes());
    

写

  1. 切换到读取数据模式
    buf.flip()
    读

  2. 缓冲区mark的使用
    mark表示标记,表示记录当前position的位置,可以通过reset()恢复到mark的位置

    String str = "abcde";
    ByteBuffer buf = ByteBuffer.allocate(1024);
    buf.put(str.getBytes);//向缓冲区中写入数据
    buf.flip();//切换到读数据模式
    byte[] dst = new byte[buf.limit()]; //声明一个limit大小的数组
    buf.get(dst,0,2); //从缓冲区读两个字节数据到声明的数组中
    
    buf.mark();//标记一下此时position的位置,此时position=2
    
    buf.get(dst,2,2); //再读取两个字节的数据,此时position=4
    
    buf.reset();//恢复到mark的位置
    
缓冲区的直接缓冲区与非直接缓冲区:

非直接缓冲区,是通过allocate()方法分配的缓冲区,建立在JVM的内存中的的一种缓冲区。
非直接缓冲区
应用程序跟物理内存之间要经过一系列复杂的读写拷贝操作。

直接缓冲区,通过allocateDirect()方法分配的一种缓冲区,将缓冲区建立在操作系统的物理内存中。在某种情况下可以提高效率。
直接缓冲区
直接缓冲区,开辟在物理内存。应用程序一有数据直接面对缓冲区,物理磁盘也是直接买对缓冲区, 省去了物理磁盘、应用程序的拷贝操作。


通道(Channel)

通道(Channel),在java.nio.channels包下。Channel表示IO源与目标打开的连接。Channel类似于传统的“流”。只不过Channel本身不能直接访问,Channel只能与Buffer进行交互。

Channel的主要实现类

java.nio.channels.Channel 接口:

  • FileChannel
  • SocketChannel
  • ServerSocketChannel
  • DatagramChannel

从上面的类名我们就可以看出FileChannel主要用于本地的文件传输,SocketChannel、ServerSocketChannel配合用于网络TCP传输,DatagramChannel主要用于网络UDP传输。

获取通道的方式
  • Java 针对支持通道的类提供了 getChannel()方法
    • 本地IO:
      • FileInputStream / FileOutputStream
      • RandomAccessFile
    • 网络IO:
      • Socket
      • ServerSocket
      • DatagramSocket
  • JDK 1.7 中的 NIO.2 针对各个通道,提供了静态方法 open()
  • JDK 1.7 中的 NIO.2 的 Files 工具类的newByteChannel()
利用通道完成文件的复制(非直接缓冲区)
public void test() {
		long start = System.currentTimeMillis();
		
		FileInputStream fis = null;
		FileOutputStream fos = null;
		
		//获取通道
		FileChannel inChannel = null;
		FileChannel outChannel = null;
		
		try {
			fis = new FileInputStream("resources/1.mkv");
			fos = new FileOutputStream("resources/2.mkv");
			
			inChannel = fis.getChannel();
			outChannel = fos.getChannel();
			
			//分配指定大小的缓冲区
			ByteBuffer buf = ByteBuffer.allocate(1024);
			
			//将通道中的数据写入缓冲区中
			while(inChannel.read(buf) != -1) {
				buf.flip();//切换读取数据的模式
				outChannel.write(buf);//将缓冲区中的数据写入通道中
				buf.clear();//清空缓冲区
			}
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			if(outChannel != null){
				try {
					outChannel.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(inChannel != null){
				try {
					inChannel.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(fos != null){
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(fis != null){
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
}
利用静态方法open() ,实现文件复制(直接缓冲区)
	FileChannel inChannel = FileChannel.open(Paths.get("resources/Lambda.png"),StandardOpenOption.READ);
	FileChannel outChannel = FileChannel.open(Paths.get("resources/2.png"),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[] dst = new byte[inMappedBuf.limit()];
	inMappedBuf.get(dst);
	outMappedBuf.put(dst); 
	
	inChannel.close();
	outChannel.close();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值