在 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
个字节到数组b
的off
位置开始的部分。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.out
是 PrintStream
类型)
PrintStream
提供了多种构造方法,常见的有:
PrintStream(OutputStream out)
:包装另一个输出流,不自动刷新。PrintStream(OutputStream out, boolean autoFlush)
:指定是否自动刷新。PrintStream(String fileName)
:直接输出到文件,自动刷新。PrintStream(File file)
:输出到文件对象,自动刷新。
核心方法
print()
:打印各种数据类型(如int
、String
、Object
等)。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
个字节到数组b
的off
位置。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();
}
}
}
性能与注意事项
- 无需关闭流 :
ByteArrayInputStream
和ByteArrayOutputStream
不涉及底层资源(如文件或网络),无需调用close()
。 - 自动扩容 :
ByteArrayOutputStream
会在数据超过容量时自动扩容,但频繁扩容可能影响性能。 - 线程安全:这两个类均非线程安全,多线程环境下需外部同步。
- 内存占用:避免处理过大的字节数组,以防内存溢出。