NIO - 基础入门之通道和缓冲区

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/J080624/article/details/82901496

【1】NIO和IO

Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的、基于通道的IO操作(IO是基于流的,单向操作)。NIO将以更加高效的方式进行文件的读写操作。

NIO和IO区别

如下表所示:

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

通道和缓冲区

Java NIO系统的核心在于:通道(Channel)和缓冲区(Buffer)。

通道表示打开到IO 设备(例如:文件、套接字)的连接。若需要使用NIO 系统,需要获取用于连接IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理。

简而言之,Channel 负责传输Buffer,Buffer 负责数据存储。


【2】缓冲区

缓冲区(Buffer)是一个用于特定基本数据类型的容器。由java.nio 包定义的,所有缓冲区都是Buffer 抽象类的子类。

Java NIO中的Buffer 主要用于与NIO 通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的(可以理解为乘坐火车出差:要先到火车站-轨道-channel,然后坐上火车–Buffer,然后火车载着你–数据 到目的地)。

① 缓冲区类型

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

  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer

上述Buffer 类他们都采用相似的方法进行管理数据,只是各自管理的数据类型不同而已。都是通过如下方法获取一个Buffer 对象:

//创建一个容量为capacity 的XxxBuffer 对象
static XxxBuffer allocate(int capacity) ;

e.g :
ByteBuffer byteBuffer = ByteBuffer.allocate(1000);
CharBuffer charBuffer = CharBuffer.allocate(1000);

② 缓冲区的基本属性

Buffer 中的重要概念:

  • 容量(capacity) :表示Buffer 最大数据容量,缓冲区容量不能为负,并且创建后不能更改。

  • 限制(limit):第一个不应该读取或写入的数据的索引,即位于limit 后的数据不可读写。缓冲区的限制不能为负,并且不能大于其容量。

  • 位置(position):下一个要读取或写入的数据的索引。缓冲区的位置不能为负,并且不能大于其限制。

  • 标记(mark)与重置(reset):标记是一个索引,通过Buffer 中的mark() 方法指定Buffer 中一个特定的position,之后可以通过调用reset() 方法恢复到这个position。

标记、位置、限制、容量遵守以下不变式:0<=mark<=position<=limit<=capacity

示意图如下:

在这里插入图片描述


③ 缓冲区数据操作核心方法

Buffer 所有子类提供了两个用于数据操作的方法:get() 与put() 方法。

获取Buffer 中的数据:

  • get() :读取当前位置的单个字节
  • get(byte[] dst):批量读取多个字节到dst 中
  • get(int index):读取指定索引位置的字节(不会移动position)

放入数据到Buffer 中:

  • put(byte b):将给定单个字节写入缓冲区的当前位置
  • put(byte[] src):将src 中的字节写入缓冲区的当前位置
  • put(int index, byte b):将指定字节写入缓冲区的索引位置(不会移动position)

④ Buffer 的常用方法

如下表所示:

方法 描述
Bufferclear() 清空缓冲区并返回对缓冲区的引用
Buffer flip() 将缓冲区的界限设置为当前位置,并将当前位置充值为0
int capacity() 返回Buffer 的capacity大小
boolean hasRemaining() 判断缓冲区中是否还有元素
int limit() 返回Buffer 的界限(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

代码示例如下:

	@Test
	public void test1(){
		String str = "abcde";
		
		//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() 存入数据到缓冲区中
		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[] dst = new byte[buf.limit()];
		buf.get(dst);

		System.out.println("-----------------get()----------------");
		System.out.println(new String(dst, 0, dst.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());
		
	}

测试结果如下:

-----------------allocate()----------------
0 //position
1024 //limit
1024 //capacity
-----------------put()----------------
5
1024
1024
-----------------flip()----------------
0
5
1024
-----------------get()----------------
abcde
5
5
1024
-----------------rewind()----------------
0
5
1024
-----------------clear()----------------
0
1024
1024
a

测试mark 和 reset:

	@Test
	public void test2(){
		String str = "abcde";
		
		ByteBuffer buf = ByteBuffer.allocate(1024);
		
		buf.put(str.getBytes());
		//position:5 limit:1024 capacity:1024
		
		buf.flip();
		//position:0 limit:5 capacity:1024
		
		byte[] dst = new byte[buf.limit()];
		buf.get(dst, 0, 2);
		System.out.println(new String(dst, 0, 2));
		System.out.println(buf.position());
		//position:2 limit:5 capacity:1024


		//mark() : 标记
		buf.mark();
		//position:2 limit:5 capacity:1024
		
		buf.get(dst, 2, 2);
		System.out.println(new String(dst, 2, 2));
		System.out.println(buf.position());
		//position:4 limit:5 capacity:1024
		
		//reset() : 恢复到 mark 的位置
		buf.reset();
		System.out.println(buf.position());
		//position:2 limit:5 capacity:1024
		
		//判断缓冲区中是否还有剩余数据
		if(buf.hasRemaining()){
			//Returns the number of elements between the current position and the limit.
			//获取缓冲区中可以操作的数量
			System.out.println(buf.remaining());
		}
	}

测试结果如下:

ab
2
cd
4
2
3

⑤ 直接和非直接缓冲区

猜你喜欢

转载自blog.csdn.net/J080624/article/details/82901496
今日推荐