第二课 缓冲区

1 缓冲区(Buffer)

1.1 缓冲区基本类型

  • 在Java NIO中,负责数据的存取。缓冲区底层实现使用数组,用于存储不同类型的数据。根据数据类型不同(boolean除外),提供了相应类型的缓冲区。
    • ByteBuffer
    • CharBuffer
    • ShortBuffer
    • IntBuffer
    • LongBuffer
    • FloatBuffer
    • DoubleBuffer
  • 上述缓冲区的管理方式几乎一致,通过allocate()获取缓冲区

1.2 缓冲区存取数据的两个核心方法

  • put():存入数据到缓冲区
  • get():获取缓冲区中的数据

1.3 缓冲区中的四个核心属性

  • capacity:容量,表示缓冲区中最大存储数据的容量。一旦声明不能改变
  • limit:界限,表示缓冲区中可以操作数据的大小(limit后数据不能进行读写)
  • position:位置,表示缓冲区中正在操作数据的位置
  • mark:标记,表示记录当前position的值,可以通过reset()方法,将position的值恢复到mark

三者的大小关系:0 <= mark <= position<=limit<=capacity

缓冲区的基本属性

1.4 Buffer的常用方法

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

import java.nio.ByteBuffer;

public class TestBuffer {

	// 1.分配一个缓冲区
	private static ByteBuffer buffer;

	public static void main(String[] args) {
		test03();
	}

	/*
	 * 基本操作
	 */
	public static void test01() {
		// 运行结果
		// -----------put()-----------
		// position: 5
		// limit: 1024
		// capacity: 1024
		// -----------flip()-----------
		// position: 0
		// limit: 5
		// capacity: 1024
		// -----------get()_前-----------
		// position: 0
		// limit: 5
		// capacity: 1024
		// abcde
		// -----------get()_后-----------
		// position: 5
		// limit: 5
		// capacity: 1024
		// -----------rewind()-----------
		// position: 0
		// limit: 5
		// capacity: 1024
		// -----------clear()-----------
		// position: 0
		// limit: 1024
		// capacity: 1024

		buffer = ByteBuffer.allocate(1024);

		// 向缓冲区写入数据
		String str = "abcde";
		buffer.put(str.getBytes());
		showBufferInfo("put()");

		// 从写入缓冲区模式转换为读取缓冲区数据模式
		buffer.flip();
		showBufferInfo("flip()");

		// 从缓冲区中读取数据
		byte[] dst = new byte[buffer.limit()];
		showBufferInfo("get()_前");
		buffer.get(dst);
		System.out.println(new String(dst));
		showBufferInfo("get()_后");

		// 重新读取缓冲区中的数据
		buffer.rewind();
		showBufferInfo("rewind()");

		// 清空缓冲区:但是缓冲区中的数据依然存在,但是是被遗忘状态
		buffer.clear();
		showBufferInfo("clear()");
	}

	/*
	 * 标志(mark)方法的使用
	 */
	public static void test02() {
		// 运行结果
		// -----------mark()-----------
		// position: 2
		// limit: 5
		// capacity: 1024
		// ab
		// -----------mark()_再次读取两个字节后-----------
		// position: 4
		// limit: 5
		// capacity: 1024
		// -----------reset()_回退后-----------
		// position: 2
		// limit: 5
		// capacity: 1024

		// 1 创建缓冲区
		buffer = ByteBuffer.allocate(1024);
		String str = "abcde";

		// 2 写入缓冲区数据
		buffer.put(str.getBytes());

		// 3 转换读写模式
		buffer.flip();

		// 4 获取数据
		byte[] dst = new byte[buffer.limit()];
		buffer.get(dst, 0, 2);

		// 5 在position为2的时候设置断点
		buffer.mark();
		showBufferInfo("mark()");
		System.out.println(new String(dst));

		// 6 设置断点后,再次从缓冲区中读取两个字节的数据
		buffer.get(dst, 2, 2);
		System.out.println(new String(dst));
		showBufferInfo("mark()_再次读取两个字节后");

		// 7 回退到之前的2
		buffer.reset();
		showBufferInfo("reset()_回退后");
	}

	/*
	 * 其他方法
	 */
	public static void test03() {
		buffer = ByteBuffer.allocate(1024);
		String str = "abcde";
		buffer.put(str.getBytes());
		if (buffer.hasRemaining()) { // 缓冲区中剩余可使用空间
			System.out.println(buffer.remaining()); // 打印可用空间数
		}
	}

	public static void showBufferInfo(String methodName) {
		System.out.println("-----------" + methodName + "-----------");
		// 当前缓冲区可操作的位置
		System.out.println("position: " + buffer.position());
		// 当前缓冲区界限
		System.out.println("limit: " + buffer.limit());
		// 当前缓冲区容量
		System.out.println("capacity: " + buffer.capacity());

	}
}

2 直接缓冲区与非直接缓冲区

字节缓冲区要么是直接的,要么是非直接的。如果为直接字节缓冲区,则Java虚拟机会尽最大努力直接在此缓冲区上执行本机I/O操作。也就是说,在每次调用基础操作系统的一个本机I/O操作之前(或之后),虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。
直接字节缓冲区可以通过调用此类的allocateDirect()工厂方法来创建。此方法返回的缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区。直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此,它们对应用程序的内存需求量造成的影响可能并不明显。所以,建议将直接缓冲区主要分配给那些易受基础系统的本机I/O操作影响的大型、持久的缓冲区。一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们。
直接字节缓冲区还可以通过FileChannelmap()方法将文件区域直接映射到内存中来创建。该方法返回MappedByteBuffer。Java平台的实现有助于通过JNI从本机代码创建直接字节缓冲区。如果以上这些缓冲区中的某个缓冲区实例指的是不可访问的内存区域,则试图访问该区域不会更改该缓冲区的内容,并且将会在访问期间或稍后的某个时间导致抛出不确定的异常。
字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其isDirect()方法来确定。提供此方法是为了能够在性能关键型代码中执行显式缓冲区管理。

  • 直接缓冲区:通过allocateDirect()方法进行直接缓冲区的创建,将缓冲区建立在物理内存上,提高了效率。但是不安全,对资源消耗较大,且写入物理内存映射文件后,应用程序无法再对写入的数据进行管控,何时写入物理磁盘,何时销毁,这些情况都无法确定。

直接缓冲区

  • 非直接缓冲区:通过allocate()方法分配缓冲区,将缓冲区建立在JVM内存中

非直接缓冲区

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值