在Java里如何使用FileInputStream与FileOutputStream_Java字节流操作解析

FileInputStream抛FileNotFoundException主因是路径错误或文件不存在,且相对路径基于JVM启动目录;FileOutputStream默认覆盖,追加需设append为true;应使用缓冲流提升性能;关闭流需判空或用try-with-resources。

FileInputStream 读取文件时为什么总抛出 FileNotFoundException

常见原因是路径写错或文件不存在,但更隐蔽的问题是:相对路径基于 JVM 启动目录而非源码所在目录。比如在 IDE 中运行时,当前工作目录通常是项目根目录,不是 srcresources 目录。

  • new FileInputStream("data.bin") 前,先确认该文件是否真在当前工作目录下(可打印 System.getProperty("user.dir") 查看)
  • 推荐用绝对路径或类路径定位资源:getClass().getResource("/data.bin") 返回 URL,再用 url.toURI() 构造 File
  • 别忽略

    FileNotFoundException
    IOException 的子类,必须捕获或声明抛出

FileOutputStream 写入时覆盖还是追加?如何控制

FileOutputStream 默认覆盖目标文件;要追加必须显式启用 append 模式,靠构造函数第二个 boolean 参数控制。

  • 覆盖写入:new FileOutputStream("log.txt")
  • 追加写入:new FileOutputStream("log.txt", true)
  • 如果目标文件不存在,两种方式都会自动创建;但父目录不存在会直接抛 FileNotFoundException(不会自动建目录)
  • 写入后务必调用 flush()close(),否则缓冲区内容可能未落盘

字节流读写效率低?要不要加 BufferedInputStream/BufferedOutputStream

直接用 FileInputStream / FileOutputStream 每次只读写一个字节或小数组,系统调用频繁,性能差。加缓冲层是标准做法。

  • 包装方式:
    try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("in.bin"));
         BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("out.bin"))) {
        int b;
        while ((b = bis.read()) != -1) {
            bos.write(b);
        }
    }
  • 默认缓冲区大小是 8192 字节,够用;如需调整,可用带 int size 参数的构造函数
  • 注意:BufferedOutputStreamwrite(int) 方法仍会触发内部缓冲,但频繁单字节写仍慢;批量写(如 write(byte[], int, int))更高效

关闭流时出现 NullPointerException 怎么办

典型写法错误:把流声明在 try 块内,导致 finally 里无法访问;或流初始化失败(如文件不可读),变量仍为 null,却在 finally 中无条件调用 close()

  • 正确做法是声明在 try 外并初始化为 null,关闭前判空:
    FileInputStream fis = null;
    try {
        fis = new FileInputStream("a.txt");
        // ...
    } finally {
        if (fis != null) {
            try { fis.close(); } catch (IOException e) { /* 忽略或记录 */ }
        }
    }
  • 更简洁的方式是使用 try-with-resources(Java 7+),它自动处理 null 安全和异常抑制:
    try (FileInputStream fis = new FileInputStream("a.txt");
         FileOutputStream fos = new FileOutputStream("b.txt")) {
        // 自动 close,即使构造函数抛异常也安全
    }
  • 注意:try-with-resources 要求资源实现 AutoCloseableFileInputStreamFileOutputStream 都满足

实际用字节流做文件拷贝时,最容易被忽略的是异常处理粒度——不能只在外层 catch 一个 IOException 就完事,要区分是读失败、写失败还是关闭失败;而 try-with-resources 能帮你挡住大部分底层细节,但得清楚它不捕获业务逻辑异常。