当前位置: 首页 > article >正文

Java--IO流详解(中)--字节流

目录

字节流

字节流的核心类

InputStream 类的核心方法

1.close()

2.mark(int readAheadLimit)

3.markSupported()

4.read()

5.read(byte[] b)

6.read(byte[] b, int off, int len)

7.reset()

8.skip(long n)

InputStream 的常用子类

1.FileInputStream

2.BufferedInputStream(高级)

3.ByteArrayInputStream

注意 :ByteArrayInputStream和BufferedInputStream的区别。

 4.ObjectInputStream

OutputStream 类的核心方法

1.close()

2.flush()

3.write(int b)

4.write(byte[] b)

5.write(byte[] b, int off, int len)

OutputStream 的常用子类

1.FileOutputStream

2.BufferedOutputStream(高级)

3.ByteArrayOutputStream

4.ObjectOutputStream

 总结序列化&反序列化

例子 


在Java--IO流详解 (上)--字符流-CSDN博客 中已经,详细介绍了有关IO流中的字符流板块以及相关方法,在此介绍另一板块字节流的定义和相关方法。

从本质上来说,字符流是基于字节流实现的。字符流在处理数据时,会先使用字节流读取字节数据,然后根据指定的字符编码将字节数据转换为字符。所以从数据的底层表示来看,字节流可以处理所有类型的数据,包括字符流所处理的文本数据。

字节流

字节流本质上和字符流是类似的。核心类自然是不同,但是核心类中包含的常用方法和相关使用定义却大差不差。

字节流的核心类

  1. InputStream:用于字节输入。

  2. OutputStream:用于字节输出。

InputStream 类的核心方法

1.close()

关闭流并释放与之关联的所有资源。

inputStream.close(); //和前面的关闭相同,通常用于有关流的相关操作后,进行关闭流处理
2.mark(int readAheadLimit)

标记流中的当前位置,以便后续可以通过 reset() 方法返回到该位置。

inputStream.mark(100); // 标记当前位置,允许向前读取最多100个字节
3.markSupported()

判断此流是否支持 mark() 操作,返回 truefalse

if (inputStream.markSupported()) {
    inputStream.mark(100);
}
4.read()

读取单个字节,返回读取的字节的整数值(将对应字节的二进制转成整数返回),如果到达流末尾则返回 -1

int b = inputStream.read();
while (b != -1) {
    System.out.print((char) b);
    b = inputStream.read();
}

/*
InputStream 是 Java 中所有字节输入流的抽象基类,它提供了基本的字节读取功能。当创建一个 InputStream 对象并关联到某个数据源(如文件、网络连接等)时,InputStream 会在内部维护一个指针,这个指针初始指向数据源的起始位置。
每次调用 read() 方法时,InputStream 会从当前指针所指向的位置读取一个字节的数据,并将该字节转换为对应的 int 类型值返回。然后,指针会自动向后移动一个字节的位置,以便下一次调用 read() 方法时能读取到下一个字节。
*/
5.read(byte[] b)

将字节读入数组,返回实际读取的字节数。

byte[] buffer = new byte[1024];
int bytesRead = inputStream.read(buffer);  //读入数组并且返回实际读取的有效字节个数
System.out.println(new String(buffer, 0, bytesRead));  //这里的bytesRead还可以是其他小于bytesRead的数(根据用户具体想要的有效长度而定)
6.read(byte[] b, int off, int len)

将字节读入数组的某一部分,返回实际读取的字节数(该方法可以将读取的数据存放到字节数组的指定位置,而不是只能从数组起始位置开始存放,这为数据的组织和处理提供了极大的灵活性。)

byte[] buffer = new byte[1024];
int bytesRead = inputStream.read(buffer, 0, 512);
System.out.println(new String(buffer, 0, bytesRead)); //用法本质还是放bytes[],只是更加灵活了
7.reset()

重置流,使流返回到最近一次调用 mark() 方法时的位置。

inputStream.reset(); // 返回到标记的位置
8.skip(long n)

跳过指定数量的字节。

inputStream.skip(10); // 跳过10个字节

InputStream 的常用子类

1.FileInputStream

从文件中读取字节流。

FileInputStream fileInputStream = new FileInputStream("example.txt");
//一般不会单独使用,经常作为其他常用子类的使用基础
2.BufferedInputStream(高级)

提供缓冲功能,提高读取效率。

  • 工作原理:当调用 bufferedInputStream 的 read 方法时,它首先会尝试从内部缓冲区中读取数据。如果缓冲区中有数据,就直接从缓冲区中取出数据返回;如果缓冲区为空,BufferedInputStream 会从底层的 FileInputStream 批量读取一定数量的字节数据到内部缓冲区中,然后再从缓冲区提供数据给调用者。这样做可以减少与底层数据源(如文件系统)的交互次数,提高读取性能。

【注:注意甄别byte[] buffer和bufferedInputStream内部自带的缓冲区的本质不同

BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("example.txt"));
byte[] buffer = new byte[1024]; //这个手动创建的字节数组 buffer 并不是 BufferedInputStream 的缓冲区,它是用于存储从 BufferedInputStream 读取的数据的临时容器。
int bytesRead;
while ((bytesRead = bufferedInputStream.read(buffer)) != -1) {
    System.out.write(buffer, 0, bytesRead);
}

/*
这里的操作其实本质就是
int bytesRead=bufferedInputStream.read(buffer);
while ( bytesRead != -1) {
    System.out.write(buffer, 0, bytesRead);
}
只是这么写更加简便
*/
3.ByteArrayInputStream

从字节数组中读取字节流。

注意 :ByteArrayInputStream和BufferedInputStream的区别。

①数据源不同

  • ByteArrayInputStream:数据源为内存中的字节数组。
  • BufferedInputStream:需包装其他输入流,数据源是底层输入流关联的资源(如文件、网络)。

②缓冲机制

  • ByteArrayInputStream:无专门缓冲机制,直接从字节数组读取。
  • BufferedInputStream:有缓冲功能,内部缓冲区减少与底层数据源交互。

③使用场景

  • ByteArrayInputStream:处理内存中已有字节数组数据。
  • BufferedInputStream:提升从慢速数据源(文件、网络)读取数据的效率。

④资源管理

  • ByteArrayInputStream:无需额外资源管理,不用关闭(因为本质是在和内存交互,并不涉及数据源的交互)。
  • BufferedInputStream:使用完需关闭以释放资源。
byte[] data = {72, 101, 108, 108, 111}; // "Hello" in ASCII
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
byte[] buffer = new byte[10];
int bytesRead;
while ((bytesRead = byteArrayInputStream.read(buffer)) != -1) {
    System.out.write(buffer, 0, bytesRead);
}
 4.ObjectInputStream

用于反序列化对象。

反序列化是序列化的逆过程,它将字节流重新转换为 Java 对象,恢复对象的状态和类的元数据。由于 readObject 方法返回的是 Object 类型,所以需要进行强制类型转换可以再自行转换。

ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("object.dat"));
Object obj = objectInputStream.readObject();
  • ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("object.dat"));:创建了一个 ObjectInputStream 对象,它用于从文件 object.dat 中读取字节流,并将其反序列化为 Java 对象。
  • Object obj = objectInputStream.readObject();:调用 readObject() 方法从输入流中读取字节序列,并将其反序列化为一个 Object 类型的对象。由于反序列化的结果是 Object 类型,可能需要根据实际情况进行强制类型转换。

反序列化使得程序能够恢复之前序列化保存的对象状态(这也是反序列化的根本目的)。

OutputStream 类的核心方法

1.close()

关闭流并释放与之关联的所有资源。

outputStream.close();
2.flush()

刷新流的缓冲区,确保所有数据都被写入目标。

outputStream.flush();
3.write(int b)

写入单个字节。

outputStream.write('a');
4.write(byte[] b)

写入字节数组。

byte[] data = {72, 101, 108, 108, 111}; // "Hello" in ASCII
outputStream.write(data);
5.write(byte[] b, int off, int len)

写入字节数组的某一部分。

byte[] data = {72, 101, 108, 108, 111}; // "Hello" in ASCII
outputStream.write(data, 0, 5); // 写入整个数组

OutputStream 的常用子类

1.FileOutputStream

将字节写入文件。

FileOutputStream fileOutputStream = new FileOutputStream("example.txt");
fileOutputStream.write("Hello World".getBytes());
fileOutputStream.close();
2.BufferedOutputStream(高级)

提供缓冲功能,提高写入效率。

BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("example.txt")); //bufferedOutputStream 的缓冲区在这里new文件时就存在在其内部了
bufferedOutputStream.write("Hello".getBytes());
bufferedOutputStream.write("\n".getBytes()); // 写入换行符
bufferedOutputStream.write("World".getBytes());
bufferedOutputStream.close();
3.ByteArrayOutputStream

将字节写入字节数组。

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byteArrayOutputStream.write("Hello".getBytes());
byteArrayOutputStream.write(" World".getBytes());
byte[] data = byteArrayOutputStream.toByteArray();
System.out.println(new String(data)); // 输出 "Hello World"

注意,这里看起来貌似也具备缓冲作用,但是这里的“缓冲”和BufferedOutputStream中的缓冲机制和作用完全不同(所以ByteArrayOutputSteam本身并不具备缓冲功能)

  • ByteArrayOutputStream(可以动态变化大小):主要用于在内存中临时存储字节数据,方便后续对这些数据进行统一处理,比如将多个小的字节数据合并成一个大的字节数组,或者将这些数据转换为字符串等。它并不直接与外部的文件、网络等资源交互
  • BufferedOutputStream(大小固定):主要是为了提高向外部资源(如文件、网络连接等)写入数据的效率。它通过在内存中设置一个缓冲区,减少与底层资源的频繁交互,将多次小数据的写入操作合并为一次大数据的写入操作。
4.ObjectOutputStream

用于序列化对象。

序列化是指将 Java 对象转换为字节序列的过程,在这个过程中,对象的状态(即对象的属性值)和类的元数据(如类名、类的继承关系等)都会被转换为字节流。这些字节流可以被存储到文件、通过网络传输或者在内存中进行持久化保存。

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

public class SerializationExample {
    public static void main(String[] args) {
        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("object.dat"))) {
            // 创建一个 String 对象
            String data = new String("Hello World");
            // 将对象写入 ObjectOutputStream
            objectOutputStream.writeObject(data);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 总结序列化&反序列化

  • 序列化ObjectOutputStream 的主要作用是将 Java 对象转换为字节序列,以便进行持久化存储(如保存到文件)或网络传输。
  • 反序列化ObjectInputStream 的主要作用是将之前序列化生成的字节序列重新转换为 Java 对象,恢复对象的原始状态,使得程序可以继续使用这些对象。

例子 

经过上面的讲解,通过BufferedInputStream 和 BufferedOutputStream 就可以实现文件复制。即:BufferedInputStream 用于从源文件中读取数据,BufferedOutputStream 用于将读取的数据写入到目标文件中,这样就完成了文件的复制操作。

import java.io.*;

public class FileCopyExample {
    public static void main(String[] args) {
        String sourceFilePath = "source.txt"; // 源文件路径
        String destFilePath = "destination.txt"; // 目标文件路径

        try (
                // 创建 BufferedInputStream 用于读取源文件
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFilePath));
                // 创建 BufferedOutputStream 用于写入目标文件
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath))
        ) {
            byte[] buffer = new byte[1024]; // 定义缓冲区
            int bytesRead;

            // 从源文件读取数据到缓冲区,并将缓冲区的数据写入目标文件
            while ((bytesRead = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }

            System.out.println("文件复制成功!");
        } catch (IOException e) {
            System.err.println("文件复制过程中出现错误: " + e.getMessage());
        }
    }
}

http://www.kler.cn/a/546518.html

相关文章:

  • C++ Primer 函数基础
  • 网络编程(tcp线程池)
  • Baumer工业相机堡盟工业相机如何通过BGAPI SDK实现一次触发控制三个光源开关分别采集三张图像(C#)
  • 信息安全管理(3):网络安全
  • TCP可靠传输的ARQ协议
  • C++ 中的继承与派生
  • 单调队列及其相关题解
  • 华为2288H V5服务器无法启动问题处理
  • 【2023 K8s CKA】云原生K8s管理员认证课-零基础 考题更新免费学-全新PSI考试系统
  • 学习京东写测试用例
  • Linux 服务器部署deepseek
  • 2025 docker可视化管理面板DPanel的安装
  • 探索后端开发中的异步API:基于Resilience4j与Reactive Programming的高性能设计
  • AI Agent未来走向何方?
  • 数值积分:通过复合梯形法计算
  • 网络将内网服务转换到公网上
  • Spring Boot 的约定优于配置,你的理解是什么?
  • 域森林基础及环境搭建
  • Kubernetes 面试题精解:从入门到进阶
  • JAVA实战开源项目:宠物咖啡馆平台(Vue+SpringBoot) 附源码