6.2字节流

在 Java 中,IO(Input/Output)流是用于处理数据输入输出的核心机制,它提供了一种统一的方式来读写不同类型的数据。

1. 基本概念

  • 流(Stream):是一种抽象概念,代表数据的传输通道。数据可以从源头(如文件、网络)通过流传输到目的地。
  • 输入流(InputStream/Reader):用于从数据源读取数据。
  • 输出流(OutputStream/Writer):用于向目标写入数据。
  • Java IO 的分类
    • 按数据类型:字节流(处理二进制数据)和字符流(处理文本数据)。
    • 按功能:节点流(直接操作数据源)和处理流(增强节点流的功能)。

2. 字节流(Byte Stream)

字节流以字节(8 位)为单位处理数据,适用于所有类型的数据(如图片、视频、二进制文件等)。

核心类
  • InputStream:所有字节输入流的抽象基类。
  • OutputStream:所有字节输出流的抽象基类。
常见实现类
  • FileInputStream/FileOutputStream:用于读写文件。
  • ByteArrayInputStream/ByteArrayOutputStream:用于读写内存中的字节数组。
  • BufferedInputStream/BufferedOutputStream:带缓冲的输入输出流,提高效率。
  • DataInputStream/DataOutputStream:用于读写基本数据类型(如 int、double 等)。
  • ObjectInputStream/ObjectOutputStream:序列化对象
  • PrintStream:格式化输出,如 System.out

3. 字节流的核心方法

InputStream 常用方法
  • int read():读取单个字节,返回 0-255 的整数;若到达流末尾,返回 -1。
  • int read(byte[] b):读取最多 b.length 个字节到数组 b 中,返回实际读取的字节数;若到达流末尾,返回 -1。
  • int read(byte[] b, int off, int len):读取最多 len 个字节到数组 boff 位置开始的部分。
  • void close():关闭流,释放资源。
OutputStream 常用方法
  • void write(int b):写入单个字节(参数 b 的低 8 位)。
  • void write(byte[] b):写入整个字节数组。
  • void write(byte[] b, int off, int len):写入字节数组 b 中从 off 开始的 len 个字节。
  • void flush():刷新缓冲区,确保数据写入目标。
  • void close():关闭流,释放资源(关闭前会自动刷新)。

4. 字节流的典型应用场景

文件复制
java 复制代码
import java.io.*;

public class FileCopyExample {
    public static void main(String[] args) {
        try (InputStream in = new FileInputStream("source.jpg");
             OutputStream out = new FileOutputStream("target.jpg")) {
            
            byte[] buffer = new byte[1024]; // 缓冲区大小
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
使用缓冲流提高效率
java 复制代码
import java.io.*;

public class BufferedStreamExample {
    public static void main(String[] args) {
        try (BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream("source.zip"));
             BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("target.zip"))) {
            
            int b;
            while ((b = bis.read()) != -1) {
                bos.write(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
读写基本数据类型
java 复制代码
import java.io.*;

public class DataStreamExample {
    public static void main(String[] args) {
        try (DataOutputStream dos = new DataOutputStream(
                new FileOutputStream("data.bin"))) {
            
            dos.writeInt(123);
            dos.writeDouble(3.14);
            dos.writeUTF("Hello, World!");
        } catch (IOException e) {
            e.printStackTrace();
        }

        try (DataInputStream dis = new DataInputStream(
                new FileInputStream("data.bin"))) {
            
            int num = dis.readInt();
            double pi = dis.readDouble();
            String str = dis.readUTF();
            
            System.out.println(num);    // 123
            System.out.println(pi);     // 3.14
            System.out.println(str);    // Hello, World!
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

5. PrintStream

标准输出(System.outPrintStream 类型)

PrintStream 提供了多种构造方法,常见的有:

  • PrintStream(OutputStream out):包装另一个输出流,不自动刷新。
  • PrintStream(OutputStream out, boolean autoFlush):指定是否自动刷新。
  • PrintStream(String fileName):直接输出到文件,自动刷新。
  • PrintStream(File file):输出到文件对象,自动刷新。
核心方法
  • print():打印各种数据类型(如 intStringObject 等)。
  • println():打印并换行。
  • printf():格式化输出,支持类似 C 语言的格式字符串。
  • format():与 printf() 功能相同,更符合 Java 风格。
  • checkError():检查流是否发生错误,返回 boolean
  • flush():刷新缓冲区。
  • close():关闭流。
  • printf()format() 支持格式化字符串,语法为:%[flags][width][.precision]conversion
    • %d:整数(十进制)。
    • %f:浮点数。
    • %s:字符串。
    • %c:字符。
    • %b:布尔值。
    • %n:换行符。
java 复制代码
public class PrintStreamExample {
    public static void main(String[] args) {
        PrintStream ps = System.out;
        
        // 基本打印
        ps.print("Hello");      // 输出: Hello
        ps.println(" World");  // 输出:  World (换行)
        
        // 格式化输出
        int num = 123;
        double pi = 3.14159;
        ps.printf("数字: %d, 圆周率: %.2f%n", num, pi);  // 输出: 数字: 123, 圆周率: 3.14
        
        // 日期格式化
        java.util.Date date = new java.util.Date();
        ps.printf("日期: %tF %tT%n", date, date);  // 输出: 日期: 2023-01-01 12:00:00
    }
}
重定向标准输出

通过 System.setOut(PrintStream ps) 可以重定向标准输出流。

示例:将输出写入文件
java 复制代码
import java.io.*;

public class RedirectOutput {
    public static void main(String[] args) {
        try (PrintStream ps = new PrintStream(new FileOutputStream("output.txt"))) {
            System.setOut(ps);  // 重定向标准输出
            System.out.println("This line will be written to output.txt");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

6. ByteArrayInputStream/ByteArrayOutputStream

ByteArrayInputStream
  • 继承关系 :继承自 InputStream,用于从内存字节数组读取数据。
  • 核心功能:将字节数组转换为输入流,支持按字节或字节块读取。

构造方法

  • ByteArrayInputStream(byte[] buf):使用整个字节数组创建输入流。
  • ByteArrayInputStream(byte[] buf, int offset, int length):使用字节数组的部分内容(从 offset 开始,长度为 length)。
ByteArrayOutputStream
  • 继承关系 :继承自 OutputStream,用于将数据写入内存字节数组。
  • 核心功能:动态扩展字节数组,支持将数据写入数组并获取最终结果。

构造方法

  • ByteArrayOutputStream():创建默认初始容量的输出流(32 字节)。
  • ByteArrayOutputStream(int size):创建指定初始容量的输出流。

核心方法:

ByteArrayInputStream
  • int read():读取下一个字节,返回 0-255 的整数;到达末尾返回 -1。
  • int read(byte[] b, int off, int len):读取最多 len 个字节到数组 boff 位置。
  • int available():返回剩余可读取的字节数。
  • void reset():将流的位置重置为初始位置。
  • void mark(int readAheadLimit):标记当前位置(需配合 reset() 使用)。
ByteArrayOutputStream
  • void write(int b):写入单个字节。
  • void write(byte[] b, int off, int len):写入字节数组的部分内容。
  • void writeTo(OutputStream out):将当前输出流的全部内容写入另一个输出流。
  • byte[] toByteArray():返回当前输出流的内容作为字节数组。
  • int size():返回当前输出流的大小(字节数)。
  • void reset():清空输出流,重置为初始状态。
java 复制代码
import java.io.*;

public class ByteArrayExample {
    public static void main(String[] args) {
        try {
            // 创建 ByteArrayOutputStream 写入数据
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            baos.write("Hello, World!".getBytes());
            
            // 将 ByteArrayOutputStream 的内容转换为字节数组
            byte[] data = baos.toByteArray();
            
            // 使用 ByteArrayInputStream 读取数据
            ByteArrayInputStream bais = new ByteArrayInputStream(data);
            int b;
            while ((b = bais.read()) != -1) {
                System.out.print((char) b); // 输出: Hello, World!
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

性能与注意事项

  1. 无需关闭流ByteArrayInputStreamByteArrayOutputStream 不涉及底层资源(如文件或网络),无需调用 close()
  2. 自动扩容ByteArrayOutputStream 会在数据超过容量时自动扩容,但频繁扩容可能影响性能。
  3. 线程安全:这两个类均非线程安全,多线程环境下需外部同步。
  4. 内存占用:避免处理过大的字节数组,以防内存溢出。
相关推荐
chxii1 小时前
5java集合框架
java·开发语言
老衲有点帅1 小时前
C#多线程Thread
开发语言·c#
C++ 老炮儿的技术栈2 小时前
什么是函数重载?为什么 C 不支持函数重载,而 C++能支持函数重载?
c语言·开发语言·c++·qt·算法
IsPrisoner2 小时前
Go语言安装proto并且使用gRPC服务(2025最新WINDOWS系统)
开发语言·后端·golang
Python私教2 小时前
征服Rust:从零到独立开发的实战进阶
服务器·开发语言·rust
chicpopoo2 小时前
Python打卡DAY25
开发语言·python
yychen_java2 小时前
R-tree详解
java·算法·r-tree
JANYI20183 小时前
嵌入式设计模式基础--C语言的继承封装与多态
java·c语言·设计模式
xrkhy3 小时前
反射, 注解, 动态代理
java