NIO——笔记三

一、复制缓冲区

可以创建从外部存储到数组中元素的缓冲对象,缓冲区不限于管理数组中的外部数据,它们页能管理其它缓冲区中的外部数据,当一个管理其他缓冲器所包含的数据元素的缓冲器被创建时,这个缓冲器被称之为视图缓冲器。大多数的视图缓冲器都是 ByteBuffer 的视图。

视图存储器总是通过调用已存在的存储器实例中的函数来创建。使用已存在的存储器实例中的工厂方法意味着视图对象为原始存储器的内部实现细节私有。数据元素可以直接存取,无论它们是存储在数组中还是以一些其他的方式,而不需经过原始缓冲区对象的 get()/put() API,如果原始缓冲区是直接缓冲区,该缓冲区的视图会具有同样的效率优势,映像缓冲区也是如此:

Duplicate() 方法创建了一个与原始缓冲区相似的新缓冲区,两个缓冲区共享数据元素,拥有同样的容量,但每个缓冲区拥有各自的位置(position)、上界(limit) 和 标记属性(mark).对一个缓冲区内的数据元素所做到改变会反映在另一个缓冲区上。这一副本缓冲区具有和原始缓冲区同样的数据视图。如果原始缓冲区为只读,或者为直接缓冲区,新的缓冲区也将继承这些属性。

注意: 复制一个缓冲区会创建一个新的 Buffer 对象,但并不会复制数据。原始缓冲区和副本都会操作同样的数据元素。如图:
NIO——笔记三_第1张图片
你可以使用 asReadOnlyBuffer() 方法来生成一个只读的缓冲区视图。这与 duplicate()相同,除了这个新的缓冲区不允许使用 put(),并且其 isReadOnly() 函数将返回 true. 对这一只读缓冲区的 put() 方法的调用尝试会抛出 ReadOnlyBufferException 异常。
注意: 如果一个只读的缓冲区与一个可写的缓冲区共享数据,或者有包装好的备份数组,那么对这个可写的缓冲区或直接对这个数组的改变将反应在所有关联的缓冲区上,包括只读缓冲区。

二、字节缓冲区

NIO——笔记三_第2张图片
NIO——笔记三_第3张图片
NIO——笔记三_第4张图片
NIO——笔记三_第5张图片
在这里插入图片描述

NIO——笔记三_第6张图片
除了了 ByteBuffer ,其他通过分配或包装一个数组所创建的缓冲区将从 order() 返回与 ByteOrder.nativeOrder() 相同的数值。这是因为包含在缓冲区中的元素在 JVM 中将会被作为基本数据直接存取。

ByteBuffer 类有所不同:默认字节顺序总是 ByteBuffer.BIG_ENDIAN,无论系统的固有字节顺序是什么。 Java 的默认字节顺序是打断字节顺序,这允许类文件等以及串行化的对象可以在任何 JVM 中工作。如果固有硬件字节顺序是小端,这会有性能隐患。在使用固有硬件字节顺序时,将 ByteBuffer 的内容当作其他数据类型存取很可能高效得多。

很可能你会对为什么 ByteBuffer 类需要一个字节顺序设定感到困惑,字节就是字节,对吗?但如你所看到那样,ByteBuffer 对象像其他基本数据类型一样,具有大量便利的方法用于获取和存放缓冲区内容。这些方法对字节进行编码或解码的方式取决于 ByteBuffer 当前字节顺序的设定。

ByteBuffer 的字符顺序设定可以随时通过调用 以 ByteOrder.BIG_ENDIAN 或 ByteOrder.LITTL_ENDIAN 为参数的 order() 方法来改变。

如果一个缓冲区被创建为一个 ByteBuffer 对象的视图,那么 order() 返回的数值就是视图被创建时其创建源头的 ByteBuffer 的字节顺序设定。视图的字节顺序设定在创建后不能被改变,而且如果原始的字节缓冲区的字节顺序在之后被改变,它也不会受影响

三、直接缓冲区

字节缓冲区跟其它缓冲区类型最明显的不同在于,它们可以成为通道所执行的 I/O 的源头或目标,你会发现通道只接收 ByteBuffer 作为参数。
NIO——笔记三_第7张图片
直接缓冲区通常是 I/O 操作的最好选择。在设计方面,它们支持 JVM 可用的最高效 I/O机制。非直接字节缓冲区可以被传递给通道,但是这样可能导致性能损耗。通常非直接缓冲不可能成为本地 I/O 操作的目标。如果你向一个通道中传递一个非直接 ByteBuffer 对象用于写入,通道可能在每次调用中隐含地进行下面的操作:
① 创建一个临时的直接 ByteBuffer 对象
② 将非直接缓冲区的内容复制到临时缓冲区中
③ 使用临时缓冲区执行低层次 I/O 操作
④ 临时缓冲区对象离开作用域,并最终成为被回收的无用数据
这可能导致缓冲区在每个 I/O 上复制并产生大量对象,而这种事都是我们极力避免的。

直接缓冲区是 I/O 的最佳选择,但可能比创建非直接缓冲区要花费更高的成本。直接缓冲区使用的内存是通过调用本地操作系统方面的代码分配的,绕过了标准 JVM 堆栈。建立和销毁直接缓冲区会明显比具有堆栈的缓冲区更加破费,这取决于操作系统以及 JVM 实现。直接缓冲区的内存区域不受无用内存存储单元收集支配,因为它们位于标准 JVM 堆栈之外。

使用直接缓存区或非直接缓冲区的性能权衡会因 JVM ,操作系统,以及代码设计而产生巨大差异。通过分配堆栈外的内存,你可以使你的应用程序依赖于 JVM 未涉及的其它力量。当加入其他的移动工作时,确定你想要的效果。我以一条旧的软件行业格言建议你:先使其工作,再加快其运行。不要一开始就过多担心优化问题;首先要注重正确性。 JVM 实现可能会执行缓冲区缓存或其他的优化。这会在不需要你参与许多不必要工作的情况下为你提供所需的性能。

直接 ByteBuffer 是通过调用具有所需容量的 ByteBuffer.allocateDirect() 函数产生的,就向所涉及的 allocate() 方法一样。注意:用一个 wrap() 方法所创建的被包装的缓冲区总是非直接的。所有的缓冲区都提供了一个叫做 isDirect() 的 boolean 方法。

四、视图缓冲区

视图缓冲区通过已存在的缓冲区对象的实例工厂方法来创建。这中视图对象维护它自己的属性、容量、位置、上界和标记,但是和原理啊的换从去共享数据元素。ByteBuffer 类允许创建视图来将 byte 型缓冲区字节数据映射为其它的原始数据类型。例如,asLongBuffer() 方法创建一个将八个字节数据当成一个 long 型数据来存取的视图缓冲区。

一旦你得到了视图缓冲区,你可以用 duplicate() ,slice() 和 asReadOnlyBuffer() 方法创建进一步的子视图…
无论何时一个视图缓冲区存取一个 ByteBuffer 的基础字节,这些字节都会根据这个视图缓冲区被创建时,视图创建的同时它也继承了基础 ByteBuffer 对象的字节顺序设定。这个视图的字节排序不能再被修改。

五、内存映射缓冲区

映射缓冲区是带有存储在文件,通过内存映射来存储数据元素的字节缓冲区。映射缓冲区通常是直接存取内存的,只能通过 FileChannel 类创建。映射缓冲区的用法和直接缓冲区类似,但 MappedByteBuffer 对象可以处理独立于文件存取形式的许多特定字符。

总结:

① 所有缓冲区的共有属性描述了缓冲区的当前状态,影响了缓冲区的表现
② 缓冲区有很多类型,创建缓冲区的方式由缓冲区的使用方式和使用地点决定
③ 除了布尔类型的原始数据,虽然缓冲区能够创建缓冲,字节缓冲区却具有其它缓冲区类型没有的特征。只有字节缓冲区能够与通道共同使用,并且字节缓冲区提供了其它数据类型的视图

你可能感兴趣的:(Java基础学习,java)