Java 中的 FileInputStream 和 FileOutputStream 是用于文件读写的字节流类,直接操作文件的二进制字节数据

一、核心概念:字节流 vs 字符流

  • ​字节流​​:以字节(byte,8位)为单位读写数据,不涉及字符编码,直接操作文件的二进制内容。适合处理所有类型的文件(包括二进制和文本)。
  • ​字符流​​:以字符(char,16位)为单位读写数据,依赖字符编码(如 UTF-8、GBK),适合处理纯文本文件的高级操作(如自动转换换行符)。

FileInputStream(输入流)和 FileOutputStream(输出流)是字节流的典型代表,直接与文件系统交互。

二、FileInputStream:文件输入流(读取文件)

1. 构造方法

用于创建指向文件的输入流,常见方式:

  • FileInputStream(String name):通过文件路径字符串创建流(如 "data.txt")。
  • FileInputStream(File file):通过 File 对象创建流(更灵活,支持 File 的方法操作文件)。

​注意​​:若文件不存在或无读取权限,抛出 FileNotFoundException

2. 核心方法
方法 说明
int read() 读取一个字节(返回值为 0~255 的整数;文件末尾返回 -1)。
int read(byte[] b) 读取最多 b.length 字节到数组 b 中,返回实际读取的字节数(末尾返回 -1)。
int read(byte[] b, int off, int len) 从数组 b 的 off 位置开始,读取最多 len 字节(off 起始位置,len 最大长度)。
void close() 关闭流,释放系统资源(必须调用,否则资源泄漏)。
3. 关键特性
  • ​单字节读取效率低​​:频繁调用 read() 会触发多次磁盘IO,适合小文件;大文件需用缓冲字节数组优化。
  • ​资源管理​​:必须显式关闭流(或使用 try-with-resources 自动关闭)。

三、FileOutputStream:文件输出流(写入文件)

1. 构造方法

用于创建指向文件的输出流,常见方式:

  • FileOutputStream(String name):覆盖模式(若文件存在则清空内容后写入)。
  • FileOutputStream(String name, boolean append)append=true 时为追加模式(在文件末尾写入);false(默认)为覆盖模式。
  • FileOutputStream(File file):同上(覆盖模式)。
  • FileOutputStream(File file, boolean append):同上(追加模式)。

​注意​​:若文件不存在且父目录不存在,抛出 FileNotFoundException;若文件存在但无写入权限,也抛出异常。

2. 核心方法
方法 说明
void write(int b) 写入一个字节(仅 b 的低8位有效)。
void write(byte[] b) 将数组 b 的所有字节写入文件。
void write(byte[] b, int off, int len) 将数组 b 中从 off 开始的 len 个字节写入文件。
void flush() 强制刷新缓冲区(将缓冲区数据写入磁盘,非必要时可省略,关闭流时自动刷新)。
void close() 关闭流,释放系统资源(必须调用)。
3. 关键特性
  • ​覆盖与追加​​:默认覆盖模式会清空原文件内容,追加模式需显式设置 append=true
  • ​缓冲优化​​:写入时数据先存入缓冲区,满一定大小后自动写入磁盘;手动 flush() 可强制写入(如需要实时保存)。

四、典型示例

示例1:单字节读取文本文件(基础用法)

读取 input.txt 并逐个字节打印(假设是文本文件)。

java
复制
 
  

import java.io.FileInputStream;
import java.io.IOException;

public class ReadFileDemo {
    public static void main(String[] args) {
        // 使用 try-with-resources 自动关闭流(Java 7+)
        try (FileInputStream fis = new FileInputStream("input.txt")) {
            int byteRead;
            // 单字节读取(效率低,仅演示)
            while ((byteRead = fis.read()) != -1) {
                System.out.print((char) byteRead); // 转换为字符打印
            }
        } catch (IOException e) {
            e.printStackTrace(); // 处理异常(如文件不存在)
        }
    }
}

示例2:缓冲读取大文件(性能优化)

使用8KB缓冲区读取大文件(如图片、视频),减少磁盘IO次数。

java
复制
import java.io.FileInputStream;
import java.io.IOException;

public class BufferedReadDemo {
    public static void main(String[] args) {
        String filePath = "large_image.jpg";
        byte[] buffer = new byte[8192]; // 8KB 缓冲区(常用大小)

        try (FileInputStream fis = new FileInputStream(filePath)) {
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                // 处理读取到的字节(如写入另一个文件、解析数据)
                processBuffer(buffer, bytesRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 示例:打印前10个字节的十六进制值(调试用)
    private static void processBuffer(byte[] buffer, int length) {
        for (int i = 0; i < Math.min(10, length); i++) {
            System.out.printf("%02X ", buffer[i]); // 十六进制格式化
        }
        System.out.println();
    }
}
示例3:覆盖模式写入文件(默认)

将字符串写入 output.txt,覆盖原有内容。

java
复制
import java.io.FileOutputStream;
import java.io.IOException;

public class WriteFileDemo {
    public static void main(String[] args) {
        String content = "Hello, FileOutputStream!";
        byte[] bytes = content.getBytes(); // 转换为字节数组(默认平台编码)

        // 覆盖模式(默认):清空文件后写入
        try (FileOutputStream fos = new FileOutputStream("output.txt")) {
            fos.write(bytes); // 写入整个字节数组
            fos.flush();      // 强制刷新(可选,关闭流时自动刷新)
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
示例4:追加模式写入文件

在 log.txt 末尾追加新日志(不覆盖原有内容)。

java
复制
import java.io.FileOutputStream;
import java.io.IOException;

public class AppendFileDemo {
    public static void main(String[] args) {
        String log = "\n[2023-10-01] 系统启动完成"; // 新日志(带换行)
        byte[] bytes = log.getBytes();

        // 追加模式(append=true):在文件末尾写入
        try (FileOutputStream fos = new FileOutputStream("log.txt", true)) {
            fos.write(bytes);
            fos.flush(); // 确保立即写入(如日志需要实时保存)
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

五、注意事项与常见问题

1. 资源释放
  • ​必须关闭流​​:FileInputStream 和 FileOutputStream 持有操作系统资源(文件描述符),未关闭会导致资源泄漏,最终可能无法打开文件。
  • ​推荐 try-with-resources​:自动关闭实现 AutoCloseable 接口的流(Java 7+),避免手动 finally 块遗漏。
2. 字符编码问题
  • FileInputStream/FileOutputStream 直接操作字节,不处理字符编码。若需读写文本并指定编码(如UTF-8),需结合 InputStreamReader/OutputStreamWriter
    java
    复制
    // 读取文本(指定UTF-8编码)
    try (FileInputStream fis = new FileInputStream("input.txt");
         InputStreamReader isr = new InputStreamReader(fis, "UTF-8")) {
        int charRead;
        while ((charRead = isr.read()) != -1) {
            System.out.print((char) charRead);
        }
    }
    
    // 写入文本(指定GBK编码)
    try (FileOutputStream fos = new FileOutputStream("output.txt");
         OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK")) {
        osw.write("你好,世界"); // 按GBK编码写入
    }
3. 文件覆盖与追加
  • 默认模式(无 append 参数或 append=false)会清空原文件内容;追加模式(append=true)保留原内容并在末尾写入。
4. 异常处理
  • 所有IO方法(如 read()write()close())可能抛出 IOException(或其子类,如 FileNotFoundException),需显式处理(捕获或声明抛出)。
5. 大文件处理
  • 避免一次性加载大文件到内存,使用缓冲字节数组分块读写(如示例2、4中的8KB缓冲区)。

六、总结

​特性​ FileInputStream FileOutputStream
​功能​ 从文件读取字节数据 向文件写入字节数据
​构造关键​ 文件路径或 File 对象 文件路径或 File 对象,可选追加模式
​核心方法​ read()read(byte[]) write(int)write(byte[])
​性能优化​ 缓冲字节数组(减少IO次数) 缓冲字节数组(减少IO次数)
​适用场景​ 读取二进制/文本文件的底层字节 写入二进制/文本文件的底层字节

​最佳实践​​:

  • 优先使用 try-with-resources 管理流资源。
  • 读写文本文件时,显式指定字符编码(避免平台差异乱码)。
  • 大文件操作时,使用缓冲字节数组(如8KB~64KB)提升性能。
  • 明确区分覆盖模式(默认)和追加模式(append=true)的使用场景。

你可能感兴趣的:(java,开发语言)