校订 附录新IO 完成80%

This commit is contained in:
LingCoder
2019-04-06 20:07:45 +08:00
parent d2b56b5799
commit 0809ffaa2d
3 changed files with 56 additions and 55 deletions

View File

@@ -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)

View File

@@ -561,24 +561,27 @@ Double Buffer 0 -> 4.8E-322,
![1554546258113](../images/1554546258113.png)
<!-- 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**
![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** 中。当显示底层字节时,默认序与后续“高位”相同,而“地位”交换字节
<!-- Data Manipulation with Buffers -->
## 使用缓冲区进行数据操作
## 缓冲区数据操作
下图说明了 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** 将经历相同的过程并恢复到原始状态。
<!-- Memory-Mapped Files -->
## 内存映射文件
内存映射文件允许您创建和修改太大而无法放入内存的文件。使用内存映射文件,您可以假装整个文件都在内存中,并将其视为一个非常大的数组来访问它。这种方法大大简化了您编写的修改文件的代码:
```java
@@ -1046,5 +1048,4 @@ Released: 0 to 50331647
1. 您可以在附录:低级并发中找到关于线程的更多细节。
=======
<!-- 分页 -->
<div style="page-break-after: always;"></div>