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");
有些系统中的安装目录可能只读。发布程序时,需要区分随应用打包的只读资源和运行期间生成或修改的用户数据。用户数据通常不应写入类路径资源目录。
备注:内容仅供参考。
