File 类与文件系统对象 | JavaSE

File 类与文件系统对象

一、学习目标

学完本章,你应该能够:

  1. 解释 java.io.File 类到底表示什么,明确区分“File 对象”“路径”“真实文件/目录”“文件内容”。
  2. 理解文件系统对象的基本模型,知道文件和目录都可以由 File 对象表示。
  3. 掌握 File 的三种核心构造方式,能够根据完整路径、父路径 + 子路径创建 File 对象。
  4. 区分绝对路径和相对路径,能够解释 Java 中相对路径到底相对于哪里。
  5. 正确处理 Windows 与跨平台路径写法,理解 \ 转义、/File.separator 的区别。
  6. 能够建立后续学习 File API、递归、IO 流所需要的统一文件系统认知模型。

二、核心知识

2.1 从内存世界走向文件系统

在前面的 JavaSE 学习中,我们处理的数据绝大多数存在于:

变量
↓
数组
↓
集合
↓
Java 对象

例如:

List<String> names = new ArrayList<>();

names.add("张三");
names.add("李四");

这些数据主要存在于程序运行期间的内存中。

程序结束以后:

JVM 结束
↓
进程退出
↓
内存中的对象消失

于是马上出现一个新的问题:

如果我要访问电脑磁盘上的 a.txtavatar.jpgD:\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,原因很明确:

  1. File 是经典 JavaSE 文件系统 API;
  2. 大量 Java 教程和既有代码仍在使用;
  3. 很多 IO API 可以直接接收 File
  4. 学懂 File 后,更容易理解 Path
  5. 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 知识问答

请闭卷回答:

  1. File 类位于哪个包?
  2. File 对象本质上表示文件内容还是路径抽象?
  3. new File("a.txt") 是否意味着真正创建了 a.txt
  4. File 对象可以表示哪些类型的文件系统对象?
  5. 为什么一个不存在的路径仍然可以构造 File 对象?
  6. 什么是绝对路径?
  7. 什么是相对路径?
  8. Java 中相对路径默认依据什么进行解析?
  9. 如何查看当前 JVM 的 user.dir
  10. Windows 路径中的 \ 为什么经常需要写成 \\
  11. File 与 IO 流分别解决什么问题?
  12. 为什么不能简单认为相对路径永远相对于 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")
        );
    }
}

回答:

  1. f1f2f3 想表达的路径是什么?
  2. 三种写法分别对应哪个 File 构造器?
  3. 执行这些构造器时,是否要求 student.txt 已经存在?
  4. 如果 data 目录不存在,三个 File 对象还能否被创建?
  5. 最后一行输出的内容与前三个相对路径有什么关系?

完成分析后再运行验证。


7.3 手写代码

任务一:三种方式构建路径

不查看前文,自己完成:

目标路径:

data/student/info.txt

分别使用:

File(String pathname)

File(String parent, String child)

File(File parent, String child)

构造目标 File 对象。


任务二:观察当前工作目录

编写程序:

  1. 输出当前 user.dir
  2. 创建相对路径 data/test.txt
  3. 输出这个 File 对象;
  4. 思考该相对路径最终会对应到哪里。

本任务暂时不要求创建真实文件。


任务三:绝对路径与相对路径

分别创建:

一个绝对路径 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);
    }
}

要求:

  1. 判断路径字符串是否存在问题;
  2. 找出所有可能被 Java 当作转义序列处理的位置;
  3. 修改为正确的 Windows 路径表示;
  4. 再尝试使用 / 改写;
  5. 用自己的话说明 Bug 的根本原因。

7.5 综合训练

假设一个 Java 项目准备保存:

data
├── users
│   └── users.txt
│
├── article
│   └── article.md
│
└── images
    └── avatar.png

目前不要真正创建任何文件

只要求使用 File

  1. data 创建一个 File 对象;
  2. 基于 data 对象创建 usersarticleimages
  3. 基于三个目录继续创建三个文件对应的 File 对象;
  4. 不允许所有路径都写成长字符串;
  5. 画出这些 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 流之间的职责边界。