【深入理解Java IO流0x02】详解Java中的文件、目录操作(File、Paths、Files类)

本文详细介绍了Java中File、Paths、Files类在文件和目录操作中的使用,包括构造方法、文件信息判断、创建和删除、目录遍历,以及RandomAccessFile类的特点。同时提到了HutoolFileUtil工具类的便捷功能。
摘要由CSDN通过智能技术生成

1. 引言

我们都知道,大部分时候,我们用IO流操作的对象是一个又一个文件,文件操作在Java中是相对复杂但是使用频率比较高的。项目中几乎都会存在一个类似于叫FileUtils的工具类。
因此本篇博客将为大家介绍Java中关于文件、目录的相关操作。主要就是三个类,分别是来自于java.io包下的File类,以及java.nio.file包下的Paths类和Files类。
接下来我们正式开始!

2. File类

java.io.File类是专门对文件进行操作的类,注意只能对文件本身进行操作,不能对文件内容进行操作,想要操作内容,必须借助IO流。
File类可以操作的是文件(file)和文件夹(directory),比如C:\folder1C:\folder1\文件.txt

2.1 构造方法

众所周知,Java中一切皆对象,File类也不例外,我们首先来分析File类的构造方法:

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

在进行实战前,需要大家先花5min了解一下Windows&Linux的目录分隔符的区别,详见我的这篇博客:Windows与Linux路径分隔符对比及Java代码实战
来看实际的写法:

@Test
public void test001(){
    String path1 = "./test.txt";
    // 表示当前项目目录下的test.txt文件
    File file1 = new File(path1);

    String path2 = "./testFolder";
    // 表示当前项目目录下的testFolder文件夹
    File file2 = new File(path2);

    String parentPath1 = "C:/workSpace/demo001";
    String childPath1 = "testFolder/cola.txt";
    // 拼接的是绝对路径
    File file3 = new File(parentPath1,childPath1);

    File parentFile = new File(parentPath1);
    File file4 = new File(parentFile,childPath1);

    // 打印绝对路径看看
    System.out.println(file1.getAbsolutePath());
    System.out.println(file2.getAbsolutePath());
    System.out.println(file3.getAbsolutePath());
    System.out.println(file4.getAbsolutePath());
}
-------------------------------------------------
output:
C:\workSpace\demo001\.\test.txt
C:\workSpace\demo001\.\testFolder
C:\workSpace\demo001\testFolder\cola.txt
C:\workSpace\demo001\testFolder\cola.txt

再重申一下,对于linux操作系统,使用/作为路径分隔符,而Windows是用的\。当然我们在Java端可以使用/,但实际上,Java 中已经提供了一个跨平台的方法来获取路径分隔符,即File.separator,这个属性会根据操作系统自动返回正确的路径分隔符,是进行路径拼接的时候推荐使用的。
这一段的最后,总结一下File 类的注意点:

  1. 一个 File 对象代表硬盘中实际存在的一个文件或者目录。
  2. File 类的构造方法不会检验这个文件或目录是否真实存在,因此无论该路径下是否存在文件或者目录,都不影响 File 对象的创建。

2.2 常用方法

File 的常用方法主要分为获取文件信息、文件信息判断、创建删除等方法。
一、获取文件信息:

  1. getAbsolutePath() :返回此 File 的绝对路径。
  2. getPath() :返回相对路径(Windows下、jdk 11)。
  3. getName() :返回文件名或目录名。
  4. length() :返回文件长度,以字节为单位。如果File对象不存在或是文件夹,返回0。

实战一把:

@Test
public void test002(){
    String path1 = "./test.txt";
    File file1 = new File(path1);

    System.out.println(file1.getAbsolutePath());
    System.out.println(file1.getPath());
    System.out.println(file1.getName());
    System.out.println(file1.length());

    File file2 = new File("./test2.txt");
    System.out.println("一个实际不存在的file的length为:"+file2.length());

    File file3 = new File("./testFolder");
    System.out.println("一个文件夹的length为:"+file3.length());
}
----------------------------------------------------------------------
output:
C:\workSpace\demo001\.\test.txt
.\test.txt
test.txt
29
一个实际不存在的file的length为:0
一个文件夹的length为:0

二、文件信息判断:

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

这部分应该没什么好说的,但还是实战一把:

@Test
public void test002(){
    String path1 = "./test.txt";
    File file1 = new File(path1);
    
    if(!file1.exists()){
        System.out.println("文件(夹)不存在");
    }
    
    if(file1.isFile()){
        System.out.println("我是一个文件");
    }

    if(file1.isDirectory()){
        System.out.println("我是一个文件夹");
    }
}

三、创建、删除:

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

对于创建目录,开发中一般使用mkdirs()
实战一把:

// 创建文件
File file = new File("./test.txt");
if (file.createNewFile()) {
    System.out.println("创建文件成功:" + file.getAbsolutePath());
} else {
    System.out.println("创建文件失败:" + file.getAbsolutePath());
}

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

// 创建多级目录
File directory = new File("./subdir1/subdir2");
if (directory.mkdirs()) {
    System.out.println("创建目录成功:" + directory.getAbsolutePath());
} else {
    System.out.println("创建目录失败:" + directory.getAbsolutePath());
}

四、目录的遍历:

  1. String[] list() :返回一个 String 数组,表示该 File 目录中的所有子文件或目录。
  2. File[] listFiles() :返回一个 File 数组,表示该 File 目录中的所有的子文件或目录。
@Test
public void test003(){
    File dir = new File("./");

    String[] strs = dir.list();
    for(String str:strs){
        System.out.println(str);
    }
    System.out.println("************");
    File[] files = dir.listFiles();
    for(File file:files){
        System.out.println(file.getName());
    }
}
-------------------------------------------
output:
.gitignore
.idea
pom.xml
src
target
test.txt
testFolder
************
.gitignore
.idea
pom.xml
src
target
test.txt
testFolder

其中,listFiles()在获取指定目录下的文件或者子目录时必须满足下面两个条件:

  1. 指定的目录必须存在;
  2. 指定的必须是目录。否则容易引发 NullPointerException 异常;

当然,直接调用list()listFiles()只会遍历一层,我们来写个递归遍历:

@Test
public void test004(){
    File file = new File("./");
    traverseDir(file);
}

public void traverseDir(File dir){
    File[] files = dir.listFiles();
    for(File file:files){
        if(file.isFile()){
            System.out.println("文件:"+file.getName());
        }
        if(file.isDirectory()){
            System.out.println("目录:"+file.getName());
            traverseDir(file);
        }
    }
}
--------------------------------------------
output:
文件:.gitignore
目录:.idea
文件:.gitignore
文件:compiler.xml
文件:encodings.xml
文件:jarRepositories.xml
文件:misc.xml
文件:workspace.xml
文件:pom.xml
目录:src
目录:main
目录:java
...
...

3. Paths类&Path接口

Paths类是在JDK 7时引入的,作为对java.io.File类的补充和改进。
Paths 类主要用于操作文件和目录路径。它提供了一些静态方法,用于创建java.nio.file.Path实例,代表文件系统中的路径。
image.png
下面是 Paths 的一个示例。

@Test
public void test001(){
    // 相对路径
    Path path1 = Paths.get("test.txt");
    // 绝对路径
    Path path2 = Paths.get("D:/Workspace/javaProjects/demo001/test.txt");
}

java.nio.file.Path接口在 Java NIO.2 中代表一个文件系统中的路径。它提供了一系列方法来操作和查询路径。
image.png

@Test
public void test001(){
    Path path = Paths.get("D:/Workspace/javaProjects/demo001");
    // 获取文件名
    System.out.println(path.getFileName());
    // 获取根路径
    System.out.println(path.getRoot());
    // 获取父路径
    System.out.println(path.getParent());


    Path path2 = Paths.get("test.txt");
    // 相对路径-->绝对路径
    System.out.println(path2.toAbsolutePath());
}
----------------------------------------------------------
output:
demo001
D:\
D:\Workspace\javaProjects
D:\Workspace\javaProjects\demo001\test.txt

4. Files类

java.nio.file.Files类提供了大量静态方法,用于处理文件系统中的文件和目录。这些方法包括文件的创建、删除、复制、移动等操作,以及读取和设置文件属性。
下面展示一个 Files 和 Paths 一起使用的示例:

@Test
public void test002() throws IOException {
    Path path = Paths.get("test2.txt");
    // 创建文件
    Files.createFile(path);
    System.out.println(Files.exists(path)); // true
    // 删除文件
    Files.delete(path);
    System.out.println(Files.exists(path)); // false
}

接下来给出一些常用方法:

  1. exists(Path path, LinkOption... options):检查文件或目录是否存在。
Path path = Paths.get("test.txt");
System.out.println(path.exists());

LinkOption 是一个枚举类,它定义了如何处理文件系统链接的选项。LinkOption 主要在与文件或目录的路径操作相关的方法中使用,以控制这些方法如何处理符号链接。平时不太常用。

  1. createFile(Path path, FileAttribute<?>... attrs):创建一个新的空文件。
Path newPath = Paths.get("newFile.txt");
Files.createFile(newPath);

其中可选的第二个参数FileAttribute 是一个泛型接口,用于处理各种不同类型的属性平时不太常用。

  1. createDirectory(Path dir, FileAttribute<?>... attrs):创建一个新的目录。
Path newDir = Paths.get("newDirectory");
Files.createDirectory(newDir);
  1. delete(Path path):删除文件或目录。
Path pathToDelete = Paths.get("fileToDelete.txt");
Files.delete(pathToDelete);
  1. copy(Path source, Path target, CopyOption... options):复制文件或目录。
Path sourcePath = Paths.get("sourceFile.txt");
Path targetPath = Paths.get("targetFile.txt");
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);

在 Java NIO 中,有两个实现了 CopyOption 接口的枚举类:StandardCopyOption 和 LinkOption。
常用的是StandardCopyOption 枚举类,提供了以下两个选项:

  • REPLACE_EXISTING:如果目标文件已经存在,该选项会使 Files.copy() 方法替换目标文件。如果不指定此选项,Files.copy() 方法在目标文件已存在时将抛出 FileAlreadyExistsException。
  • COPY_ATTRIBUTES:此选项表示在复制文件时,尽可能地复制文件的属性(如文件时间戳、权限等)。如果不指定此选项,那么目标文件将具有默认的属性。
  1. move(Path source, Path target, CopyOption... options):移动或重命名文件或目录。和copy类似,不多赘述了。
Path sourcePath = Paths.get("sourceFile.txt");
Path targetPath = Paths.get("targetFile.txt");
Files.move(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
  1. readAllLines(Path path, Charset cs):读取文件的所有行到一个字符串列表。
Path path =Paths.get("test.txt");
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
lines.forEach(System.out::println);
  1. write(Path path, Iterable<? extends CharSequence> lines, Charset cs, OpenOption... options):将字符串列表写入文件。
// 查看原文件中的文本内容
Path path =Paths.get("test.txt");
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
lines.forEach(System.out::println);

// 追加一些文字
List<String> newLines = Arrays.asList("2024","04","06","hello");
Files.write(path,newLines,StandardCharsets.UTF_8, StandardOpenOption.APPEND);

// 查看追击后的情况
lines = Files.readAllLines(path, StandardCharsets.UTF_8);
System.out.println("-------After appending-------");
lines.forEach(System.out::println);
---------------------------------------------
output:
Hello World!
I love coffee.
-------After appending-------
Hello World!
I love coffee.2024
04
06
hello
  1. newBufferedReader(Path path, Charset cs) newBufferedWriter(Path path, Charset cs, OpenOption... options):创建 BufferedReader 和 BufferedWriter 对象以读取和写入文件。
Path path = Paths.get("file.txt");

// Read file
try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
}

// Write file
Path outputPath = Paths.get("outputFile.txt");
try (BufferedWriter writer = Files.newBufferedWriter(outputPath, StandardCharsets.UTF_8)) {
    writer.write("你好世界...");
}

5. RandomAccessFile类

RandomAccessFile 是 Java 中一个非常特殊的类,它既可以用来读取文件,也可以用来写入文件。与其他 IO 类(如 FileInputStream 和 FileOutputStream)不同,RandomAccessFile 允许您跳转到文件的任何位置,从那里开始读取或写入。这使得它特别适用于需要在文件中随机访问数据的场景,如数据库系统。
构造方法:

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

访问模式mode的值可以为:

  • “r”:以只读模式打开文件。调用结果对象的任何 write 方法都将导致 IOException。
  • “rw”:以读写模式打开文件。如果文件不存在,它将被创建。
  • “rws”:以读写模式打开文件,并要求对内容或元数据的每个更新都被立即写入到底层存储设备。这种模式是同步的,可以确保在系统崩溃时不会丢失数据。
  • “rwd”:与“rws”类似,以读写模式打开文件,但仅要求对文件内容的更新被立即写入。元数据可能会被延迟写入。

主要方法:

  • long getFilePointer():返回文件指针的当前位置。
  • long length():返回此文件的长度。
  • int read():从该文件中读取一个字节数据。
  • int read(byte[] b):从该文件中读取字节数据并将其存储到指定的字节数组中。
  • int read(byte[] b, int off, int len):从该文件中读取字节数据并将其存储到指定的字节数组中,从偏移量 off 开始,最多读取 len 个字节。
  • String readLine():从该文件中读取一行文本。
  • readUTF():从文件读取 UTF-8 编码的字符串。此方法首先读取两个字节的长度信息,然后根据这个长度读取字符串的 UTF-8 字节。最后,这些字节被转换为 Java 字符串。这意味着当你使用 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):将一个字符串以 UTF-8 编码写入文件。此方法首先写入两个字节的长度信息,表示字符串的 UTF-8 字节长度,然后写入 UTF-8 字节本身。因此,当你使用 writeUTF 写入字符串时,实际写入的字节数会比字符串的 UTF-8 字节长度多两个字节。这两个字节用于在读取字符串时确定正确的字符串长度。

来看实战代码:

@Test
public void test004() throws IOException {
    File file = new File("test.txt");
    // 以"读写"的方式打开file
    RandomAccessFile raf = new RandomAccessFile(file,"rw");
    try{
        // 指针移动到文件尾,追加文本
        raf.seek(raf.length());
        raf.writeUTF("Hi,i am robot-001.");

        // 指针移动到文件头,读取文本
        raf.seek(0);
        String str = raf.readUTF();
        System.out.println(str);
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        // 释放
        raf.close();
    }
}

6. 常用File工具包:Hutool FileUtil类

之前说过,我们项目中可能都躺着一个FileUtil类。当然,如果项目所需的文件操作比较简单,不妨导入现成的工具。FileUtil 类是 Hutool 工具包中的文件操作工具类,提供了一系列简单易用的文件操作方法,可以帮助 Java 开发者快速完成文件相关的操作任务。
Hutool的Maven坐标:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.25</version>
</dependency>

FileUtil 类包含以下几类操作工具:

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

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

  1. copyFile:复制文件。该方法可以将指定的源文件复制到指定的目标文件中。
File dest = FileUtil.file("FileUtilDemo2.java");
FileUtil.copyFile(file, dest);
  1. move:移动文件或目录。该方法可以将指定的源文件或目录移动到指定的目标文件或目录中。
FileUtil.move(file, dest, true);
  1. del:删除文件或目录。该方法可以删除指定的文件或目录,如果指定的文件或目录不存在,则会抛出异常。
FileUtil.del(file);
  1. rename:重命名文件或目录。该方法可以将指定的文件或目录重命名为指定的新名称。
FileUtil.rename(file, "FileUtilDemo3.java", true);
  1. readLines:从文件中读取每一行数据。
FileUtil.readLines(file, "UTF-8").forEach(System.out::println);

更多方法,可以去看一下 hutool 的源码,里面有非常多实用的方法。

  • 78
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值