前言

Java I/O(Input/Output)流是Java程序与外部世界进行数据交换的重要桥梁。无论是读取文件、网络通信,还是控制台交互,都离不开I/O流的支持。Java提供了丰富的I/O类库,支持字节流、字符流、缓冲流、对象流等多种数据传输方式。本文将深入探讨Java I/O流的核心概念、分类体系、使用方法以及实际应用场景。

Java I/O流概述

I/O流的基本概念

I/O流是Java中处理输入输出的抽象概念,它将数据的传输抽象为流的形式,使得程序可以用统一的方式处理不同来源和目标的数据。

graph TD
    A[Java I/O流体系] --> B[按数据单位分类]
    A --> C[按数据流向分类]
    A --> D[按功能分类]
    
    B --> E[字节流 Stream]
    B --> F[字符流 Reader/Writer]
    
    C --> G[输入流 Input]
    C --> H[输出流 Output]
    
    D --> I[节点流 Node Stream]
    D --> J[处理流 Processing Stream]
    
    E --> K[InputStream]
    E --> L[OutputStream]
    
    F --> M[Reader]
    F --> N[Writer]
    
    I --> O[直接连接数据源]
    J --> P[对其他流进行包装]

I/O流的层次结构

Java I/O流采用装饰器模式设计,形成了完整的类层次结构:

字节流层次结构

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
// InputStream家族(抽象基类)
public abstract class InputStreamExample {
// 核心方法
public abstract int read() throws IOException;
public int read(byte[] b) throws IOException { /* ... */ }
public int read(byte[] b, int off, int len) throws IOException { /* ... */ }
public void close() throws IOException { /* ... */ }

// 主要实现类:
// FileInputStream - 文件输入流
// ByteArrayInputStream - 字节数组输入流
// BufferedInputStream - 缓冲输入流
// DataInputStream - 数据输入流
// ObjectInputStream - 对象输入流
}

// OutputStream家族(抽象基类)
public abstract class OutputStreamExample {
// 核心方法
public abstract void write(int b) throws IOException;
public void write(byte[] b) throws IOException { /* ... */ }
public void write(byte[] b, int off, int len) throws IOException { /* ... */ }
public void flush() throws IOException { /* ... */ }
public void close() throws IOException { /* ... */ }

// 主要实现类:
// FileOutputStream - 文件输出流
// ByteArrayOutputStream - 字节数组输出流
// BufferedOutputStream - 缓冲输出流
// DataOutputStream - 数据输出流
// ObjectOutputStream - 对象输出流
}

字符流层次结构

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
// Reader家族(抽象基类)
public abstract class ReaderExample {
// 核心方法
public abstract int read() throws IOException;
public int read(char[] cbuf) throws IOException { /* ... */ }
public abstract int read(char[] cbuf, int off, int len) throws IOException;
public void close() throws IOException { /* ... */ }

// 主要实现类:
// FileReader - 文件字符读取流
// CharArrayReader - 字符数组读取流
// StringReader - 字符串读取流
// BufferedReader - 缓冲字符读取流
// InputStreamReader - 字节流到字符流的桥梁
}

// Writer家族(抽象基类)
public abstract class WriterExample {
// 核心方法
public abstract void write(char[] cbuf, int off, int len) throws IOException;
public void write(int c) throws IOException { /* ... */ }
public void write(char[] cbuf) throws IOException { /* ... */ }
public void write(String str) throws IOException { /* ... */ }
public abstract void flush() throws IOException;
public abstract void close() throws IOException;

// 主要实现类:
// FileWriter - 文件字符写入流
// CharArrayWriter - 字符数组写入流
// StringWriter - 字符串写入流
// BufferedWriter - 缓冲字符写入流
// OutputStreamWriter - 字符流到字节流的桥梁
}

字节流详解

字节流以字节为单位进行数据传输,适用于处理二进制数据,如图片、音频、视频等文件。

InputStream和OutputStream

文件字节流基本操作

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
public class FileByteStreamExample {

// 使用FileInputStream读取文件
public void readFileWithFileInputStream(String fileName) {
FileInputStream fis = null;
try {
fis = new FileInputStream(fileName);
int data;
System.out.println("读取文件内容(字节方式):");

// 逐字节读取
while ((data = fis.read()) != -1) {
System.out.print((char) data);
}

} catch (IOException e) {
System.err.println("读取文件失败: " + e.getMessage());
} finally {
// 确保流被关闭
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
System.err.println("关闭流失败: " + e.getMessage());
}
}
}
}

// 使用FileOutputStream写入文件
public void writeFileWithFileOutputStream(String fileName, String content) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(fileName);
byte[] bytes = content.getBytes("UTF-8");

// 写入字节数据
fos.write(bytes);
fos.flush(); // 确保数据写入磁盘

System.out.println("成功写入文件: " + fileName);

} catch (IOException e) {
System.err.println("写入文件失败: " + e.getMessage());
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
System.err.println("关闭流失败: " + e.getMessage());
}
}
}
}

// 使用try-with-resources优化资源管理
public void copyFileWithTryWithResources(String sourceFile, String targetFile) {
try (FileInputStream fis = new FileInputStream(sourceFile);
FileOutputStream fos = new FileOutputStream(targetFile)) {

byte[] buffer = new byte[1024]; // 使用缓冲区提高效率
int bytesRead;

while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}

System.out.println("文件复制完成: " + sourceFile + " -> " + targetFile);

} catch (IOException e) {
System.err.println("文件复制失败: " + e.getMessage());
}
}

// 读取二进制文件(如图片)
public byte[] readBinaryFile(String fileName) {
try (FileInputStream fis = new FileInputStream(fileName);
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {

byte[] buffer = new byte[4096];
int bytesRead;

while ((bytesRead = fis.read(buffer)) != -1) {
baos.write(buffer, 0, bytesRead);
}

return baos.toByteArray();

} catch (IOException e) {
System.err.println("读取二进制文件失败: " + e.getMessage());
return null;
}
}

// 写入二进制文件
public void writeBinaryFile(String fileName, byte[] data) {
try (FileOutputStream fos = new FileOutputStream(fileName)) {
fos.write(data);
System.out.println("二进制文件写入完成: " + fileName);

} catch (IOException e) {
System.err.println("写入二进制文件失败: " + e.getMessage());
}
}
}

ByteArrayInputStream和ByteArrayOutputStream

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
public class ByteArrayStreamExample {

// ByteArrayInputStream示例
public void demonstrateByteArrayInputStream() {
String data = "Hello, Java I/O Stream!";
byte[] bytes = data.getBytes();

try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
System.out.println("从字节数组读取数据:");

int byteData;
while ((byteData = bais.read()) != -1) {
System.out.print((char) byteData);
}
System.out.println();

// 重置流位置
bais.reset();

// 批量读取
byte[] buffer = new byte[5];
int bytesRead = bais.read(buffer);
System.out.println("批量读取 " + bytesRead + " 字节: " + new String(buffer, 0, bytesRead));

} catch (IOException e) {
System.err.println("ByteArrayInputStream操作失败: " + e.getMessage());
}
}

// ByteArrayOutputStream示例
public void demonstrateByteArrayOutputStream() {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {

// 写入不同类型的数据
baos.write("Hello".getBytes());
baos.write(' ');
baos.write("World".getBytes());
baos.write('!');

// 获取写入的数据
byte[] result = baos.toByteArray();
String resultString = new String(result);

System.out.println("ByteArrayOutputStream内容: " + resultString);
System.out.println("字节数组长度: " + result.length);

// 重置输出流
baos.reset();
baos.write("Reset content".getBytes());

System.out.println("重置后内容: " + baos.toString());

} catch (IOException e) {
System.err.println("ByteArrayOutputStream操作失败: " + e.getMessage());
}
}

// 实际应用:数据序列化到字节数组
public byte[] serializeData(Object... objects) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos)) {

for (Object obj : objects) {
if (obj instanceof String) {
dos.writeUTF((String) obj);
} else if (obj instanceof Integer) {
dos.writeInt((Integer) obj);
} else if (obj instanceof Double) {
dos.writeDouble((Double) obj);
} else if (obj instanceof Boolean) {
dos.writeBoolean((Boolean) obj);
}
}

return baos.toByteArray();

} catch (IOException e) {
System.err.println("数据序列化失败: " + e.getMessage());
return null;
}
}
}

字符流详解

字符流专门用于处理文本数据,自动处理字符编码转换,适用于处理文本文件。

graph LR
    A[字符流特点] --> B[编码转换]
    A --> C[文本处理]
    A --> D[行读取支持]
    
    B --> E[自动UTF-8/GBK转换]
    C --> F[按字符读写]
    D --> G[BufferedReader.readLine]
    
    A --> H[字符流类型]
    H --> I[Reader读取流]
    H --> J[Writer写入流]
    
    I --> K[FileReader]
    I --> L[BufferedReader]
    I --> M[StringReader]
    
    J --> N[FileWriter]
    J --> O[BufferedWriter]
    J --> P[StringWriter]

Reader和Writer

文件字符流操作

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
public class FileCharacterStreamExample {

// 使用FileReader读取文本文件
public void readTextFileWithFileReader(String fileName) {
try (FileReader fr = new FileReader(fileName)) {
System.out.println("读取文本文件内容:");

int charData;
while ((charData = fr.read()) != -1) {
System.out.print((char) charData);
}
System.out.println();

} catch (IOException e) {
System.err.println("读取文本文件失败: " + e.getMessage());
}
}

// 使用FileWriter写入文本文件
public void writeTextFileWithFileWriter(String fileName, String content) {
try (FileWriter fw = new FileWriter(fileName)) {
fw.write(content);
fw.flush();

System.out.println("成功写入文本文件: " + fileName);

} catch (IOException e) {
System.err.println("写入文本文件失败: " + e.getMessage());
}
}

// 使用指定编码的字符流
public void readWriteWithEncoding(String inputFile, String outputFile,
String inputEncoding, String outputEncoding) {
try (InputStreamReader isr = new InputStreamReader(
new FileInputStream(inputFile), inputEncoding);
OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream(outputFile), outputEncoding)) {

char[] buffer = new char[1024];
int charsRead;

while ((charsRead = isr.read(buffer)) != -1) {
osw.write(buffer, 0, charsRead);
}

System.out.println("编码转换完成: " + inputEncoding + " -> " + outputEncoding);

} catch (IOException e) {
System.err.println("编码转换失败: " + e.getMessage());
}
}

// 按行读取文本文件
public List<String> readFileLines(String fileName) {
List<String> lines = new ArrayList<>();

try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
String line;
while ((line = br.readLine()) != null) {
lines.add(line);
}

} catch (IOException e) {
System.err.println("按行读取文件失败: " + e.getMessage());
}

return lines;
}

// 按行写入文本文件
public void writeFileLines(String fileName, List<String> lines) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(fileName))) {
for (String line : lines) {
bw.write(line);
bw.newLine(); // 写入换行符
}

System.out.println("按行写入文件完成: " + fileName);

} catch (IOException e) {
System.err.println("按行写入文件失败: " + e.getMessage());
}
}
}

StringReader和StringWriter

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
public class StringStreamExample {

// StringReader示例
public void demonstrateStringReader() {
String text = "Java I/O流\n字符流示例\n第三行内容";

try (StringReader sr = new StringReader(text)) {
System.out.println("StringReader逐字符读取:");

int charData;
while ((charData = sr.read()) != -1) {
char c = (char) charData;
if (c == '\n') {
System.out.println("\\n");
} else {
System.out.print(c);
}
}
System.out.println();

} catch (IOException e) {
System.err.println("StringReader操作失败: " + e.getMessage());
}

// 使用BufferedReader包装StringReader按行读取
try (BufferedReader br = new BufferedReader(new StringReader(text))) {
System.out.println("StringReader按行读取:");

String line;
int lineNumber = 1;
while ((line = br.readLine()) != null) {
System.out.println("第" + lineNumber + "行: " + line);
lineNumber++;
}

} catch (IOException e) {
System.err.println("StringReader按行读取失败: " + e.getMessage());
}
}

// StringWriter示例
public void demonstrateStringWriter() {
try (StringWriter sw = new StringWriter()) {

// 写入不同类型的数据
sw.write("Hello ");
sw.write("StringWriter!");
sw.write('\n');
sw.write("这是第二行");

// 获取写入的内容
String result = sw.toString();
System.out.println("StringWriter内容:");
System.out.println(result);

// 使用StringBuffer获取缓冲区
StringBuffer buffer = sw.getBuffer();
buffer.append("\n追加的内容");

System.out.println("追加后的内容:");
System.out.println(sw.toString());

} catch (IOException e) {
System.err.println("StringWriter操作失败: " + e.getMessage());
}
}

// 实际应用:格式化输出到字符串
public String formatDataToString(Object... data) {
try (StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw)) {

pw.println("=== 数据格式化报告 ===");
pw.printf("生成时间: %tF %tT%n", new Date(), new Date());
pw.println("数据项:");

for (int i = 0; i < data.length; i++) {
pw.printf("%d. %s (%s)%n",
i + 1, data[i], data[i].getClass().getSimpleName());
}

pw.println("=== 报告结束 ===");

return sw.toString();

} catch (Exception e) {
return "格式化失败: " + e.getMessage();
}
}
}

缓冲流

缓冲流通过内部缓冲区减少实际的I/O操作次数,显著提高I/O性能。

graph TD
    A[缓冲流工作原理] --> B[读取缓冲]
    A --> C[写入缓冲]
    
    B --> D[一次读取大块数据到缓冲区]
    B --> E[程序从缓冲区获取数据]
    B --> F[缓冲区空时重新填充]
    
    C --> G[数据先写入缓冲区]
    C --> H[缓冲区满时批量写出]
    C --> I[flush()强制写出]
    
    A --> J[性能优势]
    J --> K[减少系统调用次数]
    J --> L[提高吞吐量]
    J --> M[降低CPU开销]

BufferedInputStream和BufferedOutputStream

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
public class BufferedStreamExample {

// 性能对比:普通流vs缓冲流
public void performanceComparison(String sourceFile, String targetFile1, String targetFile2) {
// 测试普通字节流性能
long startTime = System.currentTimeMillis();
copyFileWithoutBuffer(sourceFile, targetFile1);
long timeWithoutBuffer = System.currentTimeMillis() - startTime;

// 测试缓冲字节流性能
startTime = System.currentTimeMillis();
copyFileWithBuffer(sourceFile, targetFile2);
long timeWithBuffer = System.currentTimeMillis() - startTime;

System.out.println("性能对比结果:");
System.out.println("普通流耗时: " + timeWithoutBuffer + "ms");
System.out.println("缓冲流耗时: " + timeWithBuffer + "ms");
System.out.println("性能提升: " + (timeWithoutBuffer - timeWithBuffer) + "ms");
}

// 不使用缓冲的文件复制
private void copyFileWithoutBuffer(String sourceFile, String targetFile) {
try (FileInputStream fis = new FileInputStream(sourceFile);
FileOutputStream fos = new FileOutputStream(targetFile)) {

int data;
while ((data = fis.read()) != -1) {
fos.write(data);
}

} catch (IOException e) {
System.err.println("无缓冲复制失败: " + e.getMessage());
}
}

// 使用缓冲的文件复制
private void copyFileWithBuffer(String sourceFile, String targetFile) {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(targetFile))) {

int data;
while ((data = bis.read()) != -1) {
bos.write(data);
}

} catch (IOException e) {
System.err.println("缓冲复制失败: " + e.getMessage());
}
}

// 自定义缓冲区大小
public void copyFileWithCustomBuffer(String sourceFile, String targetFile, int bufferSize) {
try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(sourceFile), bufferSize);
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(targetFile), bufferSize)) {

byte[] buffer = new byte[bufferSize];
int bytesRead;

while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}

System.out.println("使用 " + bufferSize + " 字节缓冲区复制完成");

} catch (IOException e) {
System.err.println("自定义缓冲复制失败: " + e.getMessage());
}
}

// 处理大文件的优化方法
public void processLargeFile(String fileName) {
try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(fileName), 64 * 1024)) { // 64KB缓冲区

byte[] buffer = new byte[8192]; // 8KB读取缓冲区
int bytesRead;
long totalBytes = 0;
int chunkCount = 0;

while ((bytesRead = bis.read(buffer)) != -1) {
// 处理数据块
processDataChunk(buffer, bytesRead);
totalBytes += bytesRead;
chunkCount++;

// 每处理100个数据块显示进度
if (chunkCount % 100 == 0) {
System.out.println("已处理 " + totalBytes + " 字节");
}
}

System.out.println("大文件处理完成,总计 " + totalBytes + " 字节");

} catch (IOException e) {
System.err.println("大文件处理失败: " + e.getMessage());
}
}

private void processDataChunk(byte[] data, int length) {
// 模拟数据处理逻辑
// 例如:计算校验和、数据转换等
}
}

BufferedReader和BufferedWriter

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
public class BufferedCharacterStreamExample {

// 高效的文本文件处理
public void processTextFile(String inputFile, String outputFile) {
try (BufferedReader br = new BufferedReader(new FileReader(inputFile));
BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile))) {

String line;
int lineNumber = 0;

while ((line = br.readLine()) != null) {
lineNumber++;

// 处理每一行(例如:添加行号)
String processedLine = String.format("%04d: %s", lineNumber, line);
bw.write(processedLine);
bw.newLine();
}

System.out.println("文本文件处理完成,共处理 " + lineNumber + " 行");

} catch (IOException e) {
System.err.println("文本文件处理失败: " + e.getMessage());
}
}

// 控制台交互示例
public void consoleInteraction() {
try (BufferedReader consoleReader = new BufferedReader(
new InputStreamReader(System.in))) {

System.out.println("=== 控制台交互示例 ===");
System.out.println("输入 'quit' 退出程序");

String input;
while (true) {
System.out.print("请输入: ");
input = consoleReader.readLine();

if ("quit".equalsIgnoreCase(input)) {
System.out.println("程序退出");
break;
}

// 处理用户输入
processUserInput(input);
}

} catch (IOException e) {
System.err.println("控制台交互失败: " + e.getMessage());
}
}

private void processUserInput(String input) {
System.out.println("您输入了: " + input);
System.out.println("长度: " + input.length() + " 字符");
System.out.println("大写形式: " + input.toUpperCase());
}

// 日志文件写入
public void writeLogFile(String logFile, List<String> logEntries) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(logFile, true))) { // 追加模式

for (String entry : logEntries) {
String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
bw.write(String.format("[%s] %s", timestamp, entry));
bw.newLine();
}

bw.flush(); // 确保日志立即写入
System.out.println("日志写入完成,共 " + logEntries.size() + " 条");

} catch (IOException e) {
System.err.println("日志写入失败: " + e.getMessage());
}
}

// 配置文件读取
public Properties readConfigFile(String configFile) {
Properties config = new Properties();

try (BufferedReader br = new BufferedReader(new FileReader(configFile))) {
String line;
while ((line = br.readLine()) != null) {
line = line.trim();

// 跳过注释和空行
if (line.isEmpty() || line.startsWith("#")) {
continue;
}

// 解析键值对
int equalIndex = line.indexOf('=');
if (equalIndex > 0) {
String key = line.substring(0, equalIndex).trim();
String value = line.substring(equalIndex + 1).trim();
config.setProperty(key, value);
}
}

} catch (IOException e) {
System.err.println("配置文件读取失败: " + e.getMessage());
}

return config;
}
}

数据流和对象流

DataInputStream和DataOutputStream

数据流提供了读写Java基本数据类型的便捷方法:

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
public class DataStreamExample {

// 写入基本数据类型
public void writeDataToFile(String fileName) {
try (DataOutputStream dos = new DataOutputStream(
new FileOutputStream(fileName))) {

// 写入不同类型的数据
dos.writeBoolean(true);
dos.writeByte(127);
dos.writeShort(32767);
dos.writeInt(2147483647);
dos.writeLong(9223372036854775807L);
dos.writeFloat(3.14f);
dos.writeDouble(3.141592653589793);
dos.writeChar('A');
dos.writeUTF("Hello, DataStream!");

System.out.println("数据写入完成: " + fileName);

} catch (IOException e) {
System.err.println("数据写入失败: " + e.getMessage());
}
}

// 读取基本数据类型
public void readDataFromFile(String fileName) {
try (DataInputStream dis = new DataInputStream(
new FileInputStream(fileName))) {

// 按写入顺序读取数据
boolean boolValue = dis.readBoolean();
byte byteValue = dis.readByte();
short shortValue = dis.readShort();
int intValue = dis.readInt();
long longValue = dis.readLong();
float floatValue = dis.readFloat();
double doubleValue = dis.readDouble();
char charValue = dis.readChar();
String stringValue = dis.readUTF();

System.out.println("读取的数据:");
System.out.println("boolean: " + boolValue);
System.out.println("byte: " + byteValue);
System.out.println("short: " + shortValue);
System.out.println("int: " + intValue);
System.out.println("long: " + longValue);
System.out.println("float: " + floatValue);
System.out.println("double: " + doubleValue);
System.out.println("char: " + charValue);
System.out.println("String: " + stringValue);

} catch (IOException e) {
System.err.println("数据读取失败: " + e.getMessage());
}
}

// 学生成绩数据处理示例
public void processStudentScores() {
String dataFile = "student_scores.dat";

// 写入学生成绩数据
writeStudentScores(dataFile);

// 读取并分析学生成绩数据
analyzeStudentScores(dataFile);
}

private void writeStudentScores(String fileName) {
try (DataOutputStream dos = new DataOutputStream(
new FileOutputStream(fileName))) {

// 学生数据:姓名、年龄、各科成绩
String[] names = {"张三", "李四", "王五", "赵六"};
int[] ages = {20, 21, 19, 22};
double[][] scores = {
{85.5, 92.0, 78.5}, // 张三:语文、数学、英语
{90.0, 88.5, 95.0}, // 李四
{78.0, 85.0, 82.5}, // 王五
{92.5, 90.0, 89.0} // 赵六
};

dos.writeInt(names.length); // 写入学生数量

for (int i = 0; i < names.length; i++) {
dos.writeUTF(names[i]);
dos.writeInt(ages[i]);
dos.writeInt(scores[i].length); // 科目数量

for (double score : scores[i]) {
dos.writeDouble(score);
}
}

System.out.println("学生成绩数据写入完成");

} catch (IOException e) {
System.err.println("学生成绩数据写入失败: " + e.getMessage());
}
}

private void analyzeStudentScores(String fileName) {
try (DataInputStream dis = new DataInputStream(
new FileInputStream(fileName))) {

int studentCount = dis.readInt();
System.out.println("=== 学生成绩分析报告 ===");
System.out.println("学生总数: " + studentCount);

double totalAverage = 0.0;

for (int i = 0; i < studentCount; i++) {
String name = dis.readUTF();
int age = dis.readInt();
int subjectCount = dis.readInt();

double totalScore = 0.0;
System.out.print(name + "(" + age + "岁) 成绩: ");

for (int j = 0; j < subjectCount; j++) {
double score = dis.readDouble();
totalScore += score;
System.out.printf("%.1f ", score);
}

double average = totalScore / subjectCount;
totalAverage += average;
System.out.printf(" 平均分: %.1f%n", average);
}

double classAverage = totalAverage / studentCount;
System.out.printf("班级平均分: %.1f%n", classAverage);

} catch (IOException e) {
System.err.println("学生成绩数据分析失败: " + e.getMessage());
}
}
}

ObjectInputStream和ObjectOutputStream

对象流支持Java对象的序列化和反序列化:

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// 可序列化的学生类
class Student implements Serializable {
private static final long serialVersionUID = 1L;

private String name;
private int age;
private double[] scores;
private transient String password; // transient字段不会被序列化

public Student(String name, int age, double[] scores) {
this.name = name;
this.age = age;
this.scores = scores;
this.password = "secret";
}

// getter和setter方法
public String getName() { return name; }
public int getAge() { return age; }
public double[] getScores() { return scores; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }

public double getAverageScore() {
double sum = 0;
for (double score : scores) {
sum += score;
}
return sum / scores.length;
}

@Override
public String toString() {
return String.format("Student{name='%s', age=%d, scores=%s, password='%s'}",
name, age, Arrays.toString(scores), password);
}
}

public class ObjectStreamExample {

// 序列化对象到文件
public void serializeStudents(String fileName, List<Student> students) {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(fileName))) {

oos.writeObject(students);
System.out.println("学生对象序列化完成,共 " + students.size() + " 个学生");

} catch (IOException e) {
System.err.println("对象序列化失败: " + e.getMessage());
}
}

// 从文件反序列化对象
@SuppressWarnings("unchecked")
public List<Student> deserializeStudents(String fileName) {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(fileName))) {

List<Student> students = (List<Student>) ois.readObject();
System.out.println("学生对象反序列化完成,共 " + students.size() + " 个学生");
return students;

} catch (IOException | ClassNotFoundException e) {
System.err.println("对象反序列化失败: " + e.getMessage());
return new ArrayList<>();
}
}

// 对象序列化示例
public void demonstrateObjectSerialization() {
String fileName = "students.ser";

// 创建学生对象
List<Student> originalStudents = Arrays.asList(
new Student("张三", 20, new double[]{85.5, 92.0, 78.5}),
new Student("李四", 21, new double[]{90.0, 88.5, 95.0}),
new Student("王五", 19, new double[]{78.0, 85.0, 82.5})
);

System.out.println("原始学生数据:");
for (Student student : originalStudents) {
System.out.println(student);
}

// 序列化对象
serializeStudents(fileName, originalStudents);

// 反序列化对象
List<Student> deserializedStudents = deserializeStudents(fileName);

System.out.println("\n反序列化后的学生数据:");
for (Student student : deserializedStudents) {
System.out.println(student);
System.out.println("平均分: " + student.getAverageScore());
}
}

// 深拷贝工具方法
@SuppressWarnings("unchecked")
public static <T extends Serializable> T deepCopy(T object) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos)) {

// 序列化对象
oos.writeObject(object);
oos.flush();

try (ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais)) {

// 反序列化得到深拷贝对象
return (T) ois.readObject();
}

} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("深拷贝失败", e);
}
}

// 演示深拷贝
public void demonstrateDeepCopy() {
Student original = new Student("原始学生", 20, new double[]{90, 85, 88});
Student copy = deepCopy(original);

System.out.println("原始对象: " + original);
System.out.println("拷贝对象: " + copy);
System.out.println("是否为同一对象: " + (original == copy));
System.out.println("内容是否相等: " + original.toString().equals(copy.toString()));

// 修改拷贝对象不影响原始对象
copy.getScores()[0] = 100;
System.out.println("修改拷贝后:");
System.out.println("原始对象成绩: " + Arrays.toString(original.getScores()));
System.out.println("拷贝对象成绩: " + Arrays.toString(copy.getScores()));
}
}

实际应用案例

文件处理工具类

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
public class FileUtils {

// 文件复制(支持进度回调)
public static void copyFile(String source, String target,
ProgressCallback callback) throws IOException {
File sourceFile = new File(source);
long totalSize = sourceFile.length();
long copiedSize = 0;

try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(source), 64 * 1024);
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(target), 64 * 1024)) {

byte[] buffer = new byte[8192];
int bytesRead;

while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
copiedSize += bytesRead;

if (callback != null) {
int progress = (int) ((copiedSize * 100) / totalSize);
callback.onProgress(progress, copiedSize, totalSize);
}
}
}
}

// 读取文本文件所有内容
public static String readTextFile(String fileName, String encoding) throws IOException {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(fileName), encoding))) {

StringBuilder content = new StringBuilder();
String line;

while ((line = reader.readLine()) != null) {
content.append(line).append('\n');
}

return content.toString();
}
}

// 写入文本内容到文件
public static void writeTextFile(String fileName, String content,
String encoding, boolean append) throws IOException {
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(fileName, append), encoding))) {
writer.write(content);
}
}

// 计算文件MD5校验和
public static String calculateMD5(String fileName) throws IOException, NoSuchAlgorithmException {
MessageDigest md5 = MessageDigest.getInstance("MD5");

try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(fileName))) {

byte[] buffer = new byte[8192];
int bytesRead;

while ((bytesRead = bis.read(buffer)) != -1) {
md5.update(buffer, 0, bytesRead);
}
}

byte[] digest = md5.digest();
StringBuilder result = new StringBuilder();

for (byte b : digest) {
result.append(String.format("%02x", b));
}

return result.toString();
}

// 进度回调接口
public interface ProgressCallback {
void onProgress(int percentage, long copiedBytes, long totalBytes);
}
}

// 使用示例
public class FileUtilsDemo {
public void demonstrateFileUtils() {
try {
// 文件复制with progress
System.out.println("开始复制文件...");
FileUtils.copyFile("large_file.dat", "copy_of_large_file.dat",
new FileUtils.ProgressCallback() {
@Override
public void onProgress(int percentage, long copiedBytes, long totalBytes) {
System.out.printf("\r复制进度: %d%% (%d/%d bytes)",
percentage, copiedBytes, totalBytes);
}
});
System.out.println("\n文件复制完成!");

// 读取文本文件
String content = FileUtils.readTextFile("config.txt", "UTF-8");
System.out.println("配置文件内容:\n" + content);

// 写入文本文件
String logEntry = new Date() + ": 系统启动\n";
FileUtils.writeTextFile("system.log", logEntry, "UTF-8", true);

// 计算文件校验和
String md5 = FileUtils.calculateMD5("important_file.dat");
System.out.println("文件MD5: " + md5);

} catch (Exception e) {
System.err.println("文件操作失败: " + e.getMessage());
}
}
}

总结

Java I/O流是Java编程中的重要组成部分,为程序提供了强大的数据处理能力。通过本文的学习,我们深入了解了:

  1. I/O流体系:掌握了字节流、字符流的分类和层次结构
  2. 基础流操作:学会了文件读写、数据传输的基本方法
  3. 缓冲流优化:理解了缓冲机制对性能的重要影响
  4. 数据流应用:掌握了基本数据类型和对象的序列化
  5. 实用技巧:学会了文件工具类的设计和性能优化方法

在实际开发中,正确使用I/O流能够:

  • 提高程序的数据处理能力
  • 优化文件操作性能
  • 实现复杂的数据交换需求
  • 构建健壮的文件处理系统

建议开发者在使用I/O流时注意:

  • 及时关闭流资源,推荐使用try-with-resources
  • 根据数据类型选择合适的流
  • 使用缓冲流提高性能
  • 注意字符编码问题
  • 妥善处理异常情况

掌握Java I/O流的精髓,将为构建高效、可靠的Java应用程序奠定坚实基础。

参考资料