文件 I/O(速通版
目录
文件
一般地 事物都会随着时间的流逝 慢慢消散 让人无奈
这样也让 程序猿苦恼不已 所以为了保存 自己的心血
针对硬盘这种持久化存储的I/O设备 保存数据时 独⽴成⼀个个的单位进⾏保存 而这独立的单位 就从而抽象称作 文件
文件 所包含的东西可不少
除有数据内容之外,
还有⼀部分信息 例如⽂件名、⽂件类型、⽂件⼤⼩等并不作为⽂件的数 据⽽存在,我们把这部分信息可以视为⽂件的 元信息

随着文件的增加 要对其开始管理 按照层级结构进⾏组织 也就是我们平时所谓⽂件夹(folder)或者⽬录 也是数据结构中所学习过的树形结构。
文件路径
简单地分为两种 相对路径 和 绝对路径
绝对路径 :从根目录开始定位文件,例如 /home/user/test.txt 或 C:\Users\test.txt,在任何位置都能唯一确定该文件。
相对路径 :从当前工作目录开始定位文件,例如 ./test.txt 或 ../docs/readme.md,路径含义取决于程序运行时所在的目录。
public static void main(String[] args) throws IOException {
File file = new File("./test.txt");
System.out.println(file.getParent());
System.out.println(file.getName());
System.out.println(file.getPath());
System.out.println(file.getAbsolutePath());
System.out.println(file.getCanonicalPath());
}
public static void main(String[] args) throws IOException {
File file = new File("./test.txt");
file.createNewFile();
System.out.println(file.exists());
System.out.println(file.isFile());
System.out.println(file.isDirectory());
}
各个操作系统的不同 导致文件创建目录方式都各不相同
此外一个文件的分类根据其保存数据的不同,也经常被分为不同的类型,
⼀般简单的划分为⽂本⽂件和⼆进制⽂件,
分别指代保存被字符集编码的⽂本和按照标准格式保存的⾮被字符集编码过的⽂件。

⽂件由于被操作系统进⾏了管理,所以根据不同的⽤⼾,会赋予⽤⼾不同的对待该⽂件的权限,⼀般 地可以认为有 可读、可写、可执⾏权限。
通过系统的权限 完成了对数据安全的保护作用
⽂件内容的读写
字节流
字节流以字节(8 位二进制)为单位。Java 中字节流的顶层抽象是 InputStream(输入)和 OutputStream(输出),常用实现包括 FileInputStream、FileOutputStream、BufferedInputStream 和 BufferedOutputStream。
public static void main(String[] args) throws IOException {
// 字节流写入
try (FileOutputStream fos = new FileOutputStream("test.txt")) {
fos.write("Hello".getBytes());
}
// 字节流读取
try (FileInputStream fis = new FileInputStream("test.txt")) {
int data;
while ((data = fis.read()) != -1) {
System.out.print((char) data);
}
}
}
InputStream
InputStream 是 Java 字节输入流的顶层抽象类,用于从数据源(如文件、网络、内存)读取字节数据。常见使用方法如下:
- read():每次读取一个字节,返回 0~255 的 int 值,读到末尾返回 -1。
- read(byte[] b):批量读取字节到缓冲区,返回实际读取的字节数,读到末尾返回 -1。
- read(byte[] b, int off, int len):从偏移量 off 开始,最多读取 len 个字节到缓冲区。
- available():返回当前可读取的字节数估计值。
- skip(long n):跳过并丢弃输入流中的 n 个字节。
- close():关闭输入流并释放底层系统资源,建议配合 try-with-resources 使用。
public static void main(String[] args) throws IOException {
// 方式一:单字节读取
try (InputStream in = new FileInputStream("test.txt")) {
int data;
while ((data = in.read()) != -1) {
System.out.print((char) data);
}
}
// 方式二:批量读取到缓冲区
try (InputStream in = new FileInputStream("test.txt")) {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, len));
}
}
// 方式三:跳过前 2 个字节再读取
try (InputStream in = new FileInputStream("test.txt")) {
in.skip(2);
int data;
while ((data = in.read()) != -1) {
System.out.print((char) data);
}
}
}
初始化 InputStream的过程 相当于 打开文件
FileInputStream
FileInputStream 是 InputStream 的一个常用实现类,专门用于从文件中读取字节数据。它继承自 InputStream,因此前面介绍的所有读取方法(read、available、skip、close 等)都可以直接使用。常见构造方式如下:
- new FileInputStream(String name):根据文件路径字符串创建输入流,例如
new FileInputStream("test.txt")。 - new FileInputStream(File file):根据 File 对象创建输入流,例如
new FileInputStream(new File("./test.txt"))。
将⽂件完全读完的两种⽅式。相⽐较⽽⾔,前⼀种 的IO次数更少,性能更好。
FileInputStream 的常用方法如下:
- read():从文件中读取一个字节,返回 0~255 的 int 值,读到文件末尾返回 -1。
- read(byte[] b):从文件中批量读取字节到缓冲区,返回实际读取的字节数,读到末尾返回 -1。
- read(byte[] b, int off, int len):从偏移量 off 开始,最多读取 len 个字节到缓冲区。
- available():返回当前可读取的字节数估计值。
- skip(long n):跳过并丢弃文件输入流中的 n 个字节。
- close():关闭文件输入流并释放底层系统资源,建议配合 try-with-resources 使用。
public static void main(String[] args) throws IOException {
// 方式一:根据路径字符串创建 FileInputStream
try (FileInputStream fis = new FileInputStream("test.txt")) {
int data;
while ((data = fis.read()) != -1) {
System.out.print((char) data);
}
}
// 方式二:根据 File 对象创建 FileInputStream
File file = new File("./test.txt");
try (FileInputStream fis = new FileInputStream(file)) {
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, len));
}
}
}
OutputStream
输出 向文件写出数据
OutputStream 是 Java 字节输出流的顶层抽象类,用于向数据目标(如文件、网络、内存)写出字节数据。常见使用方法如下:
- write(int b):写出一个字节,参数为 0~255 的 int 值。
- write(byte[] b):将整个字节数组写出到输出流。
- write(byte[] b, int off, int len):从偏移量 off 开始,最多写出 len 个字节。
- flush():将缓冲区中暂存的数据强制刷新到目标设备,确保数据真正落盘。
- close():关闭输出流并释放底层系统资源,建议配合 try-with-resources 使用。
public static void main(String[] args) throws IOException {
// 方式一:单字节写出
try (FileOutputStream fos = new FileOutputStream("test.txt")) {
fos.write('H');
fos.write('i');
}
// 方式二:批量写出字节数组
try (FileOutputStream fos = new FileOutputStream("test.txt")) {
byte[] data = "Hello World".getBytes();
fos.write(data);
}
// 方式三:写出字节数组的指定范围
try (FileOutputStream fos = new FileOutputStream("test.txt")) {
byte[] data = "Hello World".getBytes();
fos.write(data, 0, 5); // 只写出前 5 个字节
}
}
字符流
字符流以字符为单位读写数据,适合处理文本文件,读写时会自动完成字符集编码转换。使用 BufferedReader 的 readLine() 可以按行读取文本,配合 BufferedWriter 的 newLine() 可以按行写入。
public static void main(String[] args) throws IOException {
// 字符流写入
try (BufferedWriter bw = new BufferedWriter(new FileWriter("test.txt"))) {
bw.write("Hello");
bw.newLine();
bw.write("World");
}
// 字符流读取
try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
}
Reader 输入 往文件写入数据
Writer
OutputStreamWriter
OutputStreamWriter 是字符输出流 Writer 的常用实现类,它充当字符流到字节流的桥梁:将写入的字符按指定字符集(如 UTF-8)编码后,再交给底层的字节输出流写出。常见构造方式如下:
- new OutputStreamWriter(OutputStream out):根据字节输出流创建,使用平台默认字符集编码。
- new OutputStreamWriter(OutputStream out, String charsetName):指定字符集名称创建,例如
new OutputStreamWriter(fos, "UTF-8")。 - new OutputStreamWriter(OutputStream out, Charset charset):使用 Charset 对象指定字符集创建。
OutputStreamWriter 的常用方法如下:
- write(int c):写出单个字符。
- write(char[] cbuf):写出整个字符数组。
- write(String str):写出字符串。
- write(String str, int off, int len):写出字符串中从偏移量 off 开始、长度为 len 的子串。
- flush():将缓冲区中的字符编码后强制刷新到底层字节输出流。
- close():关闭流并释放底层系统资源,建议配合 try-with-resources 使用。
public static void main(String[] args) throws IOException {
// 方式一:使用平台默认字符集
try (OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("test.txt"))) {
osw.write("Hello 世界");
}
// 方式二:指定 UTF-8 字符集
try (OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("test.txt"), "UTF-8")) {
osw.write("Hello 世界");
osw.flush();
}
// 方式三:使用 Charset 对象
try (OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("test.txt"), StandardCharsets.UTF_8)) {
osw.write("Hello 世界", 0, 5);
}
}
PrintWriter
PrintWriter 是字符输出流 Writer 的常用实现类,它提供了格式化输出能力,可以像 System.out 一样使用 print()、println() 和 printf() 等方法写出数据,使用非常方便。常见构造方式如下:
- new PrintWriter(OutputStream out):根据字节输出流创建,例如
new PrintWriter(new FileOutputStream("test.txt"))。 - new PrintWriter(Writer out):根据字符输出流创建,例如
new PrintWriter(new FileWriter("test.txt"))。 - new PrintWriter(File file):根据 File 对象创建,例如
new PrintWriter(new File("./test.txt"))。 - new PrintWriter(String fileName):根据文件路径字符串创建,例如
new PrintWriter("test.txt")。
PrintWriter 的常用方法如下:
- print(...):写出内容但不换行,支持字符串、基本类型和对象等。
- println(...):写出内容后自动换行,是最常用的输出方式。
- printf(String format, Object... args):按指定格式写出内容,例如
printf("数值:%d", 100)。 - flush():将缓冲区中的数据强制刷新到目标设备。
- close():关闭流并释放底层系统资源,建议配合 try-with-resources 使用。
public static void main(String[] args) throws IOException {
// 方式一:基于 FileOutputStream 创建
try (PrintWriter pw = new PrintWriter(new FileOutputStream("test.txt"))) {
pw.println("Hello World");
pw.println(100);
pw.println(3.14);
}
// 方式二:基于 FileWriter 创建
try (PrintWriter pw = new PrintWriter(new FileWriter("test.txt"))) {
pw.print("Hello ");
pw.print("World");
pw.println();
}
// 方式三:格式化输出
try (PrintWriter pw = new PrintWriter("test.txt")) {
pw.printf("姓名:%s,年龄:%d", "张三", 18);
pw.flush();
}
}
输出 向文件写出数据
字节流与字符流的区别
字节流直接操作字节,不涉及编码转换,通用性强;
字符流以字符为单位读写,内部会按指定字符集(如 UTF-8)进行编码转换,更适合处理文本文件。
字符流的顶层抽象是 Reader(输入)和 Writer(输出),常用实现包括 FileReader、FileWriter、BufferedReader 和 BufferedWriter。
以读取字符串“你好”为例,两种流的处理方式有明显差异:
- 字节流读取:直接按字节读取,不关心字符集。在 UTF-8 编码下,“你好”共占 6 个字节,每次
read()只能读回 1 个字节,需要循环 6 次才能读完,且读到的只是原始字节,必须由程序自行按 UTF-8 解码才能还原成“你好”。 - 字符流读取:以字符为单位读取,内部会自动完成字符集解码。使用
BufferedReader的read()或readLine()时,每次读回的是完整的字符,例如“你”和“好”各占 1 个字符,只需读取 2 次即可读完,无需手动处理编码。
简单来说,字节流读取“你好”得到的是 6 个字节,需要自己解码;字符流读取“你好”得到的是 2 个字符,编码转换已由流内部自动完成。
I/O 流提升效率
提升 I/O 效率的核心:用缓冲流减少系统调用、用批量读写减少调用次数、显式指定字符集避免额外转换、用 try-with-resources 及时释放资源。
手动增加缓冲区 减少 对 write reader 的调用
使用标准库自带的 缓冲区流 BufferStream 对 使用的InputStream的类 包装
缓冲区是内存中临时存放数据的一块区域,用于暂存读写的数据。它先把多次小规模读写合并成一次批量操作,等缓冲区满或调用 flush() 时才真正访问底层设备,从而减少系统调用次数、提升 I/O 效率。Java 中 BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter 都基于缓冲区实现。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)