PAYATHON 2026

FileWriter为何写入.vscode/nbcode而非res文件夹

排障赵师傅

结论

FileWriter 解析相对路径时,以 Java 进程的当前工作目录为起点,与 .java 文件所在的 src 目录无关。

文件写入了 .vscode/nbcode,说明程序运行时的工作目录是这里。要把文件保存到 Testing/res,需要将工作目录设为 Testing,并在写入文件前创建 res 目录。

可以先打印当前工作目录,检查配置是否生效:

System.out.println("Working directory: "
        + Path.of("").toAbsolutePath().normalize());

推荐写法

工作目录设为 Testing 后,可以这样写:

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

public class Main {
    public static void main(String[] args) throws IOException {
        Path resDir = Path.of("res").toAbsolutePath().normalize();
        Files.createDirectories(resDir);

        Path settingsFile = resDir.resolve("settings.txt");

        try (BufferedWriter writer = Files.newBufferedWriter(
                settingsFile,
                StandardCharsets.UTF_8,
                StandardOpenOption.CREATE,
                StandardOpenOption.TRUNCATE_EXISTING)) {
            writer.write("example");
        }

        System.out.println("Saved to: " + settingsFile);
    }
}

Path.of("res") 不需要手动拼接 Windows 路径分隔符,在 Windows、Linux 和 macOS 上都能正确构造路径。

必须使用 FileWriter 时,可以写成:

Path resDir = Path.of("res").toAbsolutePath().normalize();
Files.createDirectories(resDir);

Path settingsFile = resDir.resolve("settings.txt");

try (FileWriter writer = new FileWriter(settingsFile.toFile())) {
    writer.write("example");
}

FileWriter 可以创建 settings.txt,但不会创建缺失的父目录,所以仍需调用 Files.createDirectories()。

设置 VS Code 的工作目录

如果 Testing 是当前 VS Code 工作区的根目录,可以在 Java 调试配置中这样设置:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "java",
            "name": "Launch Main",
            "request": "launch",
            "mainClass": "Main",
            "cwd": "${workspaceFolder}"
        }
    ]
}

设置后,需要通过这份配置启动程序,例如在“运行和调试”面板中选择 Launch Main。launch.json 只对通过相应调试配置启动的进程生效。

编辑器扩展提供的 Run Code、测试运行器或终端命令等其他运行入口,可能不会读取这份 launch.json。遇到这种情况,可以先在终端进入项目目录,再运行程序:

cd /path/to/Testing
java -cp out Main

在 Windows 命令提示符中可以使用:

cd C:\path\to\Testing
java -cp out Main

运行后的目录结构应类似:

Testing/
├── res/
│   └── settings.txt
└── src/
    └── Main.java

在 VS Code 多根工作区中,${workspaceFolder} 不一定指向 Testing。此时可以按工作区名称指定:

"cwd": "${workspaceFolder:Testing}"

这里的名称应与 VS Code 工作区中显示的文件夹名称一致。

为什么 ../res 没有解决问题

下面的路径仍然从当前工作目录开始解析:

Path resDir = Paths.get("..", "res");

假设当前工作目录是:

Testing/.vscode/nbcode

那么 ../res 实际指向的可能是:

Testing/.vscode/res

它不是“源代码目录的上一级”,因为 Java 解析相对路径时不会自动参考源文件的位置。

可以用下面的代码查看任意相对路径最终解析到哪里:

Path path = Path.of("..", "res").toAbsolutePath().normalize();
System.out.println(path);

打包后如何选择保存位置

程序打包成 JAR 或可执行文件后,启动位置可能发生变化。用户可以从终端、快捷方式、文件管理器或系统服务启动程序,因此当前工作目录不一定是可执行文件所在的目录。

如果配置文件必须放在应用目录下,可以通过启动脚本或启动参数明确传入应用目录:

java -Dapp.home=/path/to/Testing -jar application.jar

然后在程序中读取这个目录:

String appHome = System.getProperty("app.home");

if (appHome == null || appHome.isBlank()) {
    throw new IllegalStateException("Missing required system property: app.home");
}

Path resDir = Path.of(appHome, "res").toAbsolutePath().normalize();
Files.createDirectories(resDir);

Path settingsFile = resDir.resolve("settings.txt");

如果文件保存的是用户运行程序后产生的设置,通常应放在用户可写目录中,而不是随程序附带的 res 目录:

Path settingsDir = Path.of(
        System.getProperty("user.home"),
        ".my-application"
);

Files.createDirectories(settingsDir);
Path settingsFile = settingsDir.resolve("settings.txt");

有些系统中的安装目录可能只读。发布程序时,需要区分随应用打包的只读资源和运行期间生成或修改的用户数据。用户数据通常不应写入类路径资源目录。

备注:内容仅供参考。