【Java】字节流详解

大家好,本篇继续更新 Java IO 相关内容,今天带大家系统学习Java 字节流。字节流是 IO 流中最基础也最常用的一类,专门用来处理一切二进制文件,像图片、视频、文档、普通文本都能使用字节流完成读写,也是文件复制、文件上传下载的核心基础。

一、字节流整体概述

Java 中字节流分为两大顶层抽象父类:

  • 字节输出流java.io.OutputStream,负责把内存中的数据写出到文件/网络等目标位置
  • 字节输入流java.io.InputStream,负责从数据源(文件、网络)读取数据到内存

两个都是抽象类,日常开发我们主要使用它们的文件专属子类:FileOutputStream(文件字节输出流)、FileInputStream(文件字节输入流)。

二、字节输出流 OutputStream & FileOutputStream

1. 顶层父类 OutputStream 核心方法

OutputStream 定义了字节输出流的通用行为,所有子类都会继承这些方法:

  1. void close():关闭流资源,IO 流使用完毕必须关闭,释放系统资源
  2. void flush():刷新缓冲区,强制把缓冲区数据写出到目标
  3. void write(int b):写入单个字节
  4. void write(byte[] b):写入整个字节数组
  5. void write(byte[] b, int off, int len):从字节数组下标 off 开始,写入 len 个字节

2. FileOutputStream 构造方法

FileOutputStream 是文件字节输出流,专门操作本地文件,常用构造有四类:

  1. FileOutputStream(String fileName):根据文件路径创建流,文件不存在则新建,存在则直接覆盖原内容
  2. FileOutputStream(File file):根据 File 文件对象创建流
  3. FileOutputStream(String fileName, boolean append):追加模式,append=true 时在文件末尾续写,不覆盖
  4. FileOutputStream(File file, boolean append):文件对象 + 追加模式

3. 代码示例:基础写入数据

示例1:单个字节写入文件

import java.io.FileOutputStream;
import java.io.IOException;

public class OutputStreamDemo {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        try {
            // 创建文件字节输出流,覆盖原有内容
            fos = new FileOutputStream("test.txt");
            // 写入单个字节
            fos.write(97); // 对应字符 a
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭流
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

示例2:字节数组写入 & 分段写入

import java.io.FileOutputStream;
import java.io.IOException;

public class WriteByteArrayDemo {
    public static void main(String[] args) {
        try (FileOutputStream fos = new FileOutputStream("test.txt")) {
            byte[] bytes = "Hello Java IO".getBytes();
            // 写入整个字节数组
            fos.write(bytes);

            // 分段写入:从下标0开始,取5个字节
            fos.write(bytes, 0, 5);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这里我使用了 JDK7+ 的try-with-resources语法,流会自动关闭,简化代码。

示例3:文件追加写入 + 换行

默认流会覆盖文件内容,如果需要续写,开启追加模式即可。
补充一点:不同系统换行符不同,Windows 是 \r\n,Linux/Mac 是 \n

import java.io.FileOutputStream;
import java.io.IOException;

public class AppendWriteDemo {
    public static void main(String[] args) {
        // 第二个参数 true:开启追加模式
        try (FileOutputStream fos = new FileOutputStream("test.txt", true)) {
            // 换行 + 追加内容
            fos.write("\r\n".getBytes());
            fos.write("追加一行新内容".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

三、字节输入流 InputStream & FileInputStream

1. 顶层父类 InputStream 核心方法

InputStream 是所有字节输入流的抽象父类,核心方法:

  1. void close():关闭流,释放资源
  2. int read():读取单个字节,读到文件末尾返回 -1
  3. int read(byte[] b):读取数据存入字节数组,返回实际读取的字节个数,读到末尾返回 -1

2. FileInputStream 构造方法

专门读取文件的字节输入流,常用构造:

  1. FileInputStream(String name):根据文件路径创建输入流
  2. FileInputStream(File file):根据 File 对象创建输入流

注意:如果文件不存在,FileInputStream 会直接抛出文件找不到异常。

3. 代码示例:读取文件内容

示例1:单字节循环读取

逐个字节读取文件,直到返回 -1 结束:

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

public class ReadSingleByteDemo {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("test.txt")) {
            int b;
            // 循环读取,b=-1 代表读取完毕
            while ((b = fis.read()) != -1) {
                System.out.print((char) b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

示例2:字节数组批量读取(效率更高)

单字节读取效率很低,实际开发统一使用字节数组批量读取

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

public class ReadByteArrayDemo {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("test.txt")) {
            // 定义缓冲区数组,大小自定义
            byte[] buffer = new byte[1024];
            int len; // 记录每次实际读取的字节数
            while ((len = fis.read(buffer)) != -1) {
                // 读取多少,就转换多少
                String content = new String(buffer, 0, len);
                System.out.print(content);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

四、综合实战:字节流实现文件复制

字节流最大的优势就是通用,文本、图片、视频、压缩包都能复制。下面演示一张图片的完整复制案例,也是工作中最常用的场景。

代码示例:文件复制

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

public class FileCopyDemo {
    public static void main(String[] args) {
        // 源文件路径、目标文件路径
        String srcPath = "source.jpg";
        String destPath = "copy.jpg";

        try (
                // 输入流:读取源文件
                FileInputStream fis = new FileInputStream(srcPath);
                // 输出流:写入目标文件
                FileOutputStream fos = new FileOutputStream(destPath)
        ) {
            byte[] buffer = new byte[1024];
            int len;
            // 边读边写
            while ((len = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
            }
            System.out.println("文件复制完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

执行后就能在目录下得到一份和原图完全一致的副本,这套代码可以通用在所有类型文件上。

五、内容小结

  1. 字节流两大抽象父类:InputStream(输入)、OutputStream(输出),针对所有二进制数据。
  2. 文件专属实现类:FileInputStream 读文件,FileOutputStream 写文件。
  3. FileOutputStream 默认覆盖文件,传入 append=true 可开启追加写入;不同系统换行符有区别。
  4. 读写优先使用字节数组缓冲区,相比单字节读写,性能提升巨大。
  5. 流资源必须关闭,推荐使用 try-with-resources 语法自动释放资源。
  6. 字节流可实现任意类型文件的复制,是 IO 基础中的重点。

以上就是 Java 字节流的全部核心知识点与实战代码,下一篇我会继续讲解字符流、缓冲流等进阶 IO 内容,感兴趣的朋友可以持续关注~

Tags:

发表回复

Your email address will not be published. Required fields are marked *.

*
*