程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • JavaIO流

    • 初识 Java IO 流
    • 文件操作
    • 字节流
      • 1. 字节输出流
        • 1. 字节输出流入门
        • 2. 字节输出流写数据的方法
        • 1. 写单个字节:write(int b)
        • 2. 写入字节数组:write(byte[] b)
        • 3. 部分写入字节数组:write(byte[] b, int off, int len)
        • 3. 写数据的换行和追加写入
        • 1. 写数据并覆盖原有内容
        • 2. 写数据并追加到文件末尾
        • 3. 使用换行符处理不同操作系统
      • 2. 字节输入流
        • 1.字节输入流入门
        • 2. 每次读取一个字节 (逐字节读取)
        • 3. 每次读取一个字节数组 (批量读取)
        • 4. 异常的捕获处理
        • 1. JDK7版本之前处理方式 : 手动释放资源
        • 2. JDK7版本优化处理方式 : 自动释放资源
      • 3. 图片的拷贝 (字节流应用场景)
    • 字符流
    • 缓冲流
    • 节点流和处理流的区别
    • IO流 其他内容
  • IO流
  • JavaIO流
scholar
2024-08-22
目录

字节流

# 字节流

# 1. 字节输出流

字节输出流的作用是将程序中的数据以字节的形式输出到目标位置,如文件、网络连接、内存等。它主要用于处理二进制数据(如图片、音频、视频)或需要以字节为单位写入的其他数据。在 Java 中,字节输出流基于 OutputStream 抽象类,提供了一套标准的写出操作方法。

字节输出流的应用场景

  1. 文件写入: 字节输出流广泛应用于将数据写入文件。常见的实现类是 FileOutputStream,它可以将程序中的数据写入磁盘上的文件,适用于保存二进制文件(如图片、视频)和文本文件。
    • 示例:保存用户输入的数据、日志文件、生成的配置文件等。
  2. 网络通信: 在网络编程中,字节输出流用于通过网络发送数据。比如通过 Socket 类的输出流将数据发送到远程服务器,通常用于传输二进制数据或字符数据。
    • 示例:发送文件数据、图片、音频流等。
  3. 数据处理: 字节输出流可以用于处理大块的二进制数据,如对图片、音频、视频等文件进行读写操作。
    • 示例:将一段音频数据写入到文件,或将二进制图片数据传输到其他系统。
  4. 设备控制: 在一些硬件交互场景中,字节输出流可以用于将数据发送到特定设备,例如串口通信、I/O设备控制。
    • 示例:控制打印机、串口设备的输出。

# 1. 字节输出流入门

  • FileOutputStream类 :

    • OutputStream有很多子类,我们从最简单的一个子类开始。
    • java.io.FileOutputStream类是文件输出流,用于将数据写出到文件
  • 字节输出流OutputStream主要方法:

    • write(int b):将一个字节写入输出流中,b 是要写入的字节数据(0~255 的整数)。
    • write(byte[] b):将b.length个字节从指定的字节数组写入此输出流中。
    • write(byte[] b, int off, int len):将字节数组b中,从起始位置off开始,长度为len个字节的数据写入输出流中
    • close():关闭此输出流并释放与此流相关联的任何系统资源。
  • 操作步骤:

    • 创建字节输出流对象:实例化 FileOutputStream 对象。如果指定的文件不存在,Java 会自动创建该文件。如果文件已存在,默认情况下其内容会被清空(除非在构造函数中指定追加模式)。
    • 写入数据:使用 write(int b) 方法写入单个字节,或使用 write(byte[] b) 写入字节数组。数据写入后将以字节形式存储在文件中。
    • 释放资源:使用完文件输出流后,调用 close() 方法关闭流,释放与流相关联的系统资源。忘记关闭流可能导致资源泄露。
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamExample {
    public static void main(String[] args) throws IOException {
        // Step 1: 创建字节输出流对象
        // 如果指定路径的文件不存在,FileOutputStream 会自动创建这个文件(目录不存在不会自动创建)
        // 如果文件已经存在,则其原有内容会被清空(除非以追加模式创建FileOutputStream)
        FileOutputStream fos = new FileOutputStream("e:/test.txt");

        // Step 2: 写数据
        // 使用 write 方法将数据写入到文件中
        // 这里的参数是要写入的字节,所以写入的是 ASCII 码对应的字符 'a'、'b' 和 'c'
        fos.write(97);  // 写入字节97,即字符 'a'
        fos.write(98);  // 写入字节98,即字符 'b'
        fos.write(99);  // 写入字节99,即字符 'c'
        // 注意:文件中存储的是字节数据,如果打开文件查看,看到的是这些字节对应的字符

        // Step 3: 释放资源
        // 关闭文件输出流,释放与这个流相关联的系统资源
        // 不调用 close 方法可能会导致资源泄露
        fos.close();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 2. 字节输出流写数据的方法

在 Java 中,FileOutputStream 是常用的字节输出流类,主要用于将数据写入文件。字节输出流的操作方法分为写入单个字节、写入字节数组、部分写入字节数组。

  • 写单个字节:write(int b)方法,一次写入一个字节。
  • 写字节数组:write(byte[] b)方法,一次写入整个字节数组。
  • 部分写字节数组:write(byte[] b, int off, int len)方法,从字节数组的指定位置开始,写入指定长度的数据。

# 1. 写单个字节:write(int b)

方法功能:
将一个字节写入文件,参数 b 是要写入的字节(0~255)。

常见应用场景:
逐字节写入数据,如写入字符的 ASCII 码。

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

/**
 * 演示如何使用 write(int b) 方法逐字节写入数据到文件中。
 */
public class FileOutputStreamSingleByteDemo {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        try {
            // 创建 FileOutputStream 对象,准备向指定文件写入数据
            // 第二个参数 true 表示以追加形式写文件
            fos = new FileOutputStream("e:/singleByteOutput.txt", true);

            // 写入单个字节,写入的是 ASCII 码为 97 的字符 'a'
            fos.write(97);

            // 写入其他字节
            fos.write(98); // ASCII 码 98 -> 'b'
            fos.write(99); // ASCII 码 99 -> 'c'

            // 刷新输出流,确保数据写入文件
            fos.flush();

            System.out.println("单个字节写入完成。");
        } catch (IOException e) {
            // 捕获并处理可能的 IO 异常
            System.err.println("写入文件时发生错误:" + e.getMessage());
        } finally {
            // 关闭流,释放资源
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

输出效果:
在 e:/singleByteOutput.txt 文件中看到如下内容:

abc
1

# 2. 写入字节数组:write(byte[] b)

方法功能:
将整个字节数组 b 写入文件。

常见应用场景:
一次性将一段数据(如字符串、二进制内容)写入文件。

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

/**
 * 演示如何使用 write(byte[] b) 方法将整个字节数组写入文件。
 */
public class FileOutputStreamByteArrayDemo {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        try {
            // 创建 FileOutputStream 对象,准备向指定文件写入数据
            fos = new FileOutputStream("e:/byteArrayOutput.txt", true);

            // 将字符串转换为字节数组
            String str = "Hello, Scholar!";
            byte[] strBytes = str.getBytes();

            // 将整个字节数组写入文件
            fos.write(strBytes);

            // 刷新输出流,确保数据写入文件
            fos.flush();

            System.out.println("字节数组写入完成。");
        } catch (IOException e) {
            // 捕获并处理可能的 IO 异常
            System.err.println("写入文件时发生错误:" + e.getMessage());
        } finally {
            // 关闭流,释放资源
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

输出效果:
在 e:/byteArrayOutput.txt 文件中看到如下内容:

Hello, Scholar!
1

# 3. 部分写入字节数组:write(byte[] b, int off, int len)

方法功能:
从字节数组 b 中,从偏移量 off 开始,写入长度为 len 的字节到文件。

常见应用场景:
需要写入字节数组的部分数据时,如只写入字符串的一部分或跳过不需要的部分。

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

/**
 * 演示如何使用 write(byte[] b, int off, int len) 方法部分写入字节数组数据。
 */
public class FileOutputStreamPartialByteArrayDemo {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        try {
            // 创建 FileOutputStream 对象,准备向指定文件写入数据
            fos = new FileOutputStream("e:/partialByteArrayOutput.txt", true);

            // 将字符串转换为字节数组
            String str = "Hello, Scholar!";
            byte[] strBytes = str.getBytes();

            // 写入字节数组中的部分数据,从偏移量 7 开始,写入 8 个字节
            // 这将写入 "Scholar" 部分
            fos.write(strBytes, 7, 8);

            // 刷新输出流,确保数据写入文件
            fos.flush();

            System.out.println("部分字节数组写入完成。");
        } catch (IOException e) {
            // 捕获并处理可能的 IO 异常
            System.err.println("写入文件时发生错误:" + e.getMessage());
        } finally {
            // 关闭流,释放资源
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

输出效果:
在 e:/partialByteArrayOutput.txt 文件中看到如下内容:

Scholar
1

# 3. 写数据的换行和追加写入

在 Java 的字节输出流中,写入数据时经常需要处理换行和追加写入。


# 1. 写数据并覆盖原有内容

当你使用 FileOutputStream 时,如果不指定追加模式,新数据会覆盖原有文件内容。下面是一个该示例将展示如何在文件中写入数据并进行换行操作。

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

/**
 * 示范使用 FileOutputStream 进行文件写入时实现换行的操作。
 */
public class FileOutputStreamOverwriteDemo {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        try {
            // 创建 FileOutputStream 对象,未启用追加模式(默认),每次运行将覆盖原文件内容
            fos = new FileOutputStream("e:/overwriteOutput.txt");

            // 写入单个字节数据,这里写入的 ASCII 码 97 对应字符 'a'
            fos.write(97);

            // 写入换行符,使用系统特定的换行符(Windows: "\r\n")
            fos.write("\r\n".getBytes());

            // 继续写入其他数据并进行换行
            fos.write(98); // 'b'
            fos.write("\r\n".getBytes());
            fos.write(99); // 'c'
            fos.write("\r\n".getBytes());

            // 数据写入完毕后,刷新输出流
            fos.flush();

            System.out.println("数据已写入并覆盖原文件内容。");

        } catch (IOException e) {
            // 处理可能的 IO 异常
            System.err.println("文件写入时发生错误:" + e.getMessage());
        } finally {
            // 关闭流,释放资源
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

输出效果:
每次运行代码时,文件 e:/overwriteOutput.txt 中的内容会被新的数据覆盖,内容如下:

a
b
c
1
2
3

# 2. 写数据并追加到文件末尾

如果你希望新写入的内容追加到已有文件内容的末尾,而不是覆盖原内容,需要启用追加模式。追加模式通过构造方法中的第二个参数来设置。

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

/**
 * 示范使用 FileOutputStream 进行追加写入的操作。
 */
public class FileOutputStreamAppendDemo {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        try {
            // 创建 FileOutputStream 对象,启用追加模式
            fos = new FileOutputStream("e:/appendOutput.txt", true);

            // 写入单个字节数据,并进行换行操作
            fos.write(97); // 'a'
            fos.write("\r\n".getBytes());

            fos.write(98); // 'b'
            fos.write("\r\n".getBytes());

            fos.write(99); // 'c'
            fos.write("\r\n".getBytes());

            // 数据写入完毕后,刷新输出流
            fos.flush();

            System.out.println("数据已追加写入文件末尾。");

        } catch (IOException e) {
            // 处理可能的 IO 异常
            System.err.println("文件写入时发生错误:" + e.getMessage());
        } finally {
            // 关闭流,释放资源
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

输出效果:
文件 e:/appendOutput.txt 中的内容会随着每次运行代码不断追加,如下:

a
b
c
a
b
c
1
2
3
4
5
6

# 3. 使用换行符处理不同操作系统

不同操作系统的换行符可能不同,例如:

  • Windows: \r\n
  • Linux: \n
  • macOS(旧版): \r

为了保证跨平台的兼容性,可以在写入换行符时动态适配操作系统的换行符。

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

/**
 * 示范如何根据操作系统动态处理换行符。
 */
public class FileOutputStreamCrossPlatformDemo {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        try {
            // 创建 FileOutputStream 对象,启用追加模式
            fos = new FileOutputStream("e:/crossPlatformOutput.txt", true);

            // 获取系统的换行符
            String lineSeparator = System.getProperty("line.separator");

            // 写入数据并动态适配系统换行符
            fos.write("Line 1".getBytes());
            fos.write(lineSeparator.getBytes());

            fos.write("Line 2".getBytes());
            fos.write(lineSeparator.getBytes());

            fos.write("Line 3".getBytes());
            fos.write(lineSeparator.getBytes());

            // 数据写入完毕后,刷新输出流
            fos.flush();

            System.out.println("数据已根据操作系统换行符写入文件。");

        } catch (IOException e) {
            // 处理可能的 IO 异常
            System.err.println("文件写入时发生错误:" + e.getMessage());
        } finally {
            // 关闭流,释放资源
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

输出效果:
文件 e:/crossPlatformOutput.txt 中的内容根据操作系统的换行符进行分行,如下(在 Windows 中):

Line 1
Line 2
Line 3
1
2
3

总结

  1. 写入单个字节并换行:使用 write(int b) 方法写入单个字节,并通过 write("\r\n".getBytes()) 进行换行。
  2. 追加写入:启用追加模式后,新的数据会添加到文件末尾,而不覆盖原内容。
  3. 跨平台换行符:动态获取系统换行符,确保程序在不同操作系统上表现一致。

# 2. 字节输入流

字节输入流的作用是将外部数据源中的数据以字节的形式读入程序。它主要用于读取二进制数据,如文件、网络数据流、图片、音频、视频等。在 Java 中,字节输入流基于 InputStream 抽象类,提供了一套标准的读取操作方法。

字节输入流的应用场景

  1. 文件读取: 字节输入流常用于从文件中读取数据,特别适合读取二进制文件,如图片、音频、视频文件等。常见的实现类是 FileInputStream,它可以从文件中逐字节读取数据。
    • 示例:读取配置文件、加载图片文件、读取二进制数据等。
  2. 网络通信: 在网络编程中,字节输入流用于接收从网络传输的数据。例如,通过 Socket 类的输入流从远程服务器接收数据,适用于接收二进制数据或字符数据。
    • 示例:接收文件数据、图片、音频流等。
  3. 数据处理: 字节输入流可以用于处理和解析大块的二进制数据,适合从外部源读取复杂格式的数据流。
    • 示例:读取和解析音频文件、视频文件,或从压缩文件中解压数据。
  4. 设备读取: 在一些硬件交互场景中,字节输入流可以用于从特定设备读取数据,例如从串口设备或其他输入设备获取数据。
    • 示例:从传感器设备读取数据,从打印机接收状态信息等。

InputStream类有很多的实现子类,下面列举了一些比较常用的:

image-20230312151328362

详细说明一下上图中的类:

1.InputStream: InputStream是所有字节输入流的抽象基类,前面说过抽象类不能被实例化,实际上是作为模板而存在的,为所有实现类定义了处理输入流的方法。

2.Fi1eInputSream:文件输入流,一个非常重要的字节输入流,用于对文件进行读取操作。

3.PipedInputStream:管道字节输入流,能实现多线程间的管道通信。

4.ByteArrayInputStream:字节数组输入流,从字节数组(byte)中进行以字节为单位的读取,也就是将资源文件都以字节的形式存入到该类中的字节数组中去。

5.FilterInputStream:装饰者类,具体的装饰者继承该类,这些类都是处理类,作用是对节点类进行封装,实现一些特殊功能。

6.DataInputStream:数据输入流,它是用来装饰其它输入流,作用是"允许应用程序以与机器无关方式从底层输入流中读取基本Java数据类型。

7.BufferedInputStream:缓冲流,对节点流进行装饰,内部会有一个缓存区,用来存放字节,每次都是将缓存区存满然后发送,而不是一个字节或两个字节这样发送,效率更高。

8.ObjectInputstream:对象输入流,用来提供对基本数据或对象的持久存储。通俗点说,也就是能直接传输对象,通常应用在反序列化中。它也是一种处理流,构造器的入参是一个InputStream的实例对象。

# 1.字节输入流入门

  • 字节输入流类

    • InputStream类 : 字节输入流最顶层的类 , 抽象类
    • FileInputStream类 : FileInputStream extends InputStream
  • 字节输入流InputStream主要方法:

    • read():从输入流中读取一个字节的数据。当达到文件末尾时,返回-1。
    • read(byte[] b):从输入流中读取b.length个字节的数据并存储到字节数组b中。
    • read(byte[] b, int off, int len):从输入流中读取最多len个字节的数据,将其存储在数组b中,off参数表示数组b中的起始位置。
    • close():关闭输入流并释放与该流相关联的所有资源。
  • 操作步骤:

    • 创建输入流对象:确定要读取的文件路径,并实例化一个FileInputStream对象。注意,如果文件不存在,将抛出FileNotFoundException。
    • 读取数据:使用read()方法从文件中逐字节读取数据。read()方法每次调用返回一个字节的数据,并自动移动到下一个字节。当文件结束时,read()方法返回-1。
    • 释放资源:完成数据读取后,应使用close()方法关闭流,以释放与流相关的系统资源。

代码示例:单字节读取

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

/**
 * 示例:使用 FileInputStream 读取单个字节的数据。
 */
public class FileInputStreamDemo1 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            // 创建字节输入流对象,指定要读取的文件路径
            fis = new FileInputStream("E:/a.txt");

            // 使用 read 方法读取文件中的一个字节
            int byteData = fis.read();  // read 方法返回读取到的字节,如果达到文件末尾返回 -1
            System.out.println("读取到的字节对应字符:" + (char) byteData);  // 将读取到的字节转换为字符打印

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 完成读取操作后关闭输入流,释放资源
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

image-20240319211353609

# 2. 每次读取一个字节 (逐字节读取)

  1. 创建FileInputStream对象:指定需要读取的文件路径,创建一个FileInputStream实例。
  2. 读取数据:通过循环调用read()方法,从文件中连续读取多个字节。read()方法在读取结束时返回-1,可以作为循环结束的条件。
  3. 关闭流:使用完毕后,应调用close()方法关闭流,以释放相关资源。
import java.io.FileInputStream;
import java.io.IOException;

/**
 * 示例:使用 FileInputStream 逐字节读取文件中的数据。
 */
public class FileInputStreamDemo {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            // 创建 FileInputStream 对象,指定要读取的文件路径
            fis = new FileInputStream("E:/a.txt");

            int byteData;
            // 循环读取数据,每次读取一个字节,直到文件末尾
            while ((byteData = fis.read()) != -1) {
                // 将读取的字节转换为字符并输出
                System.out.print((char) byteData);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭输入流,释放资源
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

image-20240319211330364

# 3. 每次读取一个字节数组 (批量读取)

为了提高效率,特别是处理较大的文件时,可以使用字节数组一次读取多个字节的数据。这种方法通常用于需要高效读取大量数据的场景。

  1. **创建 FileInputStream 对象:**指定需要读取的文件路径,创建一个 FileInputStream 实例。
  2. **读取数据:**使用 read(byte[] b) 方法将多个字节的数据读入到字节数组中。可以通过循环读取文件中的所有数据。
  3. **关闭流:**使用完毕后,应调用 close() 方法关闭流,以释放相关资源。
import java.io.FileInputStream;
import java.io.IOException;

/**
 * 示例:使用 FileInputStream 读取字节数组的数据。
 */
public class FileInputStreamExample {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("E:/a.txt")) {
            byte[] buffer = new byte[1024]; // 创建字节数组作为缓冲区
            int len; // 每次读取的字节数
            // 循环读取数据,每次读取一个字节数组的长度,直到文件末尾
            while ((len = fis.read(buffer)) != -1) {
                // 将读取的字节转换为字符串进行输出
                System.out.print(new String(buffer, 0, len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 4. 异常的捕获处理

# 1. JDK7版本之前处理方式 : 手动释放资源

/*
    需求 : 对上一个赋值图片的代码进行使用捕获方式处理
 */
public class FileInputStreamDemo4 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            // 创建字节输入流对象,用于读取原图片
            fis = new FileInputStream("C:\\Users\\WuYimin\\Pictures\\图库\\123.png");

            // 创建字节输出流对象,用于写入拷贝的图片
            fos = new FileOutputStream("e:/picture/copy.jpg");

            // 定义变量by,用于存放读取到的字节
            int by;
            // 循环读取原图片的字节,并写入到拷贝图片中
            while ((by = fis.read()) != -1) {
                fos.write(by); // 一次读写一个字节
            }
        } catch (IOException e) {
            // 打印异常信息
            e.printStackTrace();
        } finally {
            // 释放资源,首先判断fis是否为null,避免NullPointerException
            if (fis != null) {
                try {
                    fis.close(); // 尝试关闭fis流
                } catch (IOException e) {
                    // 打印关闭fis流时的异常信息
                    e.printStackTrace();
                }
            }
            // 同样的方式释放fos资源
            if (fos != null) {
                try {
                    fos.close(); // 尝试关闭fos流
                } catch (IOException e) {
                    // 打印关闭fos流时的异常信息
                    e.printStackTrace();
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# 2. JDK7版本优化处理方式 : 自动释放资源

在JDK7及以后的版本中,提供了一种新的异常处理机制——try-with-resources语句,用以简化在处理资源时必须进行的关闭资源操作。这种机制特别适用于需要关闭的资源,比如各种类型的流、数据库连接等。使用try-with-resources可以确保每个资源在语句结束时自动关闭,从而避免资源泄漏,并且使代码更加简洁易读。

格式:

try (资源初始化语句) {
    // 使用资源的代码
} catch (异常类型 变量名) {
    // 异常处理代码
}
1
2
3
4
5
  • 其中,资源初始化语句通常是创建一个资源实例的语句。
  • 资源必须实现java.lang.AutoCloseable或java.io.Closeable接口,这两个接口都声明了void close()方法,try-with-resources语句会在代码块执行完毕后自动调用这个close()方法来关闭资源。

示例代码:

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

public class FileInputStreamDemo5 {
    public static void main(String[] args) {
        // 使用try-with-resources语句自动管理资源
        try (
            // 在try()中声明资源,这些资源会在try代码块执行完毕后自动关闭
            FileInputStream fis = new FileInputStream("D:/scholar Gallery/scholarSave/wallhaven-wexe9r.jpg");
            FileOutputStream fos = new FileOutputStream("D:/copy.jpg");
        ) {
            // 读取源文件,并将内容写入目标文件
            int by;
            while ((by = fis.read()) != -1) {
                fos.write(by);
            }
            // 注意:此处无需手动关闭流,try-with-resources语句已经自动处理
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

优势

  • 自动管理资源,无需手动关闭,减少了代码量,提高了代码的可读性和可维护性。
  • 提高资源管理的安全性,避免了忘记关闭资源导致的资源泄漏问题。

# 3. 图片的拷贝 (字节流应用场景)

使用字节输入流和字节输出流,可以有效地对二进制文件(如图片、音频、视频等)进行复制操作。

  1. 创建输入流对象:指定源文件路径来创建FileInputStream,用于读取源文件。
  2. 创建输出流对象:指定目标文件路径来创建FileOutputStream,用于写入数据到目标文件。
  3. 读取并写入数据:通过read(byte[] b)方法从源文件读取数据到字节数组,然后通过write(byte[] b, int off, int len)方法将字节数组的数据写入目标文件。循环此过程直到文件末尾。
  4. 关闭流:操作完成后,关闭输入流和输出流以释放资源。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopy {
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            // 创建FileInputStream对象,读取源图片
            fis = new FileInputStream("C:\\Users\\WuYimin\\Pictures\\图库\\123.png");
            // 创建FileOutputStream对象,指定拷贝图片的目标位置(目录不存在会报错)
            fos = new FileOutputStream("E:/picture/fosCopy.jpg");
            
            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();
        } finally {
            // 关闭输入流和输出流
            try {
                if (fis != null) {
                    fis.close();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

image-20240319212146117

为什么需要创建字节数组作为缓冲?

通过使用字节数组作为缓冲,可以一次性读取或写入多个字节的数据。这样,每次磁盘I/O操作可以处理更多的数据,减少了磁盘访问次数,从而提高了数据处理的速度。例如,使用1024字节作为缓冲区大小,意味着每次可以读取或写入最多1024字节的数据,这比逐个字节处理要高效得多。

编辑此页 (opens new window)
上次更新: 2024/12/28, 18:32:08
文件操作
字符流

← 文件操作 字符流→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式