Java 文件操作与基础:流对象、数字码表及缓冲区
Java 文件操作涉及数字编码与缓冲区机制。文章讲解输入输出流的概念,区分字节流与字符流及其读写方法。涵盖 FileInputStream/FileOutputStream 和 FileReader/FileWriter 的使用,强调编码转换与资源泄露风险。提供 try-with-resources 等关闭文件的规范写法,确保资源正确释放。

Java 文件操作涉及数字编码与缓冲区机制。文章讲解输入输出流的概念,区分字节流与字符流及其读写方法。涵盖 FileInputStream/FileOutputStream 和 FileReader/FileWriter 的使用,强调编码转换与资源泄露风险。提供 try-with-resources 等关闭文件的规范写法,确保资源正确释放。

站以 cpu/内存视角:
| 输入 | 硬盘 输入 内容 来 cpu/内存 |
|---|---|
| 输出 | cpu/内存 输出 内容 去 硬盘 写入 |
| 形式 | 前缀 | 含数 |
|---|---|---|
| 二进制 | 0b | 0 ~ 1 |
| 十进制 | 无 | 0 ~ 9 |
| 八进制 | 0o 或 0 | 0 ~ 7 |
| 十六进制 | 0x | 0 ~ f(大小写等价) |
| 数字类型 | 关键字 | 内存占用 | 范围 |
|---|---|---|---|
| 字节型 | 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 |
Java 的数字类型不再割分有无符号的子类型,数字类型全部都是无符号的。
数字类型有无符号的差别在它们的二进制形式中体现出来:
有符号数的首位是符号位,byte 的有符号类型的 8 位的二进制形式 - 1 个 bit 符号位、7 个 bit 数值位:
-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 个数
无符号数的全位是数值位,byte 的无符号类型的 8 位的二进制形式 - 8 个 bit 数值位:
可以表示十进制下 0~255 的 256 个数:零 1 个 + 正数 256 个 = 1 + 2^8-1 = 2^8 = 256 个数
数字大小是针对数值位进行计算的:
无符号数 10000000 = 2^(位数 - 1) = 2^(8 - 1) = 128
无符号数 11111111 = 100000000 - 1 = 2^(位数+1 -1) - 1 = 2^位数 - 1 = 2^8 - 1 = 255
码表由 数字互键字符 的两个字段组成:
| 数字 | 字符 |
|---|---|
| 同一个数的二进制 01 形式是计算机读写计量的操作实体 | 常在表间关联 |
缓冲区是 内存里的 临时存储区域,本质是 用空间换时间
缓冲区 攒存 开一次内存<->外设 通道 去交互的 更多数据:
减少了内外交互数据去开通道的总次 带来的 外设响应的时间开销、系统调用的 cpu 内核 - 用户态的切换开销
内存中 手动创建 缓冲区变量 攒存交互
传流对象参 向下构造创建 扩展的自带缓冲区流对象
BufferedInputStream bufferedStream = new BufferedInputStream(stream);
创建流对象 打开文件读写操作 流储在文件描述符表的内容流
文件内容 以字节单位地流;不参照码表:直接读写 二进制字节
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);
文件内容 以字符单位地流;参照码表:读 字符的字节 -> 再取 字节的字符、写 字符的字节 -> 再入 字节的字符
码表的参照
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[]。
stream.close();
调用流对象 关闭文件回收销毁 存储内容流的文件描述符表
打开文件 使用资源结束 后,不关闭回收 文件 就在泄露文件资源
文件资源 无用地占据着在外:
Java 的内存不需要手动回收,JVM 封装好了自动管理内存回收的 GC,不会发生内存泄露的情况
文件有去打开就必执关闭 的 捆绑执行 写法:
InputStream inputStream = null;
try {
inputStream = new FileInputStream("./test.txt");
throw new IOException();
return;
} finally {
if (inputStream != null) {
inputStream.close();
}
}
try (InputStream inputStream = new FileInputStream("./test.txt")) {
throw new IOException();
return;
}
try() 里面创建 实现 Closeable 接口 的 流对象的实例,出代码块{} 后 JVM 会调用 Closeable 接口变量的 close 方法 转型执行关闭

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online