mirror of
https://github.com/LingCoder/OnJava8.git
synced 2026-08-23 22:43:28 +08:00
校订 附录新IO 完成80%
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -561,24 +561,27 @@ Double Buffer 0 -> 4.8E-322,
|
||||

|
||||
|
||||
<!-- Endians -->
|
||||
### 端
|
||||
### 字节存储次序
|
||||
|
||||
不同的机器可以使用不同的字节顺序方法来存储数据。“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** :
|
||||
|
||||

|
||||
|
||||
这显示了字节顺序的变化取决于 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** 中。当显示底层字节时,默认排序与后续“高位”相同,而“地位”交换字节
|
||||
|
||||
<!-- Data Manipulation with Buffers -->
|
||||
## 使用缓冲区进行数据操作
|
||||
## 缓冲区数据操作
|
||||
|
||||
|
||||
下图说明了 nio 类之间的关系,展示了如何移动和转换数据。例如,要将字节数组写入文件,使用ByteBuffer.wrap() 方法包装字节数组,使用 **getChannel()** 在 **FileOutputStream** 上打开通道,然后从 **ByteBuffer** 将数据写入 **FileChannel**。
|
||||
下图说明了 **nio** 类之间的关系,展示了如何移动和转换数据。例如,要将字节数组写入文件,使用 **ByteBuffer.**`wrap()` 方法包装字节数组,使用 `getChannel()` 在 **FileOutputStream** 上打开通道,然后从 **ByteBuffer** 将数据写入 **FileChannel**。
|
||||
|
||||

|
||||
|
||||
**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()** 方法入口时缓冲区的样子:
|
||||
|
||||

|
||||
|
||||
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* 的值。缓冲区的状态为:
|
||||
|
||||

|
||||
|
||||
两个相对 get() 调用将前两个字符的值保存在变量c1和c2中。在这两个调用之后,缓冲区看起来是这样的 :
|
||||
两个相对 *get()* 调用将前两个字符的值保存在变量 `c1` 和 `c2` 中。在这两个调用之后,缓冲区看起来是这样的:
|
||||
|
||||

|
||||
|
||||
为了执行交换,我们在位置0处编写c2,在位置1处编写c1。我们可以使用绝对put方法来实现这一点,或者用 reset() 方法,将 position 的值设置为 mark 。
|
||||
为了执行交换,我们在位置 0 处编写 `c2`,在位置 1 处编写 `c1`。我们可以使用绝对 `put()` 方法来实现这一点,或者用 `reset()` 方法,将 *position* 的值设置为 *mark*:
|
||||
|
||||

|
||||
|
||||
两个put()方法分别编写c2和c1 :
|
||||
两个 `put()` 方法分别编写 `c2` 和 `c1` :
|
||||
|
||||

|
||||
|
||||
在循环的下一次迭代中,将mark设置为position的当前值 :
|
||||
在下一次循环中,将 *mark* 设置为 *position* 的当前值:
|
||||
|
||||

|
||||
|
||||
该过程将继续,直到遍历整个缓冲区为止。在while循环的末尾,position位于缓冲区的末尾。如果显示缓冲区,则只显示位置和限制之间的字符。因此,要显示缓冲区的全部内容,必须使用 rewind() 将位置设置为缓冲区的开始位置。这是 rewind() 调用后 buffer 的状态(mark的值变成undefined):
|
||||
该过程将继续,直到遍历整个缓冲区为止。在 **while** 循环的末尾,*position* 位于缓冲区的末尾。如果显示缓冲区,则只显示位置和限制之间的字符。因此,要显示缓冲区的全部内容,必须使用 `rewind()` 将 *position* 设置为缓冲区的开始位置。这是 `rewind()` 调用后缓冲区的状态(*mark* 的值变成未定义):
|
||||
|
||||

|
||||
|
||||
再次调用 **symmetricgrab()** 函数时,**CharBuffer** 将经历相同的过程并恢复到原始状态。
|
||||
再次调用 `symmetricgrab()` 方法时,**CharBuffer** 将经历相同的过程并恢复到原始状态。
|
||||
|
||||
|
||||
<!-- Memory-Mapped Files -->
|
||||
## 内存映射文件
|
||||
|
||||
|
||||
内存映射文件允许您创建和修改太大而无法放入内存的文件。使用内存映射文件,您可以假装整个文件都在内存中,并将其视为一个非常大的数组来访问它。这种方法大大简化了您编写的修改文件的代码:
|
||||
|
||||
```java
|
||||
@@ -1046,5 +1048,4 @@ Released: 0 to 50331647
|
||||
1. 您可以在附录:低级并发中找到关于线程的更多细节。
|
||||
=======
|
||||
<!-- 分页 -->
|
||||
|
||||
<div style="page-break-after: always;"></div>
|
||||
|
||||
Reference in New Issue
Block a user