网站公告

本次更新:没啥更新的

“钱塘江上潮信起,今日方知我是我”

Skip to content

File ​

字数: 0 字 时长: 0 分钟

在 IO 操作中,文件操作相对来说比较复杂,但也是使用频率最高的一部分。几乎所有项目中都会有一个叫做 FileUtil 或者 FileUtils 的工具类。

java.io.File 类是专门对文件进行操作的类,需要注意:

File 只能对文件本身进行操作,不能对文件内容进行读写。如果想要操作文件内容,需要借助输入流和输出流。

File 类是文件和目录的抽象表示,主要用于文件和目录的创建、查找、删除等操作。

怎么理解这两句话?其实很简单:

  • 第一,File 和流无关,File 类本身不能对文件内容进行读和写,也就是不能完成输入和输出。
  • 第二,File 可以表示 /文件目录1,也可以表示 /文件目录1/文件.txt。前者是目录(Directory),后者是文件(File),File 类都可以对它们进行操作。

1. File 构造方法 ​

在 Java 中,一切皆对象,File 类也不例外。要使用 File,首先需要通过构造方法创建对应的对象。

比较常用的构造方法有三个:

  1. File(String pathname):通过给定的路径创建新的 File 实例。
  2. File(String parent, String child):通过父路径字符串和子路径字符串创建新的 File 实例。
  3. File(File parent, String child):通过父路径 File 对象和子路径字符串创建新的 File 实例。

代码如下:

java
// 文件路径名
String path = "/Users/mac/Documents/CompanyCode2/123.txt";
File file1 = new File(path);

// 文件路径名
String path2 = "/Users/mac/Documents/CompanyCode2/1/2.txt";
File file2 = new File(path2);

// 通过父路径和子路径字符串
String parent = "/Users/mac/Documents/CompanyCode2/aaa";
String child = "bbb.txt";
File file3 = new File(parent, child);

// 通过父级 File 对象和子路径字符串
File parentDir = new File("/Users/mac/Documents/CompanyCode2/aaa");
String child2 = "bbb.txt";
File file4 = new File(parentDir, child2);

macOS 和 Linux 路径使用正斜杠 / 作为路径分隔符,而 Windows 路径使用反斜杠 \ 作为路径分隔符。

所以在遇到路径分隔符的时候,不要直接固定写 / 或者 \。

Java 提供了一个跨平台的路径分隔符:

java
File.separator

这个属性会根据当前操作系统自动返回对应的路径分隔符。

File 类还需要注意两点:

  1. 一个 File 对象用于表示硬盘中的文件或目录。
  2. File 的构造方法不会检查文件或目录是否真实存在,因此无论对应路径是否存在,都不影响 File 对象的创建。

2. File 常用方法 ​

File 的常用方法主要可以分为:

text
File 常用方法
├── 获取功能
├── 绝对路径和相对路径
├── 判断功能
├── 创建和删除
├── 目录遍历
└── 递归遍历

2.1 获取功能的方法 ​

常用方法:

  1. getAbsolutePath():返回此 File 的绝对路径。
  2. getPath():返回创建 File 对象时使用的路径。
  3. getName():返回文件名或目录名。
  4. length():返回文件长度,以字节为单位。

测试代码如下,具体路径以自己的电脑为准:

java
public class CommonMethodsForFile {

    public static void main(String[] args) {

        File f =
                new File(
                        "/Users/mac/Documents/CompanyCode2/logfile.log"
                );

        System.out.println(
                "文件绝对路径:" + f.getAbsolutePath()
        );

        System.out.println(
                "文件构造路径:" + f.getPath()
        );

        System.out.println(
                "文件名称:" + f.getName()
        );

        System.out.println(
                "文件长度:" + f.length() + "字节"
        );

        File f2 =
                new File(
                        "/Users/mac/Documents/CompanyCode2/"
                );

        System.out.println(
                "目录绝对路径:" + f2.getAbsolutePath()
        );

        System.out.println(
                "目录构造路径:" + f2.getPath()
        );

        System.out.println(
                "目录名称:" + f2.getName()
        );

        System.out.println(
                "目录长度:" + f2.length()
        );
    }
}

需要注意:

length() 主要用于获取文件的长度。当 File 对象表示目录时,返回值通常没有实际使用意义。

结果:

text
文件绝对路径:/Users/mac/Documents/CompanyCode2/logfile.log
文件构造路径:/Users/mac/Documents/CompanyCode2/logfile.log
文件名称:logfile.log
文件长度:43897字节
目录绝对路径:/Users/mac/Documents/CompanyCode2
目录构造路径:/Users/mac/Documents/CompanyCode2
目录名称:CompanyCode2
目录长度:128

2.2 绝对路径和相对路径 ​

绝对路径是从文件系统根目录开始的完整路径,用于描述文件或目录在文件系统中的确切位置。

Windows 中的绝对路径通常以盘符开始,例如:

text
C:\Program Files\Java\jdk1.8.0_291\bin\java.exe

macOS 和 Linux 中的绝对路径通常以 / 开始,例如:

text
/usr/local/bin/python3

相对路径是相对于当前工作目录的路径,用于描述文件或目录与当前工作目录之间的位置关系。

例如:

text
config/config.properties

如果当前工作目录是:

text
/Users/mac/project

那么:

text
config/config.properties

实际表示:

text
/Users/mac/project/config/config.properties

注意:

  • 在 Windows 操作系统中,文件系统通常默认不区分大小写。例如 example.txt 和 Example.txt 通常表示同一个文件,但 Windows 也提供了区分大小写的相关配置。
  • 在 macOS 和 Linux 等 Unix 系统中,文件系统是否区分大小写与具体文件系统和配置有关,使用时需要注意文件名大小写。
java
// 绝对路径示例
File absoluteFile =
        new File(
                "/Users/mac/Documents/CompanyCode2/logfile.log"
        );

System.out.println(
        "绝对路径:" + absoluteFile.getAbsolutePath()
);

// 相对路径示例
File relativeFile =
        new File(
                "CompanyCode2/logfile.log"
        );

System.out.println(
        "相对路径:" + relativeFile.getPath()
);

2.3 判断功能的方法 ​

常用的判断方法:

  1. exists():判断文件或目录是否存在。
  2. isDirectory():判断是否为目录。
  3. isFile():判断是否为文件。

代码如下:

java
File file =
        new File(
                "/Users/mac/Documents/CompanyCode2/logfile.log"
        );

// 判断文件或目录是否存在
if (file.exists()) {
    System.out.println("文件或目录存在");
} else {
    System.out.println("文件或目录不存在");
}

// 判断是否是目录
if (file.isDirectory()) {
    System.out.println("是目录");
} else {
    System.out.println("不是目录");
}

// 判断是否是文件
if (file.isFile()) {
    System.out.println("是文件");
} else {
    System.out.println("不是文件");
}

结果:

java
File file =
        new File(
                "/Users/mac/Documents/CompanyCode2/logfile.log"
        );

// 判断文件或目录是否存在
if (file.exists()) {
    System.out.println("文件或目录存在");
} else {
    System.out.println("文件或目录不存在");
}

// 判断是否是目录
if (file.isDirectory()) {
    System.out.println("是目录");
} else {
    System.out.println("不是目录");
}

// 判断是否是文件
if (file.isFile()) {
    System.out.println("是文件");
} else {
    System.out.println("不是文件");
}

2.4 创建、删除功能的方法 ​

常用方法:

  • createNewFile():文件不存在时创建一个新的空文件并返回 true;文件已经存在时不创建并返回 false。
  • delete():删除文件或目录。如果是目录,只有目录为空时才能删除。
  • mkdir():创建一级目录,如果父目录不存在则创建失败。返回 true 表示创建成功,返回 false 表示创建失败。
  • mkdirs():可以创建多级目录,如果父目录不存在,会一并创建。返回 true 表示创建成功,返回 false 表示创建失败或者目录已经存在。

开发中一般使用:

java
mkdirs()

方法测试:

java
// 创建文件
File file2 =
        new File(
                "/Users/mac/Documents/CompanyCode2/example/test.txt"
        );

if (file2.createNewFile()) {
    System.out.println(
            "创建文件成功:" + file2.getAbsolutePath()
    );
} else {
    System.out.println(
            "创建文件失败:" + file2.getAbsolutePath()
    );
}

// 删除文件
if (file2.delete()) {
    System.out.println(
            "删除文件成功:" + file2.getAbsolutePath()
    );
} else {
    System.out.println(
            "删除文件失败:" + file2.getAbsolutePath()
    );
}

// 创建多级目录
File directory =
        new File(
                "/Users/username/example/subdir1/subdir2"
        );

if (directory.mkdirs()) {
    System.out.println(
            "创建目录成功:" + directory.getAbsolutePath()
    );
} else {
    System.out.println(
            "创建目录失败:" + directory.getAbsolutePath()
    );
}

2.5 目录的遍历 ​

常用方法:

  • String[] list():返回一个 String 数组,表示该目录中的所有子文件或子目录名称。
  • File[] listFiles():返回一个 File 数组,表示该目录中的所有子文件或子目录。
java
File directory2 =
        new File(
                "/Users/mac/Documents/LearnCode/framework-learn-javase"
        );

// 列出目录下的文件名
String[] files =
        directory2.list();

System.out.println(
        "目录下的文件名:"
);

for (String file3 : files) {
    System.out.println(file3);
}

// 列出目录下的文件和子目录
File[] filesAndDirs =
        directory2.listFiles();

System.out.println(
        "目录下的文件和子目录:"
);

for (File fileOrDir : filesAndDirs) {

    if (fileOrDir.isFile()) {

        System.out.println(
                "文件:" + fileOrDir.getName()
        );

    } else if (fileOrDir.isDirectory()) {

        System.out.println(
                "目录:" + fileOrDir.getName()
        );
    }
}

listFiles() 在获取指定目录下的文件或子目录时,需要注意:

  1. 指定的目录必须存在。
  2. 指定的路径必须是目录,否则 listFiles() 可能返回 null,后续直接遍历容易引发 NullPointerException。

2.6 递归遍历 ​

直接看代码:

java
public static void main(String[] args) {

    File directory =
            new File(
                    "/Users/mac/Documents/LearnCode/framework-learn-javase"
            );

    // 递归遍历目录下的文件和子目录
    traverseDirectory(directory);
}

public static void traverseDirectory(
        File directory
) {

    // 列出目录下的所有文件和子目录
    File[] filesAndDirs =
            directory.listFiles();

    // 遍历每个文件和子目录
    for (
            File fileOrDir : filesAndDirs
    ) {

        if (
                fileOrDir.isFile()
        ) {

            // 如果是文件,输出文件名
            System.out.println(
                    "文件:" + fileOrDir.getName()
            );

        } else if (
                fileOrDir.isDirectory()
        ) {

            // 如果是目录,输出目录名并继续递归
            System.out.println(
                    "目录:" + fileOrDir.getName()
            );

            traverseDirectory(
                    fileOrDir
            );
        }
    }
}

3. RandomAccessFile ​

RandomAccessFile 是 Java 中一个比较特殊的类,它既可以读取文件,也可以写入文件。

与 FileInputStream、FileOutputStream 等普通 IO 类不同,RandomAccessFile 可以将文件指针移动到文件中的指定位置,再从该位置开始读取或者写入数据。

因此,它比较适合需要对文件进行随机访问的场景,例如数据库系统。

下面是一个 RandomAccessFile 的示例,包括文件的写入和读取:

image-20250321203846571

image-20250321203904351

3.1 构造方法 ​

RandomAccessFile 主要有两个构造方法:

  • RandomAccessFile(File file, String mode):使用给定的 File 对象和访问模式创建新的 RandomAccessFile 实例。
  • RandomAccessFile(String name, String mode):使用给定的文件名和访问模式创建新的 RandomAccessFile 实例。

访问模式 mode 的值可以是:

  • "r":以只读模式打开文件,调用任何 write 方法都会导致 IOException。
  • "rw":以读写模式打开文件,如果文件不存在则创建。
  • "rws":以读写模式打开文件,并要求对文件内容或元数据的每次更新同步写入底层存储设备。
  • "rwd":与 "rws" 类似,但主要要求文件内容的更新同步写入,元数据更新可以延迟。

3.2 主要方法 ​

常用方法:

  • long getFilePointer():返回文件指针的当前位置。
  • long length():返回文件长度。
  • int read():读取一个字节的数据。
  • int read(byte[] b):读取数据并保存到指定字节数组中。
  • int read(byte[] b, int off, int len):从文件读取数据并保存到指定字节数组,从 off 开始,最多读取 len 个字节。
  • String readLine():从文件中读取一行文本。
  • readUTF():读取由 writeUTF() 写入的字符串。
  • void seek(long pos):将文件指针移动到文件中的 pos 位置。
  • void write(byte[] b):将指定字节数组写入文件。
  • void write(byte[] b, int off, int len):将指定字节数组中从 off 开始的 len 个字节写入文件。
  • void write(int b):向文件写入一个字节。
  • writeUTF(String str):以 writeUTF() 所使用的格式将字符串写入文件。

其中:

text
writeUTF()
    ↓
写入长度信息 + 字符串数据

readUTF()
    ↓
读取长度信息 + 字符串数据

因此,使用 readUTF() 读取时,需要保证对应的数据是通过 writeUTF() 写入的。

再来看一个示例,结合前面的讲解,就可以掌握 RandomAccessFile 的基本使用。

java
public class RandomAccessFileDemo2 {

    public static void main(String[] args) {

        File file =
                new File(
                        "logs/javabetter/nihao.txt"
                );

        try (
                RandomAccessFile raf =
                        new RandomAccessFile(
                                file,
                                "rw"
                        )
        ) {

            // 写入文件
            raf.writeUTF(
                    "Hello, kerwim!"
            );

            // 将文件指针移动到文件开头
            raf.seek(0);

            // 读取文件内容
            String content =
                    raf.readUTF();

            System.out.println(
                    "内容: " + content
            );

        } catch (
                IOException e
        ) {

            e.printStackTrace();
        }
    }
}

在这个示例中,首先创建了一个名为 nihao.txt 的文件对象,然后使用 RandomAccessFile 以读写模式打开这个文件。

接着使用:

java
writeUTF("Hello, kerwim!");

将字符串写入文件。

然后通过:

java
seek(0);

把文件指针移动到文件开头,再使用:

java
readUTF();

读取文件内容。

最终输出:

text
Hello, kerwim!

最后使用 try-with-resources,确保 RandomAccessFile 在操作完成后被正确关闭。


4. Apache FileUtils 类 ​

FileUtils 是 Apache Commons IO 中提供的文件操作工具类,提供了更加方便的方法来操作文件和目录。

4.1 复制文件或目录 ​

java
File srcFile =
        new File(
                "path/to/src/file"
        );

File destFile =
        new File(
                "path/to/dest/file"
        );

// 复制文件
FileUtils.copyFile(
        srcFile,
        destFile
);

// 复制目录
FileUtils.copyDirectory(
        srcFile,
        destFile
);

4.2 删除文件或目录 ​

java
File file =
        new File(
                "path/to/file"
        );

// 删除文件或目录
FileUtils.delete(file);

需要注意的是,如果要删除一个非空目录,需要先删除目录中的所有文件和子目录。

4.3 移动文件或目录 ​

java
File srcFile =
        new File(
                "path/to/src/file"
        );

File destFile =
        new File(
                "path/to/dest/file"
        );

// 移动文件或目录
FileUtils.moveFile(
        srcFile,
        destFile
);

4.4 查询文件或目录的信息 ​

java
File file =
        new File(
                "path/to/file"
        );

// 获取文件或目录的修改时间
Date modifyTime =
        FileUtils.lastModified(file);

// 获取文件或目录的大小
long size =
        FileUtils.sizeOf(file);

// 获取文件或目录的扩展名
String extension =
        FileUtils.getExtension(
                file.getName()
        );

5. Hutool FileUtil 类 ​

FileUtil 是 Hutool 工具包中的文件操作工具类,提供了一系列简单易用的文件操作方法,可以帮助 Java 开发者快速完成文件相关的操作。

FileUtil 主要包含以下几类操作:

  • 文件操作:文件或目录的新建、删除、复制、移动、改名等。
  • 文件判断:判断文件或目录是否非空、是否为目录、是否为文件等。
  • 绝对路径:针对 ClassPath 中的文件转换为绝对路径文件。
  • 文件名:获取主文件名、扩展名等。
  • 读操作:包括 getReader、readXXX 等操作。
  • 写操作:包括 getWriter、writeXXX 等操作。

下面是 FileUtil 中一些常用的方法。

5.1 copyFile ​

复制文件,将指定的源文件复制到目标文件中。

java
File dest =
        FileUtil.file(
                "FileUtilDemo2.java"
        );

FileUtil.copyFile(
        file,
        dest
);

5.2 move ​

移动文件或目录,将指定的源文件或目录移动到目标位置。

java
FileUtil.move(
        file,
        dest,
        true
);

5.3 del ​

删除指定的文件或目录。

java
FileUtil.del(file);

5.4 rename ​

将指定的文件或目录重命名为新的名称。

java
FileUtil.rename(
        file,
        "FileUtilDemo3.java",
        true
);

5.5 readLines ​

读取文件中的每一行数据。

java
FileUtil.readLines(
        file,
        "UTF-8"
).forEach(
        System.out::println
);

更多方法,可以去看一下 Hutool 的源码,里面有非常多实用的方法。多看看,绝对能提升不少编程水平。

Noctiflorous Garden · MIT License