You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java扫雷克隆版导出JAR后记录文件读写异常问题咨询

问题分析

你遇到的核心问题是:Eclipse运行时,records/records.txt指向项目根目录下的文件系统文件,FileInputStream可以正常访问;但导出为JAR后,这个相对路径会指向JAR所在的运行目录(而非JAR内部),如果该目录下没有对应的文件/文件夹就会报错,且JAR内部的资源无法通过FileInputStream直接读取(因为JAR是压缩包,内部资源不是独立的文件系统实体)。

同时,JAR文件本身是只读的,你也不能把修改后的记录写回JAR内部,必须把可编辑的记录文件放在用户有权限读写的外部目录。

解决方案

我们需要将初始默认记录放在JAR的资源目录中,将用户自定义记录保存到用户主目录下的专属文件夹(跨平台兼容,且有读写权限)。以下是修改后的代码:

1. 定义路径常量

import java.io.*;
import java.util.Scanner;

public class MinesweeperIO {
    // 用户主目录下的专属文件夹,用于保存可编辑的记录
    private static final String USER_RECORD_DIR = System.getProperty("user.home") + File.separator + ".minesweeper";
    private static final String USER_RECORD_FILE = USER_RECORD_DIR + File.separator + "records.txt";
    // JAR资源目录中的默认记录文件路径(需将原records.txt放入src/main/resources/records/下)
    private static final String DEFAULT_RESOURCE_PATH = "/records/records.txt";

2. 修改读取记录方法

优先读取用户目录下的记录文件,若不存在则读取JAR内的默认记录并初始化用户目录:

public static String readRecords() {
        File userRecordFile = new File(USER_RECORD_FILE);
        // 先尝试读取用户本地的记录文件
        if (userRecordFile.exists()) {
            try (Scanner scan = new Scanner(new FileInputStream(userRecordFile))) {
                StringBuilder records = new StringBuilder();
                while (scan.hasNextLine()) {
                    records.append(scan.nextLine()).append(System.lineSeparator());
                }
                return records.toString().trim();
            } catch (IOException e) {
                // 用户文件读取失败,降级读取默认资源
                return readDefaultRecords();
            }
        } else {
            // 用户文件不存在,初始化目录并写入默认记录
            initUserRecordDir();
            String defaultRecords = readDefaultRecords();
            writeRecords(defaultRecords, USER_RECORD_FILE);
            return defaultRecords;
        }
    }

    // 读取JAR内部的默认记录资源
    private static String readDefaultRecords() {
        try (InputStream is = MinesweeperIO.class.getResourceAsStream(DEFAULT_RESOURCE_PATH)) {
            if (is == null) return ""; // 无默认记录时返回空
            Scanner scan = new Scanner(is);
            StringBuilder records = new StringBuilder();
            while (scan.hasNextLine()) {
                records.append(scan.nextLine()).append(System.lineSeparator());
            }
            return records.toString().trim();
        } catch (IOException e) {
            return "";
        }
    }

    // 初始化用户记录目录(确保目录存在)
    private static void initUserRecordDir() {
        File dir = new File(USER_RECORD_DIR);
        if (!dir.exists()) {
            dir.mkdirs();
        }
    }

3. 修改写入记录方法

确保写入前目录存在,并用try-with-resources自动管理流资源:

public static void writeRecords(String records, String fileName) {
        // 如果写入的是用户记录文件,先确保目录存在
        if (fileName.equals(USER_RECORD_FILE)) {
            initUserRecordDir();
        }
        try (PrintStream writer = new PrintStream(new File(fileName))) {
            writer.print(records);
        } catch (IOException e) {
            throw new IllegalArgumentException("无法保存文件:" + e.getMessage());
        }
    }
}

4. 项目结构调整

将原records/records.txt移动到项目的src/main/resources/records/目录下(Maven/Gradle项目结构),或Eclipse中直接放入src目录下的resources文件夹(需标记为资源目录),这样打包JAR时该文件会被包含在JAR内部。

关键注意事项
  • 使用System.getProperty("user.home")获取用户主目录,确保Windows/macOS/Linux等平台都能找到合适的读写位置。
  • 用try-with-resources语法自动关闭流,避免资源泄漏。
  • 用System.lineSeparator()代替硬编码换行符,保证跨平台兼容性。
  • 异常信息添加具体错误描述,方便调试。

内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 23:07:29