三十六、File 类与 IO 流基础------文件操作的「第一步」📁
😫 痛点引入 :Java 程序的数据都在内存里,一关机就没了?想操作磁盘上的文件却不知道从何下手?
别慌!今天带你从 File 类 开始,搞定文件路径、创建删除、判断获取,再入门 IO 字节流,让数据在内存和磁盘之间跑起来!🏃
一、File 类------文件和目录的「地图」🗺️
1.1 File 是什么
File 是文件和目录路径名的表示,在 java.io 包里。
关键理解 :File 封装的不是真正存在的文件,只是一个路径。你可以把它理解为「地址牌」------写了个地址,不代表那栋楼已经建好了 🏗️
1.2 路径的两种写法
| 路径类型 | 说明 | 示例 |
|---|---|---|
| 绝对路径 📍 | 从盘符开始的完整路径 | D:\Java\JDK\bin |
| 相对路径 📌 | 相对于项目根目录的简化路径 | a/b/c.txt |
⚠️ 注意 :IDEA 中默认的相对路径是当前项目的根目录。
1.3 路径分隔符
Windows 路径中的 \ 是转义字符,表示一个确切的 \ 需要写 \\:
java
// 方式1:双反斜杠(Windows 风格)
File f1 = new File("D:\\Java\\JDK\\bin");
// 方式2:正斜杠(推荐,跨平台通用)
File f2 = new File("D:/Java/JDK/bin");
二、File 构造方法 🏗️
2.1 四种构造方式
| 构造方法 | 说明 |
|---|---|
File(String path) |
将路径字符串封装为 File 对象 |
File(String parent, String child) |
将两个路径拼接后封装 |
File(File parent, String child) |
将 File 对象和路径拼接 |
File(URI uri) |
将 URI 封装为 File 对象 |
2.2 代码示例
java
import java.io.File;
public class Demo01 {
public static void main(String[] args) {
// 方式1:直接传路径字符串
File f1 = new File("D:/Java/JDK/bin");
System.out.println("方式1:" + f1);
// 方式2:拼接两个路径字符串
File f2 = new File("D:/Java", "JDK/bin");
System.out.println("方式2:" + f2);
// 方式3:File对象 + 子路径
File f3 = new File(f1, "java");
System.out.println("方式3:" + f3);
}
}
三、File 创建与删除 ✂️
3.1 方法速查表
| 方法 | 功能 | 返回值 |
|---|---|---|
createNewFile() |
创建文件 📄 | boolean,成功 true |
mkdir() |
创建单级文件夹 📁 | boolean,成功 true |
mkdirs() |
创建多级文件夹 📂 | boolean,成功 true |
delete() |
删除文件或空文件夹 🗑️ | boolean,成功 true |
3.2 ⚠️ 注意事项
mkdir()只能创建最后一级文件夹,父级路径必须存在mkdirs()可以创建多级文件夹,父级不存在会自动创建delete()不走回收站!删了就没了!delete()只能删除空文件夹(里面不能有任何内容)
3.3 代码示例
java
import java.io.File;
import java.io.IOException;
public class Demo02 {
public static void main(String[] args) throws IOException {
// 1. 创建文件(绝对路径)
File f1 = new File("D:/1.txt");
System.out.println("创建文件:" + f1.createNewFile()); // true
// 再次创建,文件已存在,返回false
System.out.println("再次创建:" + f1.createNewFile()); // false
// 2. 创建单级文件夹(相对路径,相对于项目根目录)
File f2 = new File("a");
System.out.println("创建单级文件夹:" + f2.mkdir()); // true
// 3. 创建多级文件夹
File f3 = new File("a/b/c/d");
System.out.println("创建多级文件夹:" + f3.mkdirs()); // true
// 4. 删除文件(不走回收站!⚠️)
File f4 = new File("D:/1.txt");
System.out.println("删除文件:" + f4.delete()); // true
// 5. 删除文件夹(只能删空文件夹!)
File f5 = new File("a/b/c/d");
System.out.println("删除空文件夹:" + f5.delete()); // true
}
}
四、File 判断与获取 🔍
4.1 判断方法
| 方法 | 功能 |
|---|---|
exists() |
路径是否存在 |
isFile() |
是否是文件 |
isDirectory() |
是否是文件夹 |
4.2 获取方法
| 方法 | 功能 |
|---|---|
getAbsolutePath() |
获取绝对路径 |
getPath() |
获取封装的路径 |
getName() |
获取文件/文件夹名称(最后一级) |
length() |
获取文件大小(字节数) |
list() |
获取文件夹下所有子项名称 → String[] |
listFiles() |
获取文件夹下所有子项 File 对象 → File[] |
4.3 代码示例
java
import java.io.File;
public class Demo03 {
public static void main(String[] args) {
File f = new File("a");
// 判断功能
System.out.println("路径是否存在:" + f.exists());
System.out.println("是否是文件:" + f.isFile());
System.out.println("是否是文件夹:" + f.isDirectory());
// 获取功能
System.out.println("绝对路径:" + f.getAbsolutePath());
System.out.println("封装路径:" + f.getPath());
System.out.println("名称:" + f.getName());
// 获取文件夹下的子项
if (f.isDirectory()) {
System.out.println("--- list() 获取名称 ---");
String[] names = f.list();
for (String name : names) {
System.out.println(name);
}
System.out.println("--- listFiles() 获取File对象 ---");
File[] files = f.listFiles();
for (File file : files) {
System.out.println(file.getAbsolutePath());
}
}
}
}
五、实战:筛选文件夹中的文件 💪
需求 :获取文件夹内以 .txt 为后缀且大小大于 1KB 的文件的绝对路径。
java
import java.io.File;
import java.util.Scanner;
public class Demo04 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入文件夹路径:");
File f = new File(sc.next());
// 第一步:判断文件夹是否存在
if (!f.exists()) {
System.out.println("文件夹不存在!");
return;
}
// 第二步:获取文件夹下所有文件对象
File[] files = f.listFiles();
if (files == null) {
System.out.println("文件夹为空!");
return;
}
// 第三步:筛选 .txt 且 > 1KB 的文件
int count = 0;
for (File file : files) {
if (file.getName().endsWith(".txt") && file.length() > 1024) {
System.out.println("找到文件:" + file.getAbsolutePath());
count++;
}
}
System.out.println("共找到 " + count + " 个符合条件的文件");
}
}
六、IO 流入门------数据传输的「管道」🚰
6.1 什么是 IO 流
约翰·冯·诺依曼 说过:永远站在内存的角度看待问题!
-
输入流(Input):从外部往内存里进 → 读数据 📥
-
输出流(Output):从内存往外出 → 写数据 📤
磁盘文件 ──输入流──→ 内存(Java程序)──输出流──→ 磁盘文件
(读数据) (写数据)
6.2 IO 流的分类
| 分类维度 | 类型 | 说明 |
|---|---|---|
| 按方向 | 输入流 / 输出流 | 读 / 写 |
| 按数据 | 字节流 / 字符流 | 字节(万能)/ 字符(文本) |
6.3 字节流 vs 字符流
| 对比项 | 字节流 | 字符流 |
|---|---|---|
| 操作单位 | 字节(byte) | 字符(char) |
| 后缀标识 | Stream |
Reader / Writer |
| 适用场景 | 所有文件(万能)✅ | 纯文本文件 |
| 中文处理 | 需要编码表 | 自动处理 ✅ |
| 推荐场景 | 不确定文件类型时优先用 | 操作文本时优先用 |
七、字节输入流 FileInputStream 📥
7.1 核心概念
FileInputStream 是 InputStream 的子类,用于从磁盘文件读取字节数据。
特点:
- 可以读取文本、视频、音频、图片等所有类型的文件
- 计算机中一切数据都以字节为单位存储
7.2 构造方法
| 构造方法 | 说明 |
|---|---|
FileInputStream(File f) |
将 File 对象封装为字节输入流 |
FileInputStream(String path) |
将路径字符串封装为字节输入流 |
⚠️ 注意 :只能封装文件路径,封装文件夹没有意义(文件夹本身没数据)。
7.3 读取方法
| 方法 | 功能 | 返回值 |
|---|---|---|
read() |
读取一个字节 | int,读到末尾返回 -1 |
read(byte[] arr) |
读取数组大小的字节 | int,本次读取的字节个数 |
close() |
关闭流 | void |
7.4 代码示例
方式一:单字节读取
java
import java.io.FileInputStream;
import java.io.IOException;
public class Demo05 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("1.txt");
int i; // 记录读取到的字节信息
while ((i = fis.read()) != -1) {
System.out.print((char) i); // 转为字符打印
}
fis.close(); // 关闭流!一定要关!
}
}
方式二:字节数组读取(推荐 ⭐效率更高)
java
import java.io.FileInputStream;
import java.io.IOException;
public class Demo06 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("1.txt");
byte[] b = new byte[4]; // 缓冲数组
int len; // 记录本次读取的字节个数
while ((len = fis.read(b)) != -1) {
// ⚠️ 一定要用 len 指定转换个数!
// 最后一次读取可能不够数组大小,不指定会有残留数据
System.out.print(new String(b, 0, len));
}
fis.close();
}
}
💡 为什么一定要 new String(b, 0, len) 而不是 new String(b)?
假设文件内容:abcde(5个字节)
数组大小:4
第一次读取:[a, b, c, d] len=4 → new String(b,0,4) = "abcd" ✅
第二次读取:[e, b, c, d] len=1 → new String(b,0,1) = "e" ✅
new String(b) = "ebcd" ❌ 残留数据!
八、字节输出流 FileOutputStream 📤
8.1 构造方法
| 构造方法 | 说明 |
|---|---|
FileOutputStream(File f) |
封装为字节输出流(覆盖模式) |
FileOutputStream(String path) |
封装为字节输出流(覆盖模式) |
FileOutputStream(String path, boolean append) |
append=true 追加模式 🔗 |
覆盖 vs 追加:
append=false(默认):每次写入相当于新建文件,原内容没了 ❌append=true:在原文件内容后面拼接 ✅
8.2 写出方法
| 方法 | 功能 |
|---|---|
write(int b) |
写出一个字节 |
write(byte[] b) |
写出字节数组 |
write(byte[] b, int off, int len) |
写出数组的一部分 |
8.3 代码示例
java
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo07 {
public static void main(String[] args) throws IOException {
// 覆盖模式(默认)
FileOutputStream fos = new FileOutputStream("2.txt");
// 写出一个字节
fos.write('a'); // 写出字符a
fos.write(98); // 写出数字98(对应字符b)
// 写出字节数组
byte[] b = {65, 66, 67, 68};
fos.write(b); // 写出ABCD
// 写出数组的一部分
fos.write(b, 1, 2); // 写出BC
// 写出字符串(先转为字节数组)
fos.write("书源最帅".getBytes());
fos.close();
}
}
追加模式示例:
java
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo08 {
public static void main(String[] args) throws IOException {
// 第二个参数为true → 追加模式,在原文件末尾拼接
FileOutputStream fos = new FileOutputStream("2.txt", true);
fos.write("这是追加的内容".getBytes());
fos.close();
}
}
九、文件复制------读写结合 📋
核心思路:读取源文件 → 写出到目标文件
java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo09 {
public static void main(String[] args) throws IOException {
// 源文件(读)
FileInputStream fis = new FileInputStream("1.txt");
// 目标文件(写)
FileOutputStream fos = new FileOutputStream("3.txt");
int i;
while ((i = fis.read()) != -1) {
fos.write(i); // 读取一个字节,写出一个字节
}
// 先关输出流,再关输入流
fos.close();
fis.close();
}
}
十、高效字节缓冲流 ⚡
10.1 为什么需要缓冲流?
普通字节流每次读写一个字节,都要访问磁盘,效率极低 🐌
缓冲流底层有一个 8192 字节(8KB) 的缓冲数组,一次性读写一大批数据,减少磁盘交互次数,效率飞升 🚀
普通流: 磁盘 → 1字节 → 内存 → 1字节 → 磁盘 (频繁访问磁盘)
缓冲流: 磁盘 → 8192字节 → 缓冲区 → 取1个 → 内存 (批量读取)
10.2 构造方法
| 构造方法 | 说明 |
|---|---|
BufferedInputStream(InputStream is) |
包装字节输入流 |
BufferedOutputStream(OutputStream os) |
包装字节输出流 |
10.3 代码示例
java
import java.io.*;
public class Demo10 {
public static void main(String[] args) throws IOException {
// 包装普通流,提升效率
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("1.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("4.txt"));
int i;
while ((i = bis.read()) != -1) {
bos.write(i);
}
bos.close(); // close() 会自动调用 flush()
bis.close();
}
}
10.4 ⚠️ 缓冲输出流的注意点
java
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("3.txt"));
bos.write(97); // 数据还在缓冲区,没有写到磁盘!
// 方式1:手动刷新
bos.flush(); // 强制将缓冲区内容写到磁盘
// 方式2:关闭流时自动刷新
bos.close(); // close() 内部会自动调用 flush()
10.5 性能对比(拷贝视频文件)
java
import java.io.*;
public class Demo11 {
public static void main(String[] args) throws IOException {
test_缓冲流拷贝();
test_普通流拷贝();
}
// 缓冲流拷贝
private static void test_缓冲流拷贝() throws IOException {
long start = System.currentTimeMillis();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("1.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("3.mp4"));
int i;
while ((i = bis.read()) != -1) {
bos.write(i);
}
bos.close();
bis.close();
long end = System.currentTimeMillis();
System.out.println("缓冲流拷贝耗时:" + (end - start) + "ms"); // 几十ms
}
// 普通流拷贝
private static void test_普通流拷贝() throws IOException {
long start = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("1.mp4");
FileOutputStream fos = new FileOutputStream("2.mp4");
int i;
while ((i = fis.read()) != -1) {
fos.write(i);
}
fos.close();
fis.close();
long end = System.currentTimeMillis();
System.out.println("普通流拷贝耗时:" + (end - start) + "ms"); // 几千ms
}
}
预期结果 :缓冲流比普通流快 几十倍!⚡
十一、IO 异常处理------流一定要关!🔒
11.1 传统方式(Java 6 及以前)
java
import java.io.*;
public class Demo12 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("1.txt");
int i;
while ((i = fis.read()) != -1) {
System.out.print((char) i);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// finally 中关闭流,确保一定会执行
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
11.2 try-with-resources(Java 7+ ⭐推荐)
java
import java.io.*;
public class Demo13 {
public static void main(String[] args) {
// 在 try() 中创建流对象,使用完自动关闭!
try (
FileInputStream fis = new FileInputStream("1.txt");
FileOutputStream fos = new FileOutputStream("5.txt");
) {
int i;
while ((i = fis.read()) != -1) {
fos.write(i);
}
} catch (IOException e) {
e.printStackTrace();
}
// 不需要手动 close(),自动关闭!✅
}
}
11.3 两种方式对比
| 方式 | 代码量 | 是否自动关闭 | 推荐度 |
|---|---|---|---|
| try-finally | 多(嵌套 try) | 手动关闭 | ⭐⭐ |
| try-with-resources | 少 | 自动关闭 ✅ | ⭐⭐⭐⭐⭐ |
本篇总结 📝
- File 类 🗺️:封装路径,创建/删除文件和文件夹
- 路径 📍:绝对路径(从盘符开始)、相对路径(相对项目根目录)
- IO 流 🚰:输入流(读)、输出流(写),永远站在内存角度
- 字节流 📦:FileInputStream/FileOutputStream,万能流,一切皆字节
- 缓冲流 ⚡:8KB 缓冲数组,减少磁盘交互,效率翻倍
- 文件复制 📋:读一个写一个,先关输出再关输入
- 异常处理 🔒:try-with-resources 自动关闭流(Java 7+)
🚀 下一篇预告 :《三十七、字符流与转换流------中文处理的「救星」》------ 用来搞定字符流读写、编码表、转换流,再也不怕中文乱码!
作者 :书源丶
发布平台 :CSDN
系列:「Java基础篇」