diff --git a/SUMMARY.md b/SUMMARY.md index cc1ebb6..14a1089 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -355,7 +355,7 @@ * [转换数据](docs/book/Appendix-New-IO.md#数据转换) * [获取原始类型](docs/book/Appendix-New-IO.md#基本类型获取) * [视图缓冲区](docs/book/Appendix-New-IO.md#视图缓冲区) - * [使用缓冲区进行数据操作](docs/book/Appendix-New-IO.md#使用缓冲区进行数据操作) + * [使用缓冲区进行数据操作](docs/book/Appendix-New-IO.md#缓冲区数据操作) * [内存映射文件](docs/book/Appendix-New-IO.md#内存映射文件) * [文件锁定](docs/book/Appendix-New-IO.md#文件锁定) * [附录:理解equals和hashCode方法](docs/book/Appendix-Understanding-equals-and-hashCode.md) diff --git a/docs/_sidebar.md b/docs/_sidebar.md index cb9265b..fa448bd 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -334,7 +334,7 @@ * [转换数据](book/Appendix-New-IO.md#数据转换) * [获取原始类型](book/Appendix-New-IO.md#基本类型获取) * [视图缓冲区](book/Appendix-New-IO.md#视图缓冲区) - * [使用缓冲区进行数据操作](book/Appendix-New-IO.md#使用缓冲区进行数据操作) + * [使用缓冲区进行数据操作](book/Appendix-New-IO.md#缓冲区数据操作) * [内存映射文件](book/Appendix-New-IO.md#内存映射文件) * [文件锁定](book/Appendix-New-IO.md#文件锁定) * [附录:理解equals和hashCode方法](book/Appendix-Understanding-equals-and-hashCode.md) diff --git a/docs/book/Appendix-New-IO.md b/docs/book/Appendix-New-IO.md index 282b4bb..da74d45 100644 --- a/docs/book/Appendix-New-IO.md +++ b/docs/book/Appendix-New-IO.md @@ -561,24 +561,27 @@ Double Buffer 0 -> 4.8E-322, ![1554546258113](../images/1554546258113.png) -### 端 +### 字节存储次序 -不同的机器可以使用不同的字节顺序方法来存储数据。“Big endian”将最重要的字节(the most significant byte)放在最低内存地址中,而“little endian”将最重要的字节放在最高内存地址中。 +不同的机器可以使用不同的字节存储顺序(*Endians*)来存储数据。“高位优先”(*Big Endian*):将最重要的字节放在最低内存地址中,而“低位优先”(*Little Endian*):将最重要的字节放在最高内存地址中。 -当存储一个大于一个字节的量时,例如 **int**、**float** 等,您可能需要考虑字节排序。字节缓冲区以大端字节形式存储数据,通过网络发送的数据总是使用大端字节顺序。您可以使用 **order()** 和**ByteOrder** 参数来更改 **ByteBuffer** 端,可选参数只有2个:**ByteOrder.BIG_ENDIAN** 或 **ByteOrder.LITTLE_ENDIAN**。 +当存储大于单字节的数据时,如 **int**、**float** 等,我们可能需要考虑字节排序问题。**ByteBuffer** 以“高位优先”形式存储数据;通过网络发送的数据总是使用“高位优先”形式。我们可以 使用 **ByteOrder** 的 `order()` 方法和参数 **ByteOrder.BIG_ENDIAN** 或 **ByteOrder.LITTLE_ENDIAN** 来改变它的字节存储次序。 -考虑一个包含以下两个字节的 **ByteBuffer** :将数据作为一个**short** (**ByteBuffer. asshortbuffer()**) 读取,生成数字 97 (00000000 01100001)。更改为 little endian 将生成数字 24832 (01100001 00000000)。 +下例是一个包含两个字节的 **ByteBuffer** : ![1554546378822](../images/1554546378822.png) -这显示了字节顺序的变化取决于 endian 设置: + +将数据作为 **short** 型来读取(**ByteBuffer.asshortbuffer()**)),生成数字 97 (00000000 01100001)。更改为“低位优先”后 将生成数字 24832 (01100001 00000000)。 + +这显示了字节顺序的变化取决于字节存储次序设置: ```java // newio/Endians.java // (c)2017 MindView LLC: see Copyright.txt -// We make no guarantees that this code is fit for any purpose. -// Visit http://OnJava8.com for more book information. -// Endian differences and data storage +// 我们无法保证该代码是否适用于其他用途。 +// 访问 http://OnJava8.com 了解更多书本信息。 +// 不同字节存储次序的存储 import java.nio.*; import java.util.*; @@ -597,61 +600,56 @@ public class Endians { System.out.println(Arrays.toString(bb.array())); } } -/* Output: +``` + +输出结果: + +``` [0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102] [0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102] [97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0] -*/ - ``` -**ByteBuffer** 分配空间将 **charArray** 中的所有字节作为外部缓冲区保存,因此可以调用 **array()** 方法来显示底层字节。**array()** 方法是“可选的”,您只能在数组支持的缓冲区上调用它 ,否则您将得到一个 **UnsupportedOperationException**。 +**ByteBuffer** 分配空间将 **charArray** 中的所有字节作为外部缓冲区保存,因此可以调用 `array()` 方法来显示底层字节。`array()` 方法是“可选的”,你只能在数组支持的缓冲区上调用它,否则将抛出 **UnsupportedOperationException** 异常。 -**charArray** 通过 **CharBuffer** 视图插入到 **ByteBuffer **中。当显示底层字节时,默认顺序与随后的大端序相同,而小端序则交换字节。 +**charArray** 通过 **CharBuffer** 视图插入到 **ByteBuffer** 中。当显示底层字节时,默认排序与后续“高位”相同,而“地位”交换字节 -## 使用缓冲区进行数据操作 +## 缓冲区数据操作 -下图说明了 nio 类之间的关系,展示了如何移动和转换数据。例如,要将字节数组写入文件,使用ByteBuffer.wrap() 方法包装字节数组,使用 **getChannel()** 在 **FileOutputStream** 上打开通道,然后从 **ByteBuffer** 将数据写入 **FileChannel**。 +下图说明了 **nio** 类之间的关系,展示了如何移动和转换数据。例如,要将字节数组写入文件,使用 **ByteBuffer.**`wrap()` 方法包装字节数组,使用 `getChannel()` 在 **FileOutputStream** 上打开通道,然后从 **ByteBuffer** 将数据写入 **FileChannel**。 ![1554546452861](../images/1554546452861.png) -**ByteBuffer** 是将数据移入和移出通道的唯一方法,您只能创建一个独立的原始类型的缓冲区,或者使用“as”方法从ByteBuffer获得一个新缓冲区。也就是说,不能将基元类型的缓冲区转换为ByteBuffer。但您能够通过视图缓冲区将原始数据移动到 **ByteBuffer** 中或移出 **ByteBuffer**。 +**ByteBuffer** 是将数据移入和移出通道的唯一方法,我们只能创建一个独立的基本类型缓冲区,或者使用 `as` 方法从 **ByteBuffer** 获得一个新缓冲区。也就是说,不能将基本类型缓冲区转换为 **ByteBuffer**。但我们能够通过视图缓冲区将基本类型数据移动到 **ByteBuffer** 中或移出 **ByteBuffer**。 -### 缓冲区的细节 +### 缓冲区细节 -缓冲区由数据和四个索引组成,以有效地访问和操作该数据: 标记、位置、限制 和 容量(mark, position, limit and capacity)。有一些方法可以设置和重置这些索引并查询它们的值。 +缓冲区由数据和四个索引组成,以有效地访问和操作该数据:*mark*、*position*、*limit* 和 *capacity*(标记、位置、限制 和 容量)。有一些方法可以设置和重置这些索引并查询它们的值。 -**capacity()** 返回缓冲区的 capacity。 +| | | +| :----- | :----- | +| **capacity()** | 返回缓冲区的 *capacity* | +|**clear()** |清除缓冲区,将 *position* 设置为零并 设 *limit* 为 *capacity*;可调用此方法来覆盖现有缓冲区| +|**flip()** | 将 *limit* 设置为 *position*,并将 *position* 设置为 0;此方法用于准备缓冲区,以便在数据写入缓冲区后进行读取| +|**limit()** |返回 *limit* 的值| +|**limit(int limit)**| 重设 *limit*| +|**mark()** |设置 *mark* 为当前的 *position* | +|**position()** |返回 *position* | +|**position(int pos)**| 设置 *position*| +|**remaining()** |返回 *limit* 到 *position* | +|**hasRemaining()**| 如果在 *position* 与 *limit* 中间有元素,返回 `true`| -**clear()** 清除缓冲区,将 position 设置为零并 设 limit 为 capacity。您可以调用此方法来覆盖现有缓冲区。 - -**flip()** 将 limit 设置为位置,并将 position 设置为零。此方法用于准备缓冲区,以便在数据写入缓冲区后进行读取。 - -**limit()** 返回 limit 的值。 - -**limit(int lim)** 重设 limit - -**mark()** 设置 mark 为当前的 position - -**position()** 返回 position - -**position(int pos) ** 设置 position - -**remaining()** 返回 limit - position 。 - -**hasRemaining()** 如果在 position 与 limit 中间有元素,返回 **true** - -从缓冲区插入和提取数据的方法更新这些索引来反映所做的更改。这个例子使用了一个非常简单的算法(交换相邻的字符)来打乱和整理字符在CharBuffer: +从缓冲区插入和提取数据的方法通过更新索引来反映所做的更改。下例使用一种非常简单的算法(交换相邻字符)来对 **CharBuffer** 中的字符进行加扰和解扰。代码示例: ```java // newio/UsingBuffers.java // (c)2017 MindView LLC: see Copyright.txt -// We make no guarantees that this code is fit for any purpose. -// Visit http://OnJava8.com for more book information. +// 我们无法保证该代码是否适用于其他用途。 +// 访问 http://OnJava8.com 了解更多书本信息。 import java.nio.*; public class UsingBuffers { @@ -665,6 +663,7 @@ public class UsingBuffers { buffer.put(c2).put(c1); } } + public static void main(String[] args) { char[] data = "UsingBuffers".toCharArray(); ByteBuffer bb = @@ -678,51 +677,54 @@ public class UsingBuffers { System.out.println(cb.rewind()); } } -/* Output: +``` + +输出结果: + +``` UsingBuffers sUniBgfuefsr UsingBuffers -*/ ``` -虽然可以通过使用 **char** 数组调用 **wrap()** 直接生成 **CharBuffer**,但是底层的 **ByteBuffer** 将被分配,而 **CharBuffer** 将作为 **ByteBuffer** 上的视图生成。这强调了目标始终是操作 **ByteBuffer**,因为它与通道交互。 +虽然可以通过使用 **char** 数组调用 `wrap()` 直接生成 **CharBuffer**,但是底层的 **ByteBuffer** 将被分配,而 **CharBuffer** 将作为 **ByteBuffer** 上的视图生成。这强调了目标始终是操作 **ByteBuffer**,因为它与通道交互。 下面是程序在 **symmetricgrab()** 方法入口时缓冲区的样子: ![1554546627710](../images/1554546627710.png) -position 指向缓冲区中的第一个元素,capacity 和 limie 紧接在最后一个元素之后。在**symmetricgrab()** 中,while循环迭代到 position 等于 limit。当在缓冲区上调用相对位置的 get() 或 put() 函数时,缓冲区的位置会发生变化。您还可以调用绝对位置的 get() 和 put() 方法,它们包含索引参数 : get()或put()发生的位置。这些方法不修改缓冲区 position 的值。 +*position* 指向缓冲区中的第一个元素,*capacity* 和 *limit* 紧接在最后一个元素之后。在`symmetricgrab()` 中,**while** 循环迭代到 *position* 等于 *limit*。当在缓冲区上调用相对位置的 `get()` 或 `put()` 函数时,缓冲区的位置会发生变化。你可以调用绝对位置的 `get()` 和 `put()` 方法,它们包含索引参数:`get()` 或 `put()` 发生的位置。这些方法不修改缓冲区 *position* 的值。 -当控件进入while循环时,使用 **mark()** 调用设置 mark 的值。缓冲区的状态为: +当控件进入 **while** 循环时,使用 `mark()` 设置 *mark* 的值。缓冲区的状态为: ![1554546666685](../images/1554546666685.png) -两个相对 get() 调用将前两个字符的值保存在变量c1和c2中。在这两个调用之后,缓冲区看起来是这样的 : +两个相对 *get()* 调用将前两个字符的值保存在变量 `c1` 和 `c2` 中。在这两个调用之后,缓冲区看起来是这样的: ![1554546693664](../images/1554546693664.png) -为了执行交换,我们在位置0处编写c2,在位置1处编写c1。我们可以使用绝对put方法来实现这一点,或者用 reset() 方法,将 position 的值设置为 mark 。 +为了执行交换,我们在位置 0 处编写 `c2`,在位置 1 处编写 `c1`。我们可以使用绝对 `put()` 方法来实现这一点,或者用 `reset()` 方法,将 *position* 的值设置为 *mark*: ![1554546847181](../images/1554546847181.png) -两个put()方法分别编写c2和c1 : +两个 `put()` 方法分别编写 `c2` 和 `c1` : ![1554546861836](../images/1554546861836.png) -在循环的下一次迭代中,将mark设置为position的当前值 : +在下一次循环中,将 *mark* 设置为 *position* 的当前值: ![1554546881189](../images/1554546881189.png) - 该过程将继续,直到遍历整个缓冲区为止。在while循环的末尾,position位于缓冲区的末尾。如果显示缓冲区,则只显示位置和限制之间的字符。因此,要显示缓冲区的全部内容,必须使用 rewind() 将位置设置为缓冲区的开始位置。这是 rewind() 调用后 buffer 的状态(mark的值变成undefined): + 该过程将继续,直到遍历整个缓冲区为止。在 **while** 循环的末尾,*position* 位于缓冲区的末尾。如果显示缓冲区,则只显示位置和限制之间的字符。因此,要显示缓冲区的全部内容,必须使用 `rewind()` 将 *position* 设置为缓冲区的开始位置。这是 `rewind()` 调用后缓冲区的状态(*mark* 的值变成未定义): ![1554546890132](../images/1554546890132.png) -再次调用 **symmetricgrab()** 函数时,**CharBuffer** 将经历相同的过程并恢复到原始状态。 +再次调用 `symmetricgrab()` 方法时,**CharBuffer** 将经历相同的过程并恢复到原始状态。 + ## 内存映射文件 - 内存映射文件允许您创建和修改太大而无法放入内存的文件。使用内存映射文件,您可以假装整个文件都在内存中,并将其视为一个非常大的数组来访问它。这种方法大大简化了您编写的修改文件的代码: ```java @@ -1046,5 +1048,4 @@ Released: 0 to 50331647 1. 您可以在附录:低级并发中找到关于线程的更多细节。 ======= -