【Java 进阶】重生之这次我要彻底掌握 Java 中的各种流
在这里插入图片描述引言在Java编程的世界中,IO流操作是每个开发者都必须掌握的核心技能。从简单的文件读写到复杂的网络通信,从基础的字节操作到高效的NIO编程,Java流操作贯穿了整个应用开发的生命周期。
随着Java技术的不断发展,从传统的BIO(Blocking IO)到现代的NIO(Non-blocking IO),再到NIO.2的异步IO,Java为开发者提供了丰富而强大的IO处理能力。掌握这些流操作不仅能够提升代码的性能和可维护性,更是成为高级Java开发者的必备技能。
本文将深入探讨Java中各种流操作的原理、特点和实际应用,通过丰富的代码示例和实战场景,帮助读者全面掌握Java流操作的精髓。
第一章:Java IO流概述与基础概念1.1 IO流的基本概念与分类Java IO流是Java程序与外部世界进行数据交换的桥梁。从概念上讲,流是一个连续的数据序列,数据可以从源头流向目的地。
1.1.1 流的基本分类Java IO流主要可以从以下几个维度进行分类:
按数据流向分类:
输入流(Input Stream):从数据源读取数据到程序中输出流(Output Stream):从程序向目的地写入数据
在这里插入图片描述按处理数据单位分类:
字节流(Byte Stream):以字节为单位处理数据,适合处理二进制文件字符流(Character Stream):以字符为单位处理数据,适合处理文本文件
在这里插入图片描述代码语言:javascript复制// 字节流示例
public class ByteStreamExample {
public static void copyFile(String source, String target) throws IOException {
try (FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(target)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
}
}
}
// 字符流示例
public class CharacterStreamExample {
public static void copyTextFile(String source, String target) throws IOException {
try (FileReader reader = new FileReader(source);
FileWriter writer = new FileWriter(target)) {
char[] buffer = new char[1024];
int charsRead;
while ((charsRead = reader.read(buffer)) != -1) {
writer.write(buffer, 0, charsRead);
}
}
}
}1.1.2 流的功能分类按功能分类:
节点流(Node Stream):直接与数据源或目的地连接处理流(Processing Stream):对其他流进行包装,提供额外功能代码语言:javascript复制// 节点流示例 - 直接操作文件
FileInputStream nodeStream = new FileInputStream("data.txt");
// 处理流示例 - 为节点流添加缓冲功能
BufferedInputStream processingStream = new BufferedInputStream(nodeStream);1.2 流的层次结构与设计模式1.2.1 字节流层次结构Java字节流的核心是InputStream和OutputStream两个抽象类:
代码语言:javascript复制// InputStream层次结构核心方法
public abstract class InputStream implements Closeable {
// 核心抽象方法
public abstract int read() throws IOException;
// 批量读取方法
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
// 带偏移量的批量读取
public int read(byte b[], int off, int len) throws IOException {
// 默认实现,子类可以重写以提高效率
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
}1.2.2 装饰器模式在IO流中的应用Java IO流大量使用了装饰器模式,通过层层包装来增强流的功能:
代码语言:javascript复制public class DecoratorPatternExample {
public static void demonstrateDecorator() throws IOException {
// 基础节点流
FileInputStream fileStream = new FileInputStream("data.txt");
// 添加缓冲功能
BufferedInputStream bufferedStream = new BufferedInputStream(fileStream);
// 添加数据解析功能
DataInputStream dataStream = new DataInputStream(bufferedStream);
// 现在可以高效地读取各种数据类型
int intValue = dataStream.readInt();
double doubleValue = dataStream.readDouble();
String stringValue = dataStream.readUTF();
dataStream.close(); // 关闭最外层流会自动关闭内层流
}
}1.3 IO流的选择策略1.3.1 性能考虑因素选择合适的IO流需要考虑多个因素:
代码语言:javascript复制public class StreamSelectionStrategy {
// 小文件快速读取
public static String readSmallFile(String filename) throws IOException {
try (BufferedReader reader = Files.newBufferedReader(Paths.get(filename))) {
return reader.lines().collect(Collectors.joining("\n"));
}
}
// 大文件分块处理
public static void processLargeFile(String filename) throws IOException {
try (BufferedReader reader = Files.newBufferedReader(Paths.get(filename))) {
String line;
int lineCount = 0;
while ((line = reader.readLine()) != null) {
processLine(line);
if (++lineCount % 10000 == 0) {
System.out.println("Processed " + lineCount + " lines");
}
}
}
}
private static void processLine(String line) {
// 处理单行数据
}
// 二进制文件高效复制
public static void copyBinaryFile(String source, String target) throws IOException {
try (FileChannel sourceChannel = FileChannel.open(Paths.get(source), StandardOpenOption.READ);
FileChannel targetChannel = FileChannel.open(Paths.get(target),
StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
sourceChannel.transferTo(0, sourceChannel.size(), targetChannel);
}
}
}第二章:字节流详解与实战应用2.1 InputStream核心类详解2.1.1 FileInputStream文件字节输入流FileInputStream是最常用的字节输入流,用于从文件中读取字节数据:
代码语言:javascript复制public class FileInputStreamExample {
// 基础文件读取
public static void basicFileRead(String filename) throws IOException {
FileInputStream fis = null;
try {
fis = new FileInputStream(filename);
int byteData;
while ((byteData = fis.read()) != -1) {
System.out.print((char) byteData);
}
} finally {
if (fis != null) {
fis.close();
}
}
}
// 使用try-with-resources的改进版本
public static void improvedFileRead(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(filename)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
System.out.write(buffer, 0, bytesRead);
}
}
}
// 读取文件的特定部分
public static byte[] readFileSegment(String filename, long offset, int length)
throws IOException {
try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
raf.seek(offset);
byte[] data = new byte[length];
int bytesRead = raf.read(data);
if (bytesRead < length) {
return Arrays.copyOf(data, bytesRead);
}
return data;
}
}
}2.1.2 ByteArrayInputStream内存字节流ByteArrayInputStream允许将字节数组作为输入源:
代码语言:javascript复制public class ByteArrayInputStreamExample {
public static void demonstrateByteArrayStream() throws IOException {
String data = "Hello, Java IO Streams!";
byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
// 读取前5个字节
byte[] buffer = new byte[5];
int bytesRead = bais.read(buffer);
System.out.println("Read: " + new String(buffer, 0, bytesRead));
// 标记当前位置
bais.mark(10);
// 继续读取
int nextByte = bais.read();
System.out.println("Next byte: " + (char) nextByte);
// 重置到标记位置
bais.reset();
// 再次读取
nextByte = bais.read();
System.out.println("After reset: " + (char) nextByte);
}
}
}2.2 OutputStream核心类详解2.2.1 FileOutputStream文件字节输出流代码语言:javascript复制public class FileOutputStreamExample {
// 基础文件写入
public static void writeToFile(String filename, String content) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filename)) {
byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
fos.write(bytes);
fos.flush(); // 确保数据写入磁盘
}
}
// 追加模式写入
public static void appendToFile(String filename, String content) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filename, true)) {
fos.write(content.getBytes(StandardCharsets.UTF_8));
fos.write(System.lineSeparator().getBytes());
}
}
// 分块写入大量数据
public static void writeDataInChunks(String filename, byte[] data) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filename);
BufferedOutputStream bos = new BufferedOutputStream(fos, 8192)) {
int chunkSize = 1024;
for (int i = 0; i < data.length; i += chunkSize) {
int length = Math.min(chunkSize, data.length - i);
bos.write(data, i, length);
// 每写入1MB数据就刷新一次
if (i % (1024 * 1024) == 0) {
bos.flush();
}
}
}
}
}2.3 文件字节流实战应用2.3.1 文件复制工具实现代码语言:javascript复制public class FileCopyUtility {
// 基础文件复制
public static void copyFile(String source, String target) throws IOException {
try (FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(target)) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
}
}
// 带进度显示的文件复制
public static void copyFileWithProgress(String source, String target) throws IOException {
File sourceFile = new File(source);
long totalBytes = sourceFile.length();
long copiedBytes = 0;
try (FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(target)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
copiedBytes += bytesRead;
// 显示进度
int progress = (int) ((copiedBytes * 100) / totalBytes);
System.out.printf("\rProgress: %d%% (%d/%d bytes)",
progress, copiedBytes, totalBytes);
}
System.out.println("\nCopy completed!");
}
}
// 多线程文件复制
public static void copyFileMultiThreaded(String source, String target, int threadCount)
throws IOException, InterruptedException {
File sourceFile = new File(source);
long fileSize = sourceFile.length();
long chunkSize = fileSize / threadCount;
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
CountDownLatch latch = new CountDownLatch(threadCount);
try (RandomAccessFile sourceRaf = new RandomAccessFile(source, "r");
RandomAccessFile targetRaf = new RandomAccessFile(target, "rw")) {
targetRaf.setLength(fileSize); // 预分配文件大小
for (int i = 0; i < threadCount; i++) {
long start = i * chunkSize;
long end = (i == threadCount - 1) ? fileSize : (i + 1) * chunkSize;
executor.submit(() -> {
try {
copyFileChunk(sourceRaf, targetRaf, start, end - start);
} catch (IOException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
});
}
latch.await();
} finally {
executor.shutdown();
}
}
private static void copyFileChunk(RandomAccessFile source, RandomAccessFile target,
long offset, long length) throws IOException {
synchronized (source) {
source.seek(offset);
}
byte[] buffer = new byte[8192];
long remaining = length;
while (remaining > 0) {
int toRead = (int) Math.min(buffer.length, remaining);
int bytesRead;
synchronized (source) {
bytesRead = source.read(buffer, 0, toRead);
}
if (bytesRead == -1) break;
synchronized (target) {
target.seek(offset + (length - remaining));
target.write(buffer, 0, bytesRead);
}
remaining -= bytesRead;
}
}
}2.4 数据流与对象流深度应用2.4.1 DataInputStream和DataOutputStream数据流提供了读写Java基本数据类型的便捷方法:
代码语言:javascript复制public class DataStreamExample {
// 写入各种数据类型
public static void writeDataTypes(String filename) throws IOException {
try (DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream(filename)))) {
// 写入各种基本数据类型
dos.writeBoolean(true);
dos.writeByte(127);
dos.writeShort(32767);
dos.writeInt(2147483647);
dos.writeLong(9223372036854775807L);
dos.writeFloat(3.14159f);
dos.writeDouble(2.718281828459045);
dos.writeUTF("Hello, DataStream!");
// 写入数组
int[] numbers = {1, 2, 3, 4, 5};
dos.writeInt(numbers.length);
for (int number : numbers) {
dos.writeInt(number);
}
}
}
// 读取各种数据类型
public static void readDataTypes(String filename) throws IOException {
try (DataInputStream dis = new DataInputStream(
new BufferedInputStream(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();
String stringValue = dis.readUTF();
System.out.printf("Boolean: %b, Byte: %d, Short: %d%n",
boolValue, byteValue, shortValue);
System.out.printf("Int: %d, Long: %d%n", intValue, longValue);
System.out.printf("Float: %f, Double: %f%n", floatValue, doubleValue);
System.out.printf("String: %s%n", stringValue);
// 读取数组
int arrayLength = dis.readInt();
int[] numbers = new int[arrayLength];
for (int i = 0; i < arrayLength; i++) {
numbers[i] = dis.readInt();
}
System.out.println("Array: " + Arrays.toString(numbers));
}
}
}2.4.2 ObjectInputStream和ObjectOutputStream对象流支持Java对象的序列化和反序列化:
代码语言:javascript复制// 可序列化的用户类
class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private transient String password; // transient字段不会被序列化
public User(String name, int age, String password) {
this.name = name;
this.age = age;
this.password = password;
}
// getter和setter方法
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
@Override
public String toString() {
return String.format("User{name='%s', age=%d, password='%s'}",
name, age, password);
}
}
public class ObjectStreamExample {
// 序列化对象到文件
public static void serializeObjects(String filename) throws IOException {
List
new User("Alice", 25, "secret123"),
new User("Bob", 30, "password456"),
new User("Charlie", 35, "mypassword")
);
try (ObjectOutputStream oos = new ObjectOutputStream(
new BufferedOutputStream(new FileOutputStream(filename)))) {
oos.writeObject(users);
oos.writeInt(42); // 可以混合写入其他数据
oos.writeUTF("Serialization completed");
}
}
// 从文件反序列化对象
@SuppressWarnings("unchecked")
public static void deserializeObjects(String filename) throws IOException, ClassNotFoundException {
try (ObjectInputStream ois = new ObjectInputStream(
new BufferedInputStream(new FileInputStream(filename)))) {
List
int number = ois.readInt();
String message = ois.readUTF();
System.out.println("Deserialized users:");
users.forEach(System.out::println);
System.out.println("Number: " + number);
System.out.println("Message: " + message);
}
}
// 自定义序列化控制
public static class CustomSerializableClass implements Serializable {
private static final long serialVersionUID = 1L;
private String data;
private transient int computedValue;
public CustomSerializableClass(String data) {
this.data = data;
this.computedValue = data.hashCode();
}
// 自定义序列化方法
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject(); // 序列化非transient字段
oos.writeInt(data.length()); // 序列化额外信息
}
// 自定义反序列化方法
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject(); // 反序列化非transient字段
int dataLength = ois.readInt(); // 读取额外信息
this.computedValue = data.hashCode(); // 重新计算transient字段
System.out.println("Custom deserialization: data length = " + dataLength);
}
@Override
public String toString() {
return String.format("CustomSerializableClass{data='%s', computedValue=%d}",
data, computedValue);
}
}
}第三章:字符流深入分析与使用场景3.1 Reader字符输入流体系3.1.1 FileReader文件字符读取FileReader是处理文本文件的首选字符流:
代码语言:javascript复制public class FileReaderExample {
// 基础文件读取
public static String readTextFile(String filename) throws IOException {
StringBuilder content = new StringBuilder();
try (FileReader reader = new FileReader(filename, StandardCharsets.UTF_8)) {
char[] buffer = new char[1024];
int charsRead;
while ((charsRead = reader.read(buffer)) != -1) {
content.append(buffer, 0, charsRead);
}
}
return content.toString();
}
// 逐行读取文件
public static List
List
try (BufferedReader reader = new BufferedReader(
new FileReader(filename, StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
lines.add(line);
}
}
return lines;
}
// 使用Java 8 Stream API读取文件
public static void processFileWithStream(String filename) throws IOException {
try (BufferedReader reader = Files.newBufferedReader(Paths.get(filename))) {
reader.lines()
.filter(line -> !line.trim().isEmpty())
.map(String::toUpperCase)
.forEach(System.out::println);
}
}
}3.1.2 StringReader和CharArrayReader这些内存字符流用于处理字符串和字符数组:
代码语言:javascript复制public class MemoryCharacterStreamExample {
public static void demonstrateStringReader() throws IOException {
String text = "Line 1\nLine 2\nLine 3\nLine 4";
try (StringReader stringReader = new StringReader(text);
BufferedReader bufferedReader = new BufferedReader(stringReader)) {
String line;
int lineNumber = 1;
while ((line = bufferedReader.readLine()) != null) {
System.out.printf("Line %d: %s%n", lineNumber++, line);
}
}
}
public static void demonstrateCharArrayReader() throws IOException {
char[] charArray = "Hello, Character Streams!".toCharArray();
try (CharArrayReader reader = new CharArrayReader(charArray)) {
// 标记支持测试
if (reader.markSupported()) {
reader.mark(5);
// 读取前5个字符
char[] buffer = new char[5];
int charsRead = reader.read(buffer);
System.out.println("First 5 chars: " + new String(buffer, 0, charsRead));
// 重置并重新读取
reader.reset();
charsRead = reader.read(buffer);
System.out.println("After reset: " + new String(buffer, 0, charsRead));
}
}
}
}3.2 Writer字符输出流体系3.2.1 FileWriter文件字符写入代码语言:javascript复制public class FileWriterExample {
// 基础文件写入
public static void writeTextFile(String filename, String content) throws IOException {
try (FileWriter writer = new FileWriter(filename, StandardCharsets.UTF_8)) {
writer.write(content);
writer.flush();
}
}
// 格式化写入
public static void writeFormattedData(String filename, List
try (BufferedWriter writer = new BufferedWriter(
new FileWriter(filename, StandardCharsets.UTF_8))) {
// 写入表头
writer.write("Name,Age,Email");
writer.newLine();
// 写入数据
for (Person person : persons) {
writer.write(String.format("%s,%d,%s",
person.getName(), person.getAge(), person.getEmail()));
writer.newLine();
}
}
}
// 追加写入日志
public static void appendLog(String logFile, String message) throws IOException {
try (FileWriter writer = new FileWriter(logFile, StandardCharsets.UTF_8, true)) {
String timestamp = LocalDateTime.now().format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
writer.write(String.format("[%s] %s%n", timestamp, message));
}
}
}
// 辅助类
class Person {
private String name;
private int age;
private String email;
public Person(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
// getter方法
public String getName() { return name; }
public int getAge() { return age; }
public String getEmail() { return email; }
}3.3 字符编码与转换流3.3.1 InputStreamReader和OutputStreamWriter转换流是字节流和字符流之间的桥梁:
代码语言:javascript复制public class ConversionStreamExample {
// 指定编码读取文件
public static String readFileWithEncoding(String filename, String encoding) throws IOException {
try (InputStreamReader reader = new InputStreamReader(
new FileInputStream(filename), encoding);
BufferedReader bufferedReader = new BufferedReader(reader)) {
return bufferedReader.lines()
.collect(Collectors.joining(System.lineSeparator()));
}
}
// 指定编码写入文件
public static void writeFileWithEncoding(String filename, String content, String encoding)
throws IOException {
try (OutputStreamWriter writer = new OutputStreamWriter(
new FileOutputStream(filename), encoding);
BufferedWriter bufferedWriter = new BufferedWriter(writer)) {
bufferedWriter.write(content);
}
}
// 编码转换工具
public static void convertFileEncoding(String sourceFile, String targetFile,
String sourceEncoding, String targetEncoding)
throws IOException {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(sourceFile), sourceEncoding));
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(targetFile), targetEncoding))) {
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
}
}
// 检测文件编码(简化版本)
public static String detectEncoding(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(filename)) {
byte[] bom = new byte[4];
int bytesRead = fis.read(bom);
if (bytesRead >= 3) {
if (bom[0] == (byte) 0xEF && bom[1] == (byte) 0xBB && bom[2] == (byte) 0xBF) {
return "UTF-8";
}
if (bom[0] == (byte) 0xFF && bom[1] == (byte) 0xFE) {
return "UTF-16LE";
}
if (bom[0] == (byte) 0xFE && bom[1] == (byte) 0xFF) {
return "UTF-16BE";
}
}
// 默认返回系统编码
return System.getProperty("file.encoding");
}
}
}3.4 打印流与格式化输出3.4.1 PrintWriter高级应用代码语言:javascript复制public class PrintWriterExample {
// 格式化输出到文件
public static void generateReport(String filename, List
try (PrintWriter writer = new PrintWriter(
new BufferedWriter(new FileWriter(filename, StandardCharsets.UTF_8)))) {
// 输出报表头
writer.println("=".repeat(60));
writer.printf("%-20s %10s %15s %10s%n", "Product", "Quantity", "Unit Price", "Total");
writer.println("=".repeat(60));
double grandTotal = 0;
for (SalesRecord record : records) {
double total = record.getQuantity() * record.getUnitPrice();
writer.printf("%-20s %10d %15.2f %10.2f%n",
record.getProduct(), record.getQuantity(), record.getUnitPrice(), total);
grandTotal += total;
}
writer.println("-".repeat(60));
writer.printf("%-46s %10.2f%n", "Grand Total:", grandTotal);
writer.println("=".repeat(60));
// 检查是否有错误
if (writer.checkError()) {
System.err.println("Error occurred while writing to file");
}
}
}
// 多目标输出
public static void multiTargetOutput(String message) throws IOException {
// 同时输出到控制台和文件
try (PrintWriter fileWriter = new PrintWriter(
new FileWriter("output.log", StandardCharsets.UTF_8, true))) {
PrintWriter consoleWriter = new PrintWriter(System.out, true);
String timestamp = LocalDateTime.now().format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
String formattedMessage = String.format("[%s] %s", timestamp, message);
// 输出到控制台
consoleWriter.println(formattedMessage);
// 输出到文件
fileWriter.println(formattedMessage);
}
}
}
// 辅助类
class SalesRecord {
private String product;
private int quantity;
private double unitPrice;
public SalesRecord(String product, int quantity, double unitPrice) {
this.product = product;
this.quantity = quantity;
this.unitPrice = unitPrice;
}
// getter方法
public String getProduct() { return product; }
public int getQuantity() { return quantity; }
public double getUnitPrice() { return unitPrice; }
}第四章:缓冲流性能优化与最佳实践4.1 字节缓冲流性能分析4.1.1 BufferedInputStream性能优化代码语言:javascript复制public class BufferedStreamPerformance {
// 性能对比测试
public static void performanceComparison(String filename) throws IOException {
byte[] testData = generateTestData(1024 * 1024); // 1MB测试数据
// 写入测试数据
try (FileOutputStream fos = new FileOutputStream(filename)) {
fos.write(testData);
}
// 测试无缓冲读取
long startTime = System.nanoTime();
readWithoutBuffer(filename);
long unbufferedTime = System.nanoTime() - startTime;
// 测试缓冲读取
startTime = System.nanoTime();
readWithBuffer(filename);
long bufferedTime = System.nanoTime() - startTime;
// 测试自定义缓冲区大小
startTime = System.nanoTime();
readWithCustomBuffer(filename, 8192);
long customBufferedTime = System.nanoTime() - startTime;
System.out.printf("Unbuffered read: %.2f ms%n", unbufferedTime / 1_000_000.0);
System.out.printf("Buffered read: %.2f ms%n", bufferedTime / 1_000_000.0);
System.out.printf("Custom buffered read: %.2f ms%n", customBufferedTime / 1_000_000.0);
System.out.printf("Buffered is %.2fx faster than unbuffered%n",
(double) unbufferedTime / bufferedTime);
}
private static void readWithoutBuffer(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(filename)) {
int byteData;
while ((byteData = fis.read()) != -1) {
// 模拟处理
}
}
}
private static void readWithBuffer(String filename) throws IOException {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filename))) {
int byteData;
while ((byteData = bis.read()) != -1) {
// 模拟处理
}
}
}
private static void readWithCustomBuffer(String filename, int bufferSize) throws IOException {
try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(filename), bufferSize)) {
int byteData;
while ((byteData = bis.read()) != -1) {
// 模拟处理
}
}
}
private static byte[] generateTestData(int size) {
byte[] data = new byte[size];
new Random().nextBytes(data);
return data;
}
}4.1.2 缓冲区大小优化策略代码语言:javascript复制public class BufferSizeOptimization {
// 动态缓冲区大小调整
public static void adaptiveBufferCopy(String source, String target) throws IOException {
File sourceFile = new File(source);
long fileSize = sourceFile.length();
// 根据文件大小选择缓冲区大小
int bufferSize = calculateOptimalBufferSize(fileSize);
try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(source), bufferSize);
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(target), bufferSize)) {
byte[] buffer = new byte[bufferSize];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
}
}
private static int calculateOptimalBufferSize(long fileSize) {
if (fileSize < 1024) {
return 512; // 小文件使用小缓冲区
} else if (fileSize < 1024 * 1024) {
return 4096; // 中等文件使用4KB缓冲区
} else if (fileSize < 10 * 1024 * 1024) {
return 8192; // 大文件使用8KB缓冲区
} else {
return 16384; // 超大文件使用16KB缓冲区
}
}
// 缓冲区大小基准测试
public static void benchmarkBufferSizes(String filename) throws IOException {
int[] bufferSizes = {1024, 2048, 4096, 8192, 16384, 32768, 65536};
System.out.println("Buffer Size\tRead Time (ms)\tWrite Time (ms)");
System.out.println("-".repeat(50));
for (int bufferSize : bufferSizes) {
long readTime = benchmarkRead(filename, bufferSize);
long writeTime = benchmarkWrite(filename + ".copy", bufferSize);
System.out.printf("%d\t\t%.2f\t\t%.2f%n",
bufferSize, readTime / 1_000_000.0, writeTime / 1_000_000.0);
}
}
private static long benchmarkRead(String filename, int bufferSize) throws IOException {
long startTime = System.nanoTime();
try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(filename), bufferSize)) {
byte[] buffer = new byte[bufferSize];
while (bis.read(buffer) != -1) {
// 读取数据
}
}
return System.nanoTime() - startTime;
}
private static long benchmarkWrite(String filename, int bufferSize) throws IOException {
byte[] testData = new byte[1024 * 1024]; // 1MB测试数据
new Random().nextBytes(testData);
long startTime = System.nanoTime();
try (BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(filename), bufferSize)) {
bos.write(testData);
}
return System.nanoTime() - startTime;
}
}4.2 字符缓冲流高效应用4.2.1 BufferedReader高级技巧代码语言:javascript复制public class BufferedReaderAdvanced {
// 大文件逐行处理
public static void processLargeTextFile(String filename, Function
throws IOException {
try (BufferedReader reader = new BufferedReader(
new FileReader(filename, StandardCharsets.UTF_8), 16384)) {
String line;
int lineNumber = 0;
while ((line = reader.readLine()) != null) {
lineNumber++;
String processedLine = processor.apply(line);
if (processedLine != null) {
System.out.printf("Line %d: %s%n", lineNumber, processedLine);
}
// 每处理10000行显示进度
if (lineNumber % 10000 == 0) {
System.out.printf("Processed %d lines...%n", lineNumber);
}
}
}
}
// 文件内容搜索
public static List
throws IOException {
List
try (BufferedReader reader = new BufferedReader(
new FileReader(filename, StandardCharsets.UTF_8))) {
String line;
int lineNumber = 0;
while ((line = reader.readLine()) != null) {
lineNumber++;
int index = line.toLowerCase().indexOf(searchTerm.toLowerCase());
if (index != -1) {
results.add(new SearchResult(lineNumber, index, line));
}
}
}
return results;
}
// 文件统计信息
public static FileStatistics analyzeTextFile(String filename) throws IOException {
FileStatistics stats = new FileStatistics();
try (BufferedReader reader = new BufferedReader(
new FileReader(filename, StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
stats.incrementLineCount();
stats.addCharacterCount(line.length());
stats.addWordCount(countWords(line));
if (line.trim().isEmpty()) {
stats.incrementEmptyLineCount();
}
}
}
return stats;
}
private static int countWords(String line) {
return line.trim().isEmpty() ? 0 : line.trim().split("\\s+").length;
}
}
// 辅助类
class SearchResult {
private int lineNumber;
private int position;
private String line;
public SearchResult(int lineNumber, int position, String line) {
this.lineNumber = lineNumber;
this.position = position;
this.line = line;
}
@Override
public String toString() {
return String.format("Line %d, Position %d: %s", lineNumber, position, line);
}
}
class FileStatistics {
private int lineCount = 0;
private int emptyLineCount = 0;
private long characterCount = 0;
private long wordCount = 0;
public void incrementLineCount() { lineCount++; }
public void incrementEmptyLineCount() { emptyLineCount++; }
public void addCharacterCount(int count) { characterCount += count; }
public void addWordCount(int count) { wordCount += count; }
@Override
public String toString() {
return String.format("Lines: %d, Empty Lines: %d, Characters: %d, Words: %d",
lineCount, emptyLineCount, characterCount, wordCount);
}
}4.3 缓冲区大小优化策略4.3.1 内存使用与性能平衡代码语言:javascript复制public class MemoryPerformanceBalance {
// 内存敏感的文件处理
public static void memoryEfficientProcessing(String filename, long maxMemoryUsage)
throws IOException {
// 根据内存限制计算缓冲区大小
int bufferSize = calculateBufferSize(maxMemoryUsage);
try (BufferedReader reader = new BufferedReader(
new FileReader(filename, StandardCharsets.UTF_8), bufferSize)) {
String line;
int processedLines = 0;
long memoryUsed = 0;
while ((line = reader.readLine()) != null) {
// 估算内存使用量
memoryUsed += line.length() * 2; // 字符占用2字节
processLine(line);
processedLines++;
// 定期检查内存使用
if (processedLines % 1000 == 0) {
if (memoryUsed > maxMemoryUsage * 0.8) {
System.gc(); // 建议垃圾回收
memoryUsed = 0; // 重置计数
}
}
}
}
}
private static int calculateBufferSize(long maxMemoryUsage) {
// 缓冲区不超过最大内存的10%
long maxBufferSize = maxMemoryUsage / 10;
return (int) Math.min(maxBufferSize, 65536); // 最大64KB
}
private static void processLine(String line) {
// 模拟行处理
}
// 自适应缓冲区管理
public static class AdaptiveBuffer {
private int currentBufferSize;
private final int minBufferSize;
private final int maxBufferSize;
private long lastOperationTime;
public AdaptiveBuffer(int minSize, int maxSize) {
this.minBufferSize = minSize;
this.maxBufferSize = maxSize;
this.currentBufferSize = minSize;
}
public int getBufferSize() {
return currentBufferSize;
}
public void recordOperationTime(long operationTime) {
// 根据操作时间调整缓冲区大小
if (operationTime > lastOperationTime * 1.2 && currentBufferSize < maxBufferSize) {
// 性能下降,增加缓冲区
currentBufferSize = Math.min(currentBufferSize * 2, maxBufferSize);
} else if (operationTime < lastOperationTime * 0.8 && currentBufferSize > minBufferSize) {
// 性能提升,可以减少缓冲区以节省内存
currentBufferSize = Math.max(currentBufferSize / 2, minBufferSize);
}
lastOperationTime = operationTime;
}
}
}4.4 内存映射文件与零拷贝技术4.4.1 MappedByteBuffer应用代码语言:javascript复制public class MemoryMappedFileExample {
// 内存映射文件读取
public static void readWithMemoryMapping(String filename) throws IOException {
try (RandomAccessFile file = new RandomAccessFile(filename, "r");
FileChannel channel = file.getChannel()) {
long fileSize = channel.size();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
// 直接从内存读取
byte[] data = new byte[1024];
while (buffer.hasRemaining()) {
int bytesToRead = Math.min(data.length, buffer.remaining());
buffer.get(data, 0, bytesToRead);
processData(data, bytesToRead);
}
}
}
// 内存映射文件写入
public static void writeWithMemoryMapping(String filename, byte[] data) throws IOException {
try (RandomAccessFile file = new RandomAccessFile(filename, "rw");
FileChannel channel = file.getChannel()) {
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, data.length);
buffer.put(data);
buffer.force(); // 强制写入磁盘
}
}
// 大文件分段映射
public static void processLargeFileWithMapping(String filename) throws IOException {
try (RandomAccessFile file = new RandomAccessFile(filename, "r");
FileChannel channel = file.getChannel()) {
long fileSize = channel.size();
long mappingSize = 64 * 1024 * 1024; // 64MB分段
for (long position = 0; position < fileSize; position += mappingSize) {
long size = Math.min(mappingSize, fileSize - position);
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, position, size);
processBuffer(buffer);
// 显示进度
double progress = (double) (position + size) / fileSize * 100;
System.out.printf("Progress: %.2f%%%n", progress);
}
}
}
private static void processData(byte[] data, int length) {
// 处理数据
}
private static void processBuffer(MappedByteBuffer buffer) {
// 处理映射缓冲区
while (buffer.hasRemaining()) {
byte b = buffer.get();
// 处理字节
}
}
// 零拷贝文件传输
public static void zeroCopyTransfer(String source, String target) throws IOException {
try (FileChannel sourceChannel = FileChannel.open(Paths.get(source), StandardOpenOption.READ);
FileChannel targetChannel = FileChannel.open(Paths.get(target),
StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
long transferred = 0;
long fileSize = sourceChannel.size();
while (transferred < fileSize) {
long bytesTransferred = sourceChannel.transferTo(transferred,
fileSize - transferred, targetChannel);
transferred += bytesTransferred;
// 显示进度
double progress = (double) transferred / fileSize * 100;
System.out.printf("Transfer progress: %.2f%%%n", progress);
}
}
}
}第五章:高级流操作与NIO新特性5.1 NIO核心组件详解5.1.1 Channel通道基础Java NIO引入了Channel概念,提供了更高效的IO操作方式:
代码语言:javascript复制public class ChannelBasics {
// FileChannel基础操作
public static void fileChannelBasics(String filename) throws IOException {
// 读取操作
try (FileChannel readChannel = FileChannel.open(Paths.get(filename), StandardOpenOption.READ)) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (readChannel.read(buffer) > 0) {
buffer.flip(); // 切换到读模式
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
buffer.clear(); // 清空缓冲区,准备下次读取
}
}
// 写入操作
try (FileChannel writeChannel = FileChannel.open(Paths.get(filename + ".copy"),
StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
String content = "Hello, NIO Channel!";
ByteBuffer buffer = ByteBuffer.wrap(content.getBytes(StandardCharsets.UTF_8));
while (buffer.hasRemaining()) {
writeChannel.write(buffer);
}
}
}
// Channel之间的数据传输
public static void channelToChannelTransfer(String source, String target) throws IOException {
try (FileChannel sourceChannel = FileChannel.open(Paths.get(source), StandardOpenOption.READ);
FileChannel targetChannel = FileChannel.open(Paths.get(target),
StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
// 方法1:transferFrom
targetChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
// 方法2:transferTo(在另一个方法中演示)
// sourceChannel.transferTo(0, sourceChannel.size(), targetChannel);
}
}
// 随机访问文件
public static void randomAccessWithChannel(String filename) throws IOException {
try (FileChannel channel = FileChannel.open(Paths.get(filename),
StandardOpenOption.READ, StandardOpenOption.WRITE)) {
// 在文件末尾写入数据
long fileSize = channel.size();
ByteBuffer buffer = ByteBuffer.wrap("\nAppended text".getBytes());
channel.write(buffer, fileSize);
// 读取文件开头的数据
buffer = ByteBuffer.allocate(10);
channel.read(buffer, 0);
buffer.flip();
System.out.println("First 10 bytes: " + StandardCharsets.UTF_8.decode(buffer));
}
}
}5.2 Channel通道操作实战5.2.1 网络Channel应用代码语言:javascript复制public class NetworkChannelExample {
// NIO服务器示例
public static void startNIOServer(int port) throws IOException {
try (ServerSocketChannel serverChannel = ServerSocketChannel.open()) {
serverChannel.bind(new InetSocketAddress(port));
serverChannel.configureBlocking(false);
Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NIO Server started on port " + port);
while (true) {
if (selector.select() == 0) {
continue;
}
Set
Iterator
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
handleAccept(key, selector);
} else if (key.isReadable()) {
handleRead(key);
}
}
}
}
}
private static void handleAccept(SelectionKey key, Selector selector) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = serverChannel.accept();
if (clientChannel != null) {
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ);
System.out.println("Client connected: " + clientChannel.getRemoteAddress());
}
}
private static void handleRead(SelectionKey key) throws IOException {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
try {
int bytesRead = clientChannel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
String message = StandardCharsets.UTF_8.decode(buffer).toString();
System.out.println("Received: " + message);
// 回显消息
ByteBuffer response = ByteBuffer.wrap(("Echo: " + message).getBytes());
clientChannel.write(response);
} else if (bytesRead == -1) {
// 客户端断开连接
System.out.println("Client disconnected");
key.cancel();
clientChannel.close();
}
} catch (IOException e) {
System.err.println("Error reading from client: " + e.getMessage());
key.cancel();
clientChannel.close();
}
}
// NIO客户端示例
public static void connectToNIOServer(String host, int port, String message) throws IOException {
try (SocketChannel channel = SocketChannel.open()) {
channel.connect(new InetSocketAddress(host, port));
// 发送消息
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8));
channel.write(buffer);
// 读取响应
buffer = ByteBuffer.allocate(1024);
int bytesRead = channel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
String response = StandardCharsets.UTF_8.decode(buffer).toString();
System.out.println("Server response: " + response);
}
}
}
}5.3 Buffer缓冲区高级应用5.3.1 ByteBuffer高级操作代码语言:javascript复制public class ByteBufferAdvanced {
// ByteBuffer的各种创建方式
public static void bufferCreationMethods() {
// 分配堆内存缓冲区
ByteBuffer heapBuffer = ByteBuffer.allocate(1024);
// 分配直接内存缓冲区(更高效,但创建成本高)
ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);
// 包装现有字节数组
byte[] array = new byte[1024];
ByteBuffer wrappedBuffer = ByteBuffer.wrap(array);
// 包装数组的一部分
ByteBuffer partialBuffer = ByteBuffer.wrap(array, 10, 100);
System.out.println("Heap buffer: " + heapBuffer.isDirect());
System.out.println("Direct buffer: " + directBuffer.isDirect());
}
// Buffer的标记和重置
public static void bufferMarkAndReset() {
ByteBuffer buffer = ByteBuffer.allocate(20);
// 写入一些数据
buffer.put("Hello".getBytes());
System.out.println("After put: position=" + buffer.position() + ", limit=" + buffer.limit());
// 标记当前位置
buffer.mark();
// 继续写入
buffer.put(" World".getBytes());
System.out.println("After more put: position=" + buffer.position());
// 重置到标记位置
buffer.reset();
System.out.println("After reset: position=" + buffer.position());
// 切换到读模式
buffer.flip();
// 读取数据
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
System.out.println("Read data: " + new String(data));
}
// Buffer的切片和复制
public static void bufferSliceAndDuplicate() {
ByteBuffer original = ByteBuffer.allocate(20);
original.put("Hello World Test".getBytes());
original.flip();
// 移动到位置5
original.position(5);
// 创建切片(共享数据,但有独立的位置标记)
ByteBuffer slice = original.slice();
System.out.println("Slice remaining: " + slice.remaining());
// 创建副本(共享数据,但有独立的位置标记)
original.rewind();
ByteBuffer duplicate = original.duplicate();
System.out.println("Duplicate remaining: " + duplicate.remaining());
// 修改切片会影响原始缓冲区
slice.put(0, (byte) 'X');
original.rewind();
byte[] result = new byte[original.remaining()];
original.get(result);
System.out.println("After slice modification: " + new String(result));
}
}5.3.2 多种Buffer类型应用代码语言:javascript复制public class MultipleBufferTypes {
// IntBuffer示例
public static void intBufferExample() {
IntBuffer intBuffer = IntBuffer.allocate(10);
// 写入整数
for (int i = 1; i <= 5; i++) {
intBuffer.put(i * 10);
}
intBuffer.flip();
// 读取整数
while (intBuffer.hasRemaining()) {
System.out.println("Int value: " + intBuffer.get());
}
}
// CharBuffer与字符串处理
public static void charBufferExample() {
CharBuffer charBuffer = CharBuffer.allocate(50);
// 写入字符
charBuffer.put("Hello, NIO CharBuffer!");
charBuffer.flip();
// 转换为字符串
String result = charBuffer.toString();
System.out.println("CharBuffer content: " + result);
// 使用CharBuffer进行字符串操作
charBuffer.rewind();
StringBuilder sb = new StringBuilder();
while (charBuffer.hasRemaining()) {
char c = charBuffer.get();
sb.append(Character.toUpperCase(c));
}
System.out.println("Uppercase: " + sb.toString());
}
// ByteBuffer与其他类型Buffer的转换
public static void bufferViewExample() {
ByteBuffer byteBuffer = ByteBuffer.allocate(40);
// 写入不同类型的数据
byteBuffer.putInt(42);
byteBuffer.putDouble(3.14159);
byteBuffer.putLong(123456789L);
byteBuffer.flip();
// 读取数据
int intValue = byteBuffer.getInt();
double doubleValue = byteBuffer.getDouble();
long longValue = byteBuffer.getLong();
System.out.printf("Values: int=%d, double=%.5f, long=%d%n",
intValue, doubleValue, longValue);
// 创建视图Buffer
byteBuffer.rewind();
IntBuffer intView = byteBuffer.asIntBuffer();
System.out.println("Int view capacity: " + intView.capacity());
}
}5.4 Selector选择器与多路复用5.4.1 Selector基础应用代码语言:javascript复制public class SelectorBasics {
// 基础Selector使用
public static void basicSelectorUsage() throws IOException {
Selector selector = Selector.open();
// 创建ServerSocketChannel
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.configureBlocking(false);
// 注册到Selector
SelectionKey serverKey = serverChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Server started, waiting for connections...");
while (true) {
// 阻塞等待事件
int readyChannels = selector.select();
if (readyChannels == 0) {
continue;
}
Set
Iterator
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
keyIterator.remove();
if (key.isAcceptable()) {
handleAccept(key, selector);
} else if (key.isReadable()) {
handleRead(key);
} else if (key.isWritable()) {
handleWrite(key);
}
}
}
}
private static void handleAccept(SelectionKey key, Selector selector) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = serverChannel.accept();
if (clientChannel != null) {
clientChannel.configureBlocking(false);
// 为客户端通道附加一个缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
SelectionKey clientKey = clientChannel.register(selector, SelectionKey.OP_READ);
clientKey.attach(buffer);
System.out.println("New client connected: " + clientChannel.getRemoteAddress());
}
}
private static void handleRead(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
try {
int bytesRead = channel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String message = new String(data, StandardCharsets.UTF_8);
System.out.println("Received: " + message);
// 准备回写数据
buffer.clear();
buffer.put(("Echo: " + message).getBytes(StandardCharsets.UTF_8));
buffer.flip();
// 注册写事件
key.interestOps(SelectionKey.OP_WRITE);
} else if (bytesRead == -1) {
// 客户端关闭连接
System.out.println("Client disconnected");
key.cancel();
channel.close();
}
} catch (IOException e) {
System.err.println("Error reading from client: " + e.getMessage());
key.cancel();
channel.close();
}
}
private static void handleWrite(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
try {
channel.write(buffer);
if (!buffer.hasRemaining()) {
// 写完了,重新注册读事件
buffer.clear();
key.interestOps(SelectionKey.OP_READ);
}
} catch (IOException e) {
System.err.println("Error writing to client: " + e.getMessage());
key.cancel();
channel.close();
}
}
}5.4.2 高性能NIO服务器实现代码语言:javascript复制public class HighPerformanceNIOServer {
private final int port;
private final Selector selector;
private final ServerSocketChannel serverChannel;
private volatile boolean running = false;
public HighPerformanceNIOServer(int port) throws IOException {
this.port = port;
this.selector = Selector.open();
this.serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(port));
serverChannel.configureBlocking(false);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}
public void start() throws IOException {
running = true;
System.out.println("High-performance NIO server started on port " + port);
while (running) {
try {
int readyChannels = selector.select(1000); // 1秒超时
if (readyChannels == 0) {
continue;
}
processSelectedKeys();
} catch (IOException e) {
System.err.println("Error in server loop: " + e.getMessage());
break;
}
}
cleanup();
}
private void processSelectedKeys() throws IOException {
Set
Iterator
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (!key.isValid()) {
continue;
}
try {
if (key.isAcceptable()) {
handleAccept(key);
} else if (key.isReadable()) {
handleRead(key);
} else if (key.isWritable()) {
handleWrite(key);
}
} catch (IOException e) {
System.err.println("Error handling key: " + e.getMessage());
closeChannel(key);
}
}
}
private void handleAccept(SelectionKey key) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = serverChannel.accept();
if (clientChannel != null) {
clientChannel.configureBlocking(false);
// 创建客户端会话
ClientSession session = new ClientSession(clientChannel);
SelectionKey clientKey = clientChannel.register(selector, SelectionKey.OP_READ);
clientKey.attach(session);
System.out.println("Client connected: " + clientChannel.getRemoteAddress());
}
}
private void handleRead(SelectionKey key) throws IOException {
ClientSession session = (ClientSession) key.attachment();
SocketChannel channel = session.getChannel();
int bytesRead = channel.read(session.getReadBuffer());
if (bytesRead > 0) {
session.processReadData();
// 如果有数据要写回,注册写事件
if (session.hasDataToWrite()) {
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
}
} else if (bytesRead == -1) {
System.out.println("Client disconnected: " + channel.getRemoteAddress());
closeChannel(key);
}
}
private void handleWrite(SelectionKey key) throws IOException {
ClientSession session = (ClientSession) key.attachment();
SocketChannel channel = session.getChannel();
session.writeData();
// 如果没有更多数据要写,取消写事件
if (!session.hasDataToWrite()) {
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
}
}
private void closeChannel(SelectionKey key) {
try {
key.cancel();
key.channel().close();
} catch (IOException e) {
System.err.println("Error closing channel: " + e.getMessage());
}
}
public void stop() {
running = false;
}
private void cleanup() throws IOException {
selector.close();
serverChannel.close();
System.out.println("Server stopped");
}
// 客户端会话类
private static class ClientSession {
private final SocketChannel channel;
private final ByteBuffer readBuffer;
private final ByteBuffer writeBuffer;
public ClientSession(SocketChannel channel) {
this.channel = channel;
this.readBuffer = ByteBuffer.allocate(1024);
this.writeBuffer = ByteBuffer.allocate(1024);
}
public SocketChannel getChannel() {
return channel;
}
public ByteBuffer getReadBuffer() {
return readBuffer;
}
public void processReadData() {
readBuffer.flip();
if (readBuffer.hasRemaining()) {
// 简单的回显处理
writeBuffer.clear();
writeBuffer.put("Echo: ".getBytes());
writeBuffer.put(readBuffer);
writeBuffer.flip();
}
readBuffer.clear();
}
public boolean hasDataToWrite() {
return writeBuffer.hasRemaining();
}
public void writeData() throws IOException {
channel.write(writeBuffer);
}
}
}第六章:总结与展望6.1 知识点总结与技术扩展6.1.1 核心知识点回顾在这里插入图片描述通过本文的深入学习,我们全面掌握了Java流操作的核心技术:
基础流操作体系:
字节流:以InputStream/OutputStream为核心的字节处理体系,适合处理二进制数据字符流:以Reader/Writer为核心的字符处理体系,专门优化文本数据处理转换流:InputStreamReader/OutputStreamWriter作为字节流和字符流的桥梁性能优化策略:
缓冲流:通过BufferedInputStream/BufferedOutputStream显著提升IO性能缓冲区大小调优:根据文件大小和系统资源动态调整缓冲区内存映射:利用MappedByteBuffer实现零拷贝,提升大文件处理效率高级NIO特性:
Channel通道:提供双向数据传输能力,支持异步操作Buffer缓冲区:灵活的数据容器,支持多种数据类型Selector选择器:实现单线程管理多个通道,提升并发性能6.1.2 技术扩展与深化响应式流处理:
代码语言:javascript复制// 结合Java 9+ Flow API的响应式流处理
public class ReactiveStreamExample {
public static void processFileReactively(String filename) {
Flow.Publisher
try (BufferedReader reader = Files.newBufferedReader(Paths.get(filename))) {
reader.lines().forEach(subscriber::onNext);
subscriber.onComplete();
} catch (IOException e) {
subscriber.onError(e);
}
};
// 订阅处理
publisher.subscribe(new Flow.Subscriber
@Override
public void onSubscribe(Flow.Subscription subscription) {
subscription.request(Long.MAX_VALUE);
}
@Override
public void onNext(String item) {
System.out.println("Processing: " + item);
}
@Override
public void onError(Throwable throwable) {
System.err.println("Error: " + throwable.getMessage());
}
@Override
public void onComplete() {
System.out.println("Processing completed");
}
});
}
}云原生IO操作:
对象存储集成:与AWS S3、阿里云OSS等云存储服务的流式交互分布式文件系统:HDFS、GlusterFS等分布式存储的Java客户端应用容器化部署:Docker环境下的IO性能优化和资源限制处理6.2 学习资源与参考资料6.2.1 官方文档与权威资料Oracle官方文档:
Java IO Tutorial - Oracle官方IO教程NIO.2 File API - 新文件API详解Java NIO Selector - Selector API文档经典技术书籍:
《Java NIO》by Ron Hitchens - NIO编程权威指南《Netty in Action》by Norman Maurer - 基于NIO的网络编程框架《Java并发编程实战》by Brian Goetz - 并发环境下的IO操作6.2.2 实战项目与开源框架高性能IO框架:
Netty:基于NIO的异步事件驱动网络应用框架Vert.x:响应式应用开发工具包Reactor Netty:Spring WebFlux底层的响应式网络库文件处理工具:
Apache Commons IO:丰富的IO工具类库Google Guava:包含优秀的IO辅助工具Apache Tika:文件类型检测和内容提取6.2.3 性能测试与监控工具代码语言:javascript复制// JMH基准测试示例
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class IOPerformanceBenchmark {
private static final String TEST_FILE = "benchmark_test.txt";
private static final int FILE_SIZE = 1024 * 1024; // 1MB
@Setup
public void setup() throws IOException {
// 创建测试文件
try (FileOutputStream fos = new FileOutputStream(TEST_FILE)) {
byte[] data = new byte[FILE_SIZE];
new Random().nextBytes(data);
fos.write(data);
}
}
@Benchmark
public void testBufferedRead() throws IOException {
try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(TEST_FILE))) {
byte[] buffer = new byte[8192];
while (bis.read(buffer) != -1) {
// 读取数据
}
}
}
@Benchmark
public void testNIORead() throws IOException {
try (FileChannel channel = FileChannel.open(Paths.get(TEST_FILE))) {
ByteBuffer buffer = ByteBuffer.allocate(8192);
while (channel.read(buffer) != -1) {
buffer.clear();
}
}
}
@TearDown
public void cleanup() {
new File(TEST_FILE).delete();
}
}6.3 技术发展趋势与实践建议6.3.1 未来技术趋势Project Loom与虚拟线程:
Java 19+引入的虚拟线程将革命性地改变IO编程模式,使得传统的阻塞IO也能获得高并发性能。
GraalVM与原生编译:
通过GraalVM将Java应用编译为原生可执行文件,显著提升IO密集型应用的启动速度和内存效率。
云原生与Serverless:
函数计算:AWS Lambda、阿里云函数计算等环境下的IO优化容器化:Kubernetes环境下的存储卷和网络IO管理边缘计算:IoT设备和边缘节点的轻量级IO处理6.3.2 最佳实践建议性能优化原则:
选择合适的流类型:根据数据特性选择字节流或字符流合理使用缓冲:避免频繁的系统调用,提升整体性能资源管理:使用try-with-resources确保资源正确释放异步处理:对于高并发场景,优先考虑NIO或异步IO代码质量保证:
代码语言:javascript复制// 推荐的IO操作模板
public class IOBestPractices {
// 标准的文件读取模板
public static String readFileContent(Path filePath) throws IOException {
try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)) {
return reader.lines()
.collect(Collectors.joining(System.lineSeparator()));
}
}
// 安全的文件写入模板
public static void writeFileContent(Path filePath, String content) throws IOException {
try (BufferedWriter writer = Files.newBufferedWriter(filePath,
StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
writer.write(content);
}
}
// 大文件处理模板
public static void processLargeFile(Path filePath, Consumer
try (Stream
lines.forEach(lineProcessor);
}
}
}6.4 互动与讨论6.4.1 深度思考问题让我们一起探讨几个有趣的技术问题:
性能权衡:在什么情况下直接内存(DirectByteBuffer)比堆内存缓冲区更有优势?如何量化这种性能差异?
并发安全:多线程环境下使用同一个FileChannel是否安全?如何设计线程安全的文件操作工具类?
内存管理:处理超大文件(如几十GB的日志文件)时,如何平衡内存使用和处理效率?
错误处理:在网络IO操作中,如何优雅地处理连接中断、超时等异常情况?
性能监控:如何设计一个通用的IO性能监控框架,实时跟踪应用的IO操作效率?
6.4.2 实战挑战项目挑战1:高性能日志分析器
设计一个能够实时分析大型日志文件的工具,要求:
支持多种日志格式实时统计关键指标内存使用控制在合理范围支持分布式部署挑战2:文件同步工具
实现一个类似rsync的文件同步工具,包含:
增量同步算法网络传输优化断点续传功能冲突解决策略6.4.3 技术交流与分享加入技术社区:
GitHub:分享你的IO工具项目,参与开源贡献Stack Overflow:回答Java IO相关问题,帮助其他开发者技术博客:分享你的实践经验和性能优化心得持续学习建议:
关注Java新版本的IO改进特性学习其他语言的IO模型(如Go的goroutine、Rust的async/await)深入理解操作系统的IO机制结语Java流操作是每个Java开发者必须掌握的核心技能。从传统的BIO到现代的NIO,从简单的文件读写到复杂的网络通信,IO操作贯穿了整个应用开发的生命周期。
通过本文的学习,相信你已经对Java流操作有了全面而深入的理解。但技术的学习永无止境,希望你能够:
持续实践:将所学知识应用到实际项目中深入研究:探索更高级的IO优化技术分享交流:与其他开发者分享经验和心得关注发展:跟上Java IO技术的最新发展如果这篇文章对你有帮助,请不要忘记:
👍 点赞支持:让更多人看到这篇技术分享⭐ 收藏备用:方便日后查阅和复习🔄 转发分享:帮助更多Java开发者提升技能💬 评论讨论:分享你的见解和实践经验让我们一起在Java技术的道路上不断前进,共同构建更高效、更优雅的应用程序!