File 类与文件系统对象 | JavaSE
File 类与文件系统对象
一、学习目标
学完本章,你应该能够:
- 解释
java.io.File类到底表示什么,明确区分“File 对象”“路径”“真实文件/目录”“文件内容”。 - 理解文件系统对象的基本模型,知道文件和目录都可以由
File对象表示。 - 掌握
File的三种核心构造方式,能够根据完整路径、父路径 + 子路径创建File对象。 - 区分绝对路径和相对路径,能够解释 Java 中相对路径到底相对于哪里。
- 正确处理 Windows 与跨平台路径写法,理解
\转义、/、File.separator的区别。 - 能够建立后续学习 File API、递归、IO 流所需要的统一文件系统认知模型。
二、核心知识
2.1 从内存世界走向文件系统
在前面的 JavaSE 学习中,我们处理的数据绝大多数存在于:
变量
↓
数组
↓
集合
↓
Java 对象
例如:
List<String> names = new ArrayList<>();
names.add("张三");
names.add("李四");
这些数据主要存在于程序运行期间的内存中。
程序结束以后:
JVM 结束
↓
进程退出
↓
内存中的对象消失
于是马上出现一个新的问题:
如果我要访问电脑磁盘上的
a.txt、avatar.jpg、D:\data,Java 应该怎样描述它们?
这就是 File 类出现的第一个意义。
2.2 什么是 File 类
File 位于:
java.io.File
需要导包:
import java.io.File;
在 Java 中,File 可以用来表示:
文件路径
目录路径
例如:
D:\Java\data.txt
D:\Java\images
C:\Users\LingXi\Desktop
都可以被封装成一个 File 对象。
最基本的代码:
File file = new File("D:\\Java\\data.txt");
这里最重要的不是记住语法,而是理解:
File对象表示的是一个抽象路径名(Abstract Pathname)。
可以建立下面这个模型:
Java 程序
│
│ new File(...)
▼
File 对象
│
│ 描述一个路径
▼
D:\Java\data.txt
│
│ 可能对应
▼
真实文件系统对象
注意关键词:
可能对应。
File 对象本身并不是那个真实文件。
2.3 File 对象 ≠ 文件内容
这是学习 File 时最容易产生的第一个误区。
假设磁盘上存在:
D:\data\hello.txt
内容为:
Hello Java
我们写:
File file = new File("D:\\data\\hello.txt");
此时 file 对象里面并没有保存:
Hello Java
它主要描述的是:
D:\data\hello.txt
也就是:
文件在文件系统中的路径信息。
因此:
File
主要解决的是:
这个文件在哪里?
这个路径是什么?
这个路径是否存在?
它是文件还是目录?
叫什么名字?
多大?
什么时候修改的?
能不能创建、删除?
目录下面有什么?
而:
读取 hello.txt 里面的 Hello Java
属于后面学习的:
IO 流
职责。
所以一定要建立边界:
File
├── 描述路径
├── 获取文件系统信息
├── 创建/删除文件或目录
└── 遍历目录
IO Stream
├── 读取文件内容
└── 写入文件内容
一句话:
File 管“文件系统对象”,IO 流管“文件里面的数据”。
2.4 File 对象 ≠ 真实文件
这是第二个极其重要的认知。
执行:
File file = new File("D:\\data\\abc.txt");
并不意味着磁盘上已经创建:
abc.txt
new File(...) 做的事情,本质上只是:
路径字符串
↓
转换
↓
File 抽象路径对象
所以即使:
D:\data\abc.txt
完全不存在:
File file = new File("D:\\data\\abc.txt");
仍然可以正常创建 File 对象。
因此必须区分两个“创建”:
new File(...)
是:
创建 Java 中的 File 对象
而真正让磁盘出现文件:
创建实际文件
则需要后续 File API。
可以把它类比成:
String address = "山西省太原市某街道";
你创建了一个:
地址描述
并不意味着你刚刚在那里盖了一栋房子。
同样:
File file = new File("D:\\house.txt");
只是创建了一个:
路径描述对象
并没有凭空在 D 盘创建文件。
2.5 File 可以表示文件,也可以表示目录
虽然类名叫:
File
但它并不仅仅代表普通文件。
例如:
File file = new File("D:\\Java\\Hello.txt");
可以描述:
文件
而:
File directory = new File("D:\\Java");
可以描述:
目录 / 文件夹
因此更准确的理解应该是:
File是 Java 对文件系统路径的一种抽象表示。
一个 File 对象指向的路径:
可能是普通文件
可能是目录
可能根本不存在
仅仅看到:
File file = new File("abc");
不能判断:
abc 到底是文件还是目录
必须进一步查询文件系统。
2.6 什么是文件系统对象
操作系统通常会组织类似这样的层次结构:
D:\
├── Java
│ ├── project
│ │ ├── src
│ │ └── pom.xml
│ │
│ ├── notes
│ │ └── java.md
│ │
│ └── README.txt
│
└── images
└── avatar.png
这里:
Java
project
src
notes
images
是目录。
而:
pom.xml
java.md
README.txt
avatar.png
是普通文件。
但是从路径角度看,它们都可以表示成:
根路径
↓
目录
↓
目录
↓
文件或目录
例如:
D:\Java\project\pom.xml
可以拆成:
D:\
└── Java
└── project
└── pom.xml
File 类正是 Java 与这种文件系统层次结构之间的一座桥梁。
2.7 抽象路径是什么
JDK 对 File 的正式定位是:
文件和目录路径名的抽象表示。
这里的“抽象”非常重要。
Windows 使用:
C:\Users\LingXi\data.txt
Linux/macOS 常见:
/home/lingxi/data.txt
不同操作系统的路径规则并不完全相同。
Java 不希望我们每次操作文件时都直接处理操作系统内部实现,于是提供:
File
作为统一的 Java 对象模型。
形成:
Windows 文件系统 ─┐
│
Linux 文件系统 ─┼── File 抽象路径模型
│
macOS 文件系统 ─┘
当然:
File进行了抽象,但路径本身仍具有一定平台相关性。
例如:
D:\Java\a.txt
显然不是 Linux 通用路径。
这也是后面讨论跨平台路径时必须注意的问题。
三、使用方法
3.1 创建 File 对象
原课程重点掌握三个构造器:
File(String pathname)
File(String parent, String child)
File(File parent, String child)
它们的本质都是:
构造一个抽象路径。
3.2 构造方式一:直接传入完整路径
语法:
File file = new File(String pathname);
例如:
import java.io.File;
public class FileDemo {
public static void main(String[] args) {
File file = new File("D:\\Java\\data.txt");
System.out.println(file);
}
}
这里:
D:\Java\data.txt
作为完整路径交给 File。
适合:
已经拥有完整路径的情况。
3.3 构造方式二:父路径 + 子路径
可以写:
File file = new File(
"D:\\Java",
"data.txt"
);
这里:
父路径
D:\Java
子路径
data.txt
组合形成:
D:\Java\data.txt
即:
parent
+
child
↓
完整路径
代码:
import java.io.File;
public class FileDemo {
public static void main(String[] args) {
File file =
new File("D:\\Java", "data.txt");
System.out.println(file);
}
}
这种方式的价值在于:
不需要自己到处进行字符串路径拼接。
不要大量写:
String path = parent + "\\" + child;
因为:
路径分隔符
平台差异
重复 /
重复 \
都会增加错误概率。
3.4 构造方式三:File 父对象 + 子路径
还可以先创建父目录:
File parent = new File("D:\\Java");
然后:
File child = new File(parent, "data.txt");
完整代码:
import java.io.File;
public class FileDemo {
public static void main(String[] args) {
File parent =
new File("D:\\Java");
File child =
new File(parent, "data.txt");
System.out.println(child);
}
}
逻辑结构:
File parent
D:\Java
│
└── "data.txt"
↓
File child
D:\Java\data.txt
这种方式非常适合以后:
目录遍历
递归
文件搜索
复制文件
因为在这些场景中:
我们经常已经拥有一个表示父目录的
File对象。
然后继续构造其子文件对象。
3.5 三种构造方式怎么选择
例如目标都是:
D:\Java\data.txt
可以写:
File f1 =
new File("D:\\Java\\data.txt");
File f2 =
new File("D:\\Java", "data.txt");
File parent =
new File("D:\\Java");
File f3 =
new File(parent, "data.txt");
可以理解为:
| 构造方式 | 适合场景 |
| ----------------------------------- | ------------------------ |
| File(String pathname) | 已知完整路径 |
| File(String parent, String child) | 已知父路径字符串和子路径 |
| File(File parent, String child) | 已经拥有父目录 File 对象 |
真正需要掌握的不是三个构造器的死记硬背,而是:
完整路径
可以:
一次提供
也可以:
父路径
+
子路径
逐层构造。
3.6 什么是绝对路径
绝对路径(Absolute Path):
不依赖其他路径信息,就能够定位目标的完整路径。
Windows 例如:
D:\Java\data.txt
或者:
C:\Users\LingXi\Desktop\a.txt
Linux/macOS 例如:
/home/lingxi/data.txt
特点:
信息完整
↓
通常能够直接定位
↓
不依赖程序当前工作目录
Java 示例:
File file =
new File("D:\\Java\\data.txt");
3.7 什么是相对路径
相对路径(Relative Path)没有完整描述文件系统位置。
例如:
data.txt
或者:
data\user.txt
或者:
src\data.txt
Java 示例:
File file =
new File("data\\user.txt");
这里出现关键问题:
到底相对于哪里?
3.8 相对路径到底相对于哪里
在 java.io 中,默认情况下,相对路径会基于:
当前用户目录
进行解析。
这个目录可以通过系统属性:
user.dir
查看:
public class UserDirDemo {
public static void main(String[] args) {
System.out.println(
System.getProperty("user.dir")
);
}
}
假设输出:
D:\Code\JavaStudy
而你写:
File file =
new File("data\\user.txt");
那么它可以理解为:
D:\Code\JavaStudy
+
data\user.txt
↓
D:\Code\JavaStudy\data\user.txt
因此:
相对路径
+
当前工作目录
↓
实际定位路径
3.9 不要简单记成“相对路径就是相对于项目根目录”
初学阶段经常会听到:
相对路径默认从当前工程目录寻找。
这作为 IDEA 入门经验通常比较容易理解。
但更准确的 Java 语义应该是:
相对路径按照 JVM 当前工作目录解析,通常可以通过
user.dir查看。
在 IDEA 中:
Working Directory
可以配置。
因此不能机械认为:
永远 = Project 根目录
更可靠的方法是遇到路径问题直接打印:
System.out.println(
System.getProperty("user.dir")
);
这是以后 Debug 文件路径问题非常实用的一招。
3.10 Windows 路径为什么经常写两个反斜杠
Windows 原始路径:
D:\Java\data.txt
但是在 Java 字符串中:
\
本身是:
转义字符的开始标志。
例如:
"\n"
代表换行。
"\t"
代表制表符。
因此 Windows 路径通常需要写成:
File file =
new File("D:\\Java\\data.txt");
Java 字符串中的:
\\
最终表示一个普通:
\
于是:
Java 源代码
"D:\\Java\\data.txt"
↓
实际字符串
D:\Java\data.txt
3.11 为什么路径问题特别容易产生隐蔽 Bug
例如:
String path =
"D:\test\data.txt";
你本来以为:
\test
只是目录。
但是:
\t
在 Java 字符串中表示:
Tab
所以程序获得的字符串实际上已经不是你想象中的路径。
因此看到 Windows 路径时,必须立即想到:
\ 是 Java 转义符
正确写法:
"D:\\test\\data.txt"
3.12 Windows 下也经常使用 /
Java 在 Windows 环境下通常也能够识别:
File file =
new File("D:/Java/data.txt");
这种写法的优点是:
不需要写 \\ 转义
所以教学和一些项目配置中经常能看到:
D:/Java/data.txt
但真正进行跨平台程序设计时,不应该把:
Windows 盘符
硬编码到业务代码中。
例如:
"D:/Java/data.txt"
拿到 Linux:
没有 D 盘
自然没有意义。
3.13 File.separator
Java 还提供了:
File.separator
表示:
当前系统默认的文件名分隔符。
例如:
System.out.println(File.separator);
Windows 通常得到:
\
Unix/Linux/macOS 通常是:
/
因此可以出现:
String path =
"data"
+ File.separator
+ "user.txt";
不过实际开发时,如果只是组合父路径和子路径:
new File(parent, child)
往往比自己手动拼接:
parent
+ File.separator
+ child
更加清晰。
3.14 一个完整的 File 路径认知案例
观察:
import java.io.File;
public class FilePathDemo {
public static void main(String[] args) {
File absolute =
new File("D:\\Java\\data.txt");
File relative =
new File("data\\user.txt");
File parent =
new File("data");
File child =
new File(parent, "score.txt");
System.out.println(
"当前工作目录:" +
System.getProperty("user.dir")
);
System.out.println(
"absolute = " + absolute
);
System.out.println(
"relative = " + relative
);
System.out.println(
"child = " + child
);
}
}
这个案例需要建立四个认识:
File
不是文件内容
File
主要封装路径
路径
可以绝对,也可以相对
相对路径
必须依赖某个基准进行解析
四、原理与进阶
4.1 File 对象本质上维护的是抽象路径
可以把:
File file =
new File("data\\user.txt");
理解为:
File Object
┌───────────────────────┐
│ Abstract Pathname │
│ data\user.txt │
└───────────────────────┘
它并不是:
File Object
┌───────────────────────┐
│ user.txt 全部文件内容 │
└───────────────────────┘
因此创建 File 对象本身通常是非常轻量的行为。
真正访问文件系统发生在诸如:
查询
创建
删除
遍历
读取
写入
这些具体操作发生的时候。
4.2 File 对象可以指向不存在的路径
假设磁盘只有:
D:\Java
不存在:
D:\Java\future.txt
依然可以:
File future =
new File("D:\\Java\\future.txt");
此时可以建立:
File 对象存在
≠
磁盘目标存在
这是一个非常重要的逻辑区别。
类似:
数据库查询条件对象存在
≠
数据库中一定存在对应记录
以后你会发现:
Java 中“描述某资源的对象”和“资源本身”经常是两层东西。
4.3 File 的抽象路径具有不可变性
JDK 对 File 的设计还有一个值得知道的特性:
File实例所表示的抽象路径一旦创建就不会改变。
例如:
File file =
new File("data\\a.txt");
这个 File 对象表示:
data\a.txt
之后不会突然变成:
data\b.txt
如果你想表示另一个路径:
File another =
new File("data\\b.txt");
应该创建另一个 File 对象。
但注意:
File 对象路径不可变,不代表磁盘中的实际文件状态不会变化。
真实文件仍然可能被:
创建
删除
修改
重命名
4.4 File 与 Path / Files
Java 7 以后又提供了:
java.nio.file.Path
java.nio.file.Files
它们属于:
NIO.2
现代 Java 文件系统编程中经常优先使用:
Path
+
Files
而不是所有操作都围绕老的:
File
展开。
例如:
Path path =
Path.of("data", "user.txt");
不过本课程仍然先学习 File,原因很明确:
File是经典 JavaSE 文件系统 API;- 大量 Java 教程和既有代码仍在使用;
- 很多 IO API 可以直接接收
File; - 学懂 File 后,更容易理解
Path; - File、递归、IO 流之间具有非常直接的教学关系。
而且二者可以互相衔接,例如:
file.toPath();
本阶段不需要展开 NIO.2。
现在只需要知道:
File
经典文件路径抽象
Path / Files
现代 NIO.2 文件系统 API
即可。
五、实践应用
5.1 为什么 Web 项目也离不开文件系统认知
以后开发 Java Web 项目时,经常会出现:
头像上传
文章图片
Markdown 文件
日志文件
配置文件
Excel 导入导出
文件下载
临时文件
附件
例如“星雨笔录”中的:
教程 Markdown
博客图片
简历附件
作品附件
最终如果存储在服务器本地磁盘上:
Java 程序
↓
文件路径
↓
服务器文件系统
都必须建立在今天这套认知之上。
5.2 File 是后续 IO 流的定位信息
以后会看到:
File file =
new File("data.txt");
随后:
FileInputStream input =
new FileInputStream(file);
这里职责非常清晰:
File
↓
告诉程序“文件在哪里”
FileInputStream
↓
负责“从里面读取字节”
所以:
路径定位
和:
数据传输
是两个不同问题。
5.3 为什么先学 File 再学 IO
如果连:
文件在哪
目录是什么
路径怎么表达
绝对路径是什么
相对路径是什么
都没有理解,那么直接学习:
FileInputStream
FileOutputStream
BufferedReader
BufferedWriter
很容易陷入:
为什么程序一直提示找不到文件?
结果真正的问题不是 IO:
而是路径错了。
所以本组知识依赖是:
File
↓
目录遍历
↓
递归
↓
字符编码
↓
IO 流
这是一条非常自然的技术链路。
六、常见问题
6.1 new File() 会在磁盘创建文件吗?
不会。
例如:
new File("D:\\abc.txt");
只是创建:
File Java 对象
表示:
D:\abc.txt
这个抽象路径。
不意味着:
abc.txt
已经真实存在。
6.2 File 对象必须指向已经存在的文件吗?
不需要。
即使目标路径不存在:
File file =
new File("D:\\not-exist.txt");
File 对象仍然可以正常构造。
后续可以再判断该文件系统对象是否真正存在。
6.3 File 只能表示文件吗?
不是。
可以表示:
普通文件
目录
不存在的路径
因此不能因为类名叫:
File
就理解成:
只能代表
.txt、.jpg这样的文件。
6.4 File 对象保存文件内容吗?
不是。
例如:
File file =
new File("article.md");
不会自动将 article.md 的所有文本读进 Java 内存。
读取文件内容属于后面的 IO 流职责。
6.5 为什么 "D:\test\a.txt" 容易出问题?
因为:
\
在 Java 字符串中是转义符。
例如:
\t
会被解释为 Tab。
因此应该:
"D:\\test\\a.txt"
或者在 Windows 环境下通常可以:
"D:/test/a.txt"
6.6 相对路径永远相对于 IDEA 项目根目录吗?
不能这样绝对理解。
更加准确的是:
java.io默认根据 JVM 当前用户目录解析相对路径。
可以查看:
System.getProperty("user.dir")
IDEA 的 Working Directory 又可以进行配置。
6.7 File 和 IO 流到底什么关系?
可以记住:
File
解决“在哪里”
IO Stream
解决“怎么读写”
例如:
File
↓
D:\data\a.txt
InputStream
↓
把 a.txt 数据读进 Java
OutputStream
↓
把 Java 数据写入 a.txt
6.8 File 和文件系统中的真实对象是一回事吗?
不是。
准确模型是:
File Java 对象
↓
表示
抽象路径
↓
可能对应
真实文件系统对象
这个:
可能
非常关键。
七、练习与验收
7.1 知识问答
请闭卷回答:
File类位于哪个包?File对象本质上表示文件内容还是路径抽象?new File("a.txt")是否意味着真正创建了a.txt?- File 对象可以表示哪些类型的文件系统对象?
- 为什么一个不存在的路径仍然可以构造
File对象? - 什么是绝对路径?
- 什么是相对路径?
- Java 中相对路径默认依据什么进行解析?
- 如何查看当前 JVM 的
user.dir? - Windows 路径中的
\为什么经常需要写成\\? - File 与 IO 流分别解决什么问题?
- 为什么不能简单认为相对路径永远相对于 IDEA 工程根目录?
7.2 代码阅读
先不要运行下面代码:
import java.io.File;
public class FileReadDemo {
public static void main(String[] args) {
File f1 =
new File("data\\student.txt");
File f2 =
new File(
"data",
"student.txt"
);
File parent =
new File("data");
File f3 =
new File(
parent,
"student.txt"
);
System.out.println(f1);
System.out.println(f2);
System.out.println(f3);
System.out.println(
System.getProperty("user.dir")
);
}
}
回答:
f1、f2、f3想表达的路径是什么?- 三种写法分别对应哪个
File构造器? - 执行这些构造器时,是否要求
student.txt已经存在? - 如果
data目录不存在,三个File对象还能否被创建? - 最后一行输出的内容与前三个相对路径有什么关系?
完成分析后再运行验证。
7.3 手写代码
任务一:三种方式构建路径
不查看前文,自己完成:
目标路径:
data/student/info.txt
分别使用:
File(String pathname)
File(String parent, String child)
File(File parent, String child)
构造目标 File 对象。
任务二:观察当前工作目录
编写程序:
- 输出当前
user.dir; - 创建相对路径
data/test.txt; - 输出这个 File 对象;
- 思考该相对路径最终会对应到哪里。
本任务暂时不要求创建真实文件。
任务三:绝对路径与相对路径
分别创建:
一个绝对路径 File
一个相对路径 File
然后用自己的话解释:
二者定位文件系统对象时的区别
7.4 Debug
观察代码:
import java.io.File;
public class FileDebugDemo {
public static void main(String[] args) {
File file =
new File("D:\test\new\data.txt");
System.out.println(file);
}
}
要求:
- 判断路径字符串是否存在问题;
- 找出所有可能被 Java 当作转义序列处理的位置;
- 修改为正确的 Windows 路径表示;
- 再尝试使用
/改写; - 用自己的话说明 Bug 的根本原因。
7.5 综合训练
假设一个 Java 项目准备保存:
data
├── users
│ └── users.txt
│
├── article
│ └── article.md
│
└── images
└── avatar.png
目前不要真正创建任何文件。
只要求使用 File:
- 为
data创建一个 File 对象; - 基于
data对象创建users、article、images; - 基于三个目录继续创建三个文件对应的 File 对象;
- 不允许所有路径都写成长字符串;
- 画出这些 File 对象之间的父子路径关系。
目标是理解:
文件系统层级
↓
可以映射成
↓
File 对象之间的路径组合
7.6 本章验收
如果你能够闭卷解释下面这一整段逻辑,本章核心就已经掌握:
File 位于 java.io 包。
File 对象并不代表文件内容,
它本质上是文件或目录路径名的抽象表示。
File 可以描述普通文件,也可以描述目录,
甚至可以描述当前并不存在的路径。
new File(...)
只是创建 Java File 对象,
不会自动在磁盘创建文件。
路径可以分为绝对路径和相对路径。
绝对路径自身拥有完整定位信息;
相对路径需要基于当前工作目录解析,
java.io 默认会参考 user.dir。
Windows 路径中的反斜杠在 Java 字符串中涉及转义,
因此经常需要使用 \\。
File 主要解决文件系统对象和路径问题,
真正读取、写入文件内容需要后面的 IO 流。
最后要求自己完成:
- [ ] 能闭卷写出
File的三种核心构造方式。 - [ ] 能解释
File 对象 ≠ 真实文件。 - [ ] 能解释
File 对象 ≠ 文件内容。 - [ ] 能区分文件、目录和路径三个概念。
- [ ] 能区分绝对路径和相对路径。
- [ ] 能解释
user.dir与相对路径之间的关系。 - [ ] 能正确书写 Windows 文件路径。
- [ ] 能说明 File 与后续 IO 流之间的职责边界。