Java SE PART 4
Java.IO
Java IO Intro
IO 即 Input/Output
,输入和输出,具体概念可以参考简书-程序员应该这样理解 IO
Classification
Byte Based | Character Based | |||
---|---|---|---|---|
Input | Output | Input | Output | |
Abstract Class | InputStream | OutputStream | Reader | Writer |
Arrays | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
Files | FileInputStream | FileOutputStream | FileReader | FileWriter |
RandomAccessFile | RandomAccessFile | |||
Pipes | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter |
Buffering | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
Filtering | FilterInputStream | FilterOutputStream | FilterReader | FilterWriter |
Parsing | PushbackInputStream | PushbackReader | ||
StreamTokenizer | LineNumberReader | |||
Strings | StringReader | StringWriter | ||
Data | DataInputStream | DataOutputStream | ||
Data - Formatted | PrintStream | PrintWriter | ||
Objects | ObjectInputStream | ObjectOutputStream | ||
Utilities | SequenceInputStream |
ByteStream
在 Java 中,InputStream
和OutputStream
是用于处理字节流输入和输出的抽象类,它们分别表示输入流和输出流。字节流是处理原始字节数据(如图片、音频文件等)的基础
InputStream Methods
InputStream
就是 Java 标准库提供的最基本的输入流。它位于 java.io
这个包里,InputStream
是一个抽象类,它是所有输入流的超类。
read()
:返回输入流中下一个字节的数据。返回的值介于 0 到 255 之间。如果未读取任何字节,则代码返回-1
,表示文件结束。read(byte b[ ])
: 从输入流中读取一些字节存储到数组b
中。如果数组b
的长度为零,则不读取。如果没有可用字节读取,返回-1
。如果有可用字节读取,则最多读取的字节数最多等于b.length
, 返回读取的字节数。这个方法等价于read(b, 0, b.length)
。read(byte b[], int off, int len)
:在read(byte b[ ])
方法的基础上增加了off
参数(偏移量)和len
参数(要读取的最大字节数)。skip(long n)
:忽略输入流中的 n 个字节 ,返回实际忽略的字节数。available()
:返回输入流中可以读取的字节数。close()
:关闭输入流释放相关的系统资源。
从 Java 9 开始,InputStream
新增加了多个实用的方法:
readAllBytes()
:读取输入流中的所有字节,返回字节数组。readNBytes(byte[] b, int off, int len)
:阻塞直到读取len
个字节。transferTo(OutputStream out)
:将所有字节从一个输入流传递到一个输出流。
OutputStream Methods
OutputStream
用于将数据(字节信息)写入到目的地(通常是文件),java.io.OutputStream
抽象类是所有字节输出流的父类。
write(int b)
:将特定字节写入输出流。虽然传入的是int
参数,但只会写入一个字节,即只写入int
最低 8 位表示字节的部分,相当于b & 0xff
write(byte b[ ])
: 将数组b
写入到输出流,等价于write(b, 0, b.length)
。write(byte[] b, int off, int len)
: 在write(byte b[ ])
方法的基础上增加了off
参数(偏移量)和len
参数(要读取的最大字节数)。flush()
:它的作用是将缓冲区中的数据强制写入目标设备或输出流,而不是等待缓冲区满或流关闭时自动写入。这对于确保及时输出数据非常关键。close()
:关闭输出流释放相关的系统资源。
CharacterStream
In Java, characters are stored using Unicode conventions. Character stream automatically allows us to read/write data character by character. For example, FileReader and FileWriter are character streams used to read from the source and write to the destination
Reader Methods
Reader
是 Java 的 IO 库提供的另一个输入流接口。和 InputStream
的区别是,InputStream
是一个字节流,即以 byte
为单位读取,而 Reader
是一个字符流,即以 char
为单位读取:
InputStream | Reader |
---|---|
字节流,以 byte 为单位 | 字符流,以 char 为单位 |
读取字节(-1,0~255):int read() | 读取字符(-1,0~65535):int read() |
读到字节数组:int read(byte[] b) | 读到字符数组:int read(char[] c) |
read()
: 从输入流读取一个字符。read(char[] cbuf)
: 从输入流中读取一些字符,并将它们存储到字符数组cbuf
中,等价于read(cbuf, 0, cbuf.length)
。read(char[] cbuf, int off, int len)
:在read(char[] cbuf)
方法的基础上增加了off
参数(偏移量)和len
参数(要读取的最大字符数)。skip(long n)
:忽略输入流中的 n 个字符 ,返回实际忽略的字符数。close()
: 关闭输入流并释放相关的系统资源。
Writer Methods
Reader
是带编码转换器的 InputStream
,它把 byte
转换为 char
,而 Writer
就是带编码转换器的 OutputStream
,它把 char
转换为 byte
并输出。
OutputStream | Writer |
---|---|
字节流,以 byte 为单位 | 字符流,以 char 为单位 |
写入字节(0~255):void write(int b) | 写入字符(0~65535):void write(int c) |
写入字节数组:void write(byte[] b) | 写入字符数组:void write(char[] c) |
写入 String: 无对应方法 | 写入 String:void write(String s) |
write(int c)
: 写入单个字符。write(char[] cbuf)
:写入字符数组cbuf
,等价于write(cbuf, 0, cbuf.length)
。write(char[] cbuf, int off, int len)
:在write(char[] cbuf)
方法的基础上增加了off
参数(偏移量)和len
参数(要读取的最大字符数)。write(String str)
:写入字符串,等价于write(str, 0, str.length())
。write(String str, int off, int len)
:在write(String str)
方法的基础上增加了off
参数(偏移量)和len
参数(要读取的最大字符数)。append(CharSequence csq)
:将指定的字符序列附加到指定的Writer
对象并返回该Writer
对象。append(char c)
:将指定的字符附加到指定的Writer
对象并返回该Writer
对象。flush()
:刷新此输出流并强制写出所有缓冲的输出字符。close()
:关闭输出流释放相关的系统资源。
Buffering
缓冲是指通过在内存中暂时存储数据来优化 I/O 操作的性能。Java 中的缓冲通常是通过缓冲流(Buffered Streams)来实现的。缓冲的主要目的是通过将数据加载至缓冲区,一次性读取/写入多个字节,避免频繁的 I/O 操作,从而提高性能,尤其是在处理大文件或高频繁数据读写时。
读写时机 👇
- 写入:当缓冲区已满或程序里调用 flush 方法时,它才会将数据写入实际的输出目标设备(如磁盘文件、网络等)
- 读取:缓冲区会提前从输入源(如文件、网络流)读取一定数量的数据,直到缓冲区的数据被消耗完,才会重新从源读取数据。
内部实现 👇
缓冲机制为什么会提高性能
减少 IO 操作次数: I/O 操作(无论是文件操作、网络通信还是磁盘访问)通常比内存操作要慢得多。每次进行 I/O 操作时,涉及的系统调用、硬盘读写、网络延迟等都会消耗大量时间。
- 每次 I/O 操作都会涉及到系统调用,这会消耗宝贵的 CPU 时间。比如用户态与内核态的切换、中断处理、内核锁的竞争、数据复制、上下文切换等等
I/O 系统调用的开销来自多个方面,其中每个方面都可能影响 I/O 的整体性能。为了减少这些开销,现代操作系统和编程语言会使用缓冲区(buffering)、异步 I/O、非阻塞 I/O 等优化技术,从而提高系统性能
NIO
Java NIO(New Input/Output)是 Java 1.4 中引入的一组新的 I/O API,相比传统的 I/O(即 Java IO)具有更高效的性能。Java NIO 主要用于构建高性能、可伸缩的网络应用程序,特别适合处理大量并发连接。NIO 通过以下核心特性提供了更灵活的 I/O 处理方式:
非阻塞模式
Java NIO 支持非阻塞 I/O 操作,即一个线程可以同时处理多个连接而不被某个特定 I/O 操作阻塞。对于网络编程中的高并发场景,非阻塞模式允许程序不必等待 I/O 操作完成,可以立即执行其他任务,提高了应用程序的响应速度。Channel 和 Buffer
Channel:Channel 类似于传统 I/O 中的流,但与流不同的是,Channel 是双向的,可以同时用于读取和写入数据。
Buffer:Buffer 是一个容器,用于临时存储数据。数据从 Channel 读取到 Buffer,或者从 Buffer 写入到 Channel。Buffer 不仅简化了数据管理,还允许我们直接操作数据,提高了 I/O 性能。Selector(选择器)
Selector 是 NIO 中的关键组件,允许一个单独的线程监控多个 Channel 的状态。通过 Selector,一个线程可以管理多个 Channel,利用非阻塞 I/O 来轮询多个连接,提高并发能力。Selector 通过选择“就绪”的 Channel 来执行后续操作,适合处理大量并发连接的网络服务程序。零拷贝(Zero Copy)
NIO 通过直接缓冲区(Direct Buffer)实现了零拷贝。Direct Buffer 在物理内存中分配,不经过 JVM 堆,因此数据可以直接在硬件设备(如磁盘、网络)与内存之间传输,减少了复制次数和用户态/内核态的切换,提高了 I/O 性能。内存映射文件(Memory-Mapped File)
NIO 提供了内存映射文件(Memory-Mapped File)功能,可以将文件的一部分映射到内存中,支持快速随机访问。对于大文件的处理,内存映射文件特别有效,可以直接在内存中访问文件数据,且不需要传统 I/O 的频繁系统调用。NIO 与 NIO.2 扩展
Java 7 引入了 NIO.2(java.nio.file 包),进一步扩展了 NIO。NIO.2 提供了更便捷的文件系统操作 API(例如异步文件 I/O、文件监听等),使得文件操作更加方便和高效。它还增强了异常处理、路径操作等功能。
Java SE PART 4