一、文件基础 (二): 数字&码表&缓冲区
1. 辨别"输入/输出"
站以 cpu/内存视角:
| 输入 | 硬盘 输入 内容 来 cpu/内存 |
|---|---|
| 输出 | cpu/内存 输出 内容 去 硬盘 写入 |
2. 数字
2.1 形式
| 形式 | 前缀 | 含数 |
|---|---|---|
| 二进制 | 0b | 0 ~ 1 |
| 十进制 | 无 | 0 ~ 9 |
| 八进制 | 0o 或 0 | 0 ~ 7 |
| 十六进制 | 0x | 0 ~ f(大小写等价) |
2.2 类型
| 数字类型 | 关键字 | 内存占用 | 范围 |
|---|---|---|---|
| 字节型 | byte | 1 字节 | -128 ~ 127 |
| 短整型 | short | 2 字节 | -32768 ~ 32767 |
| 整型 | int | 4 字节 | -2^31 ~ (2^31) - 1 |
| 长整型 | long | 8 字节 | -2^63 ~ (2^63) - 1 |
| 单精度浮点数 | float | 4 字节 | -3.4×10³⁸ ~ 3.4×10³⁸(有效位数 6~7 位) |
| 双精度浮点数 | double | 8 字节 | -1.8×10³⁰⁸ ~ +1.8×10³⁰⁸(有效位数 15~17 位) |
| 非数字类型 | 关键字 | 内存占用 | 范围 |
|---|---|---|---|
| 字符型 | char | 2 字节 | 0 ~ 65535 |
| 布尔型 | boolean | 没有明确规定 (1 字节) | true 和 false |
2.2.1 符号子类型
Java 的数字类型不再割分有无符号的子类型,数字类型全部都是无符号的。
数字类型有无符号的差别在它们的二进制形式中体现出来:
2.2.1.1 有符号数
有符号数的首位是符号位,byte 的有符号类型的 8 位的二进制形式 - 1 个 bit 符号位、7 个 bit 数值位:
- min: 10000000 = -128
- max: 01111111 = +127
-127 的原码:11111111, 补码:10000001
-127 - 1 = -128 的补码:10000000, 原码:10000000 -> 形式 -0 但意义值是 -128
0 的原码:00000000 -> 0 已经用 +0 表示了
可以表示十进制下 -128~127 的 256 个数:负数 128 个 + 零 1 个 + 正数 127 个 = 2^7-1+1 + 2^7-1 = 2^7 * 2 = 2^8 = 256 个数
2.2.1.2 无符号数
无符号数的全位是数值位,byte 的无符号类型的 8 位的二进制形式 - 8 个 bit 数值位:
- min: 00000000 = 0
- max: 11111111 = 255
可以表示十进制下 0~255 的 256 个数:零 1 个 + 正数 256 个 = 1 + 2^8-1 = 2^8 = 256 个数
3. 大小
数字大小是针对数值位进行计算的:
3.1: 1 零型 - 位值标准计算
无符号数 10000000 = 2^(位数 - 1) = 2^(8 - 1) = 128
3.2: 全 1 型 - 和差逆减计算
无符号数 11111111 = 100000000 - 1 = 2^(位数+1 -1) - 1 = 2^位数 - 1 = 2^8 - 1 = 255
4. 码表
码表由 数字互键字符 的两个字段组成:
| 数字 | 字符 |
|---|---|
| 同一个数的二进制 01 形式是计算机读写计量的操作实体 | 常在表间关联 |
5. 缓冲区
缓冲区是 内存里的 临时存储区域,本质是 用空间换时间
5.1 作用
缓冲区 攒存 开一次内存<->外设 通道 去交互的 更多数据:
- 写 存数据在缓冲区 每次向外通道多写
- 读 存数据在缓冲区 每次向内通道多读
减少了内外交互数据去开通道的总次 带来的 外设响应的时间开销、系统调用的 cpu 内核 - 用户态的切换开销
5.2 实现
5.2.1 内存变量
内存中 手动创建 缓冲区变量 攒存交互
5.2.2 缓冲区流对象
传流对象参 向下构造创建 扩展的自带缓冲区流对象
BufferedInputStream bufferedStream = new BufferedInputStream(stream);
二、文件操作 (二): 流对象的文件内容操作
1. 打开文件
创建流对象 打开文件读写操作 流储在文件描述符表的内容流
1.1 字节流
文件内容 以字节单位地流;不参照码表:直接读写 二进制字节
1.1.1 输入流 new FileInputStream(file)
流对象 直接读取在文件描述符表 的 文件流出 的字节流
read(): int 读取一个整字节的 8 个二进制 01 数等价成 1 个 0~256 在 int 范围的十进制数返回。等到读取完字节、读取到内容流末尾或抛出异常时结束。
read(byte[] b, int off, int len): int 读取指定个整字节的二进制 01 数放在外面的字节数组的指定部分。返回实际读整的字节个数。等到读取完字节、读取到内容流末尾或抛出异常时结束。
操作文件指针端不能自选位置
read(byte[] b): int 读取字节数组长度个整字节的二进制 01 数放在外面的全部字节数组。返回实际读整的字节个数。等到读取完字节、读取到内容流末尾或抛出异常时结束。
方法内部 可用 引用外存 或 return 返回出值
1.1.2 输出流 new FileOutputStream(file, boolean)
流对象 直接写入在文件描述符表 的 文件流入 的字节流
write(int b): void 写入整体等价用 1 个 0~256 在 int 范围的十进制数表示的一个整字节 8 个二进制 01 数。
write(byte[] b, int off, int len): void 写入字节数组的指定部分的整字节的二进制 01 数。
write(byte[] b): void 写入全部字节数组的整字节的二进制 01 数。
写文件****文件不存在 文件不存在打开会创建文件 全新写(创建文件后 清空与追加 结果都是全新写)
文件存在
2.1 清空写 文件存在打开会清空内容 空白写:
OutputStream outputStream = new FileOutputStream(file); // 默认 false
Writer writer = new FileWriter(file); // 默认 false
2.2 追加写 文件存在 打开会追加内容 往后写:
OutputStream outputStream = new FileOutputStream(file, true);
Writer writer = new FileWriter(file, true);
1.2 字符流
文件内容 以字符单位地流;参照码表:读 字符的字节 -> 再取 字节的字符、写 字符的字节 -> 再入 字节的字符
码表的参照
InputStreamReader/OutputStreamWriter 手动参照 指定编码:
try (FileInputStream fis = new FileInputStream("test.txt"); InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8)) {
// 3. 指定使用 UTF8 码表
char[] chars = new char[1024];
int len = isr.read(chars);
// 正确读字符
String content = new String(chars, 0, len);
System.out.println(content);
}
try (FileOutputStream fos = new FileOutputStream("test.txt"); OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8)) {
// 4. 指定使用 UTF8 码表
osw.write("Hello World"); // 正确写字符
}
FileReader/FileWriter 默认参照 系统编码:
// 文件 test.txt 是 UTF-8 编码,系统默认编码是 GBK 编码
try (FileReader fr = new FileReader("test.txt")) {
char[] chars = new char[1024];
int len = fr.read(chars);
// 1. 默认使用 GBK 码表读 UTF8 文件的字节、默认使用 GBK 码表取字节对应字符 -> 读乱码
String content = new String(chars, 0, len);
System.out.println(content);
}
try (FileWriter fw = new FileWriter("test.txt")) {
String content = "Hello World";
fw.write(content); // 2. 默认使用 GBK 码表写 UTF8 文件的字节、默认使用 GBK 码表入字节对应字符 -> 写乱码
System.out.println("FileWriter 写入完成(默认 GBK 编码)");
}
1.2.1 输入流 new FileReader(file)
流对象 参照码表读取在文件描述符表 的 文件流出 的字符流
read(): int 参照码表读一个字符的字节量 二进制 01 数等价成 1 个 0~256 在 int 范围的十进制数返回 —> 参照码表取数字对应的字符。等到读取完字符、读取到字节流末尾或发生 IO 错误结束。
read(char[] cbuf, int off, int len): int 参照码表读指定个字符的字节量 二进制 01 数 —> 参照码表取数字对应的字符 存出放到字符数组的指定部分。返回实际读取的字符个数。等到读取完字符、读取到字节流末尾或发生 IO 错误结束。
read(char[] cbuf): int 参照码表读字符数组长度个字符的字节量 二进制 01 数 —> 参照码表取数字对应的字符 存出放到全部字符数组。返回实际读取的字符个数。等到读取完字符、读取到字节流末尾或发生 IO 错误结束。
read(CharBuffer target): int 存放的CharBuffer 是 char[] 的封装
1.2.2 输出流 new FileWriter(file, boolean)
流对象 参照码表写入在文件描述符表 的 文件流入 的字符流
write(int b): void 写 整体等价用 1 个 0~256 在 int 范围的十进制数表示的一个整字节 8 个二进制 01 数 —> 参照码表入数字对应的字符。
write(char[] cbuf, int off, int len): void 参照码表写字符数组的指定部分 的每个字符对应数字 二进制 01 数 —> 参照码表入每个数字对应的字符。
write(char[] cbuf): void 参照码表写全部字符数组 的每个字符对应数字 二进制 01 数;参照码表入每个数字对应的字符。
write(String str, int off, int len): void 写入的String 等价于 char[]。
write(String str): void 写入的 String 等价于 char[]。
2. 关闭文件
stream.close();
调用流对象 关闭文件回收销毁 存储内容流的文件描述符表
2.1 文件泄露
打开文件 使用资源结束 后,不关闭回收 文件 就在泄露文件资源
2.1.1 危害
文件资源 无用地占据着在外:
- 泄减 了 供使用的内部资源
- 露增 了 无故修改资源的权限
Java 的内存不需要手动回收,JVM 封装好了自动管理内存回收的 GC,不会发生内存泄露的情况
2.2 绑定关闭
文件有去打开就必执关闭 的 捆绑执行 写法:
2.2.1 try{new}finally{close}
InputStream inputStream = null;
try {
inputStream = new FileInputStream("./test.txt");
throw new IOException();
return;
} finally {
if (inputStream != null) {
inputStream.close();
}
}
2.2.2 try(new){}
try (InputStream inputStream = new FileInputStream("./test.txt")) {
throw new IOException();
return;
}
try() 里面创建 实现 Closeable 接口 的 流对象的实例,出代码块{} 后 JVM 会调用 Closeable 接口变量的 close 方法 转型执行关闭


