输入输出(I/O):熟悉 Java 的 I/O 类库,尤其是 NIO 和文件操作
输入输出(I/O):熟悉 Java 的 I/O 类库,尤其是 NIO 和文件操作
在 Java 中,I/O(输入输出)操作是开发中非常重要的一部分,用于与文件、网络和其他数据流交互。Java 提供了传统的 I/O(基于流的 I/O)和更现代的 NIO(非阻塞 I/O)两种方式。本文将带你熟悉 Java I/O 的核心类库,并重点介绍 NIO 的特点及文件操作相关内容。
一、Java I/O 的基本概念
-
1.1 流(Stream)
流(Stream)是 Java I/O 的核心概念,代表了数据传输的路径。在 Java 中,流的概念被分为 输入流 和 输出流,并且每种流都可以分为字节流和字符流。
- 输入流(Input Stream): 从数据源读取数据。
- 输出流(Output Stream): 向目标写入数据。
字节流与字符流
- 字节流(Byte Stream): 以字节为单位进行数据读写。适用于所有类型的I/O操作,包括文本、图像、音频等。
InputStream
和OutputStream
是字节流的根类。- 常用类:
FileInputStream
,FileOutputStream
,BufferedInputStream
,BufferedOutputStream
。
- 字符流(Character Stream): 以字符为单位进行数据读写,适用于文本文件的处理。
Reader
和Writer
是字符流的根类。- 常用类:
FileReader
,FileWriter
,BufferedReader
,BufferedWriter
。
示例代码:字节流和字符流的使用
// 使用字节流读取文件 import java.io.*; public class ByteStreamExample { public static void main(String[] args) throws IOException { FileInputStream inputStream = new FileInputStream("example.txt"); int byteData; while ((byteData = inputStream.read()) != -1) { System.out.print((char) byteData); // 输出文件内容 } inputStream.close(); } } // 使用字符流读取文件 import java.io.*; public class CharStreamExample { public static void main(String[] args) throws IOException { FileReader fileReader = new FileReader("example.txt"); BufferedReader bufferedReader = new BufferedReader(fileReader); String line; while ((line = bufferedReader.readLine()) != null) { System.out.println(line); // 输出文件内容 } bufferedReader.close(); } }
二、常见的 Java I/O 类库
2.1 文件操作类
-
File
类:是 Java 早期提供的文件操作类,提供了创建、删除文件和目录的功能。
- 优点:操作简单。
- 缺点:功能有限,不支持高级操作如复制、移动等。
示例代码:
import java.io.File;
public class FileExample {
public static void main(String[] args) {
File file = new File("example.txt");
// 创建文件
try {
if (file.createNewFile()) {
System.out.println("File created: " + file.getName());
} else {
System.out.println("File already exists.");
}
} catch (Exception e) {
e.printStackTrace();
}
// 删除文件
if (file.delete()) {
System.out.println("Deleted the file: " + file.getName());
}
}
}
-
Files
类:Java 7 引入的 NIO 类,提供了文件操作的高级接口,支持复制、移动、删除等操作。
- 优点:功能丰富,支持文件复制、移动、文件权限操作等。
- 常用方法:
copy()
,move()
,delete()
,exists()
,createDirectory()
等。
示例代码:使用 Files
类进行文件复制
import java.nio.file.*;
public class FilesExample {
public static void main(String[] args) {
Path source = Paths.get("source.txt");
Path target = Paths.get("target.txt");
try {
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
System.out.println("File copied successfully.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
三、Java NIO 的特点与使用
3.1 什么是 NIO?
NIO(New I/O)是 Java 1.4 引入的一组 API,旨在提供比传统 I/O 更高效的操作。它支持基于缓冲区和通道的 I/O 方式,能有效提高性能。
- 传统 I/O: 基于流,采用阻塞方式,即每次只能处理一个连接。
- NIO: 基于缓冲区和通道,支持非阻塞操作,并且能够处理多个 I/O 操作。
3.2 NIO 的核心组件
- 通道(Channel): NIO 中的通道类似于传统 I/O 中的流,但通道支持双向数据传输。常见的通道类包括:
FileChannel
:用于文件的读取和写入。SocketChannel
:用于网络的客户端和服务端通信。ServerSocketChannel
:用于创建服务端套接字通道。
- 缓冲区(Buffer): NIO 中的数据读写都通过缓冲区进行,避免了流的每次读取。常见的缓冲区包括:
ByteBuffer
:用于字节数据的处理。CharBuffer
:用于字符数据的处理。
- 选择器(Selector): 选择器允许单个线程同时监控多个通道的 I/O 操作。常用于网络 I/O。
3.3 NIO 文件操作示例
示例:使用 FileChannel
和 ByteBuffer
读写文件
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class FileChannelExample {
public static void main(String[] args) {
try (RandomAccessFile file = new RandomAccessFile("example.txt", "rw");
FileChannel channel = file.getChannel()) {
// 写数据到文件
ByteBuffer buffer = ByteBuffer.allocate(48);
buffer.put("Hello NIO!".getBytes());
buffer.flip();
channel.write(buffer);
// 读数据从文件
buffer.clear();
channel.read(buffer);
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.4 非阻塞 I/O 示例
示例:使用 Selector
实现多路复用
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
public class NIONonBlockingExample {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
if (key.isAcceptable()) {
SocketChannel client = serverChannel.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(256);
client.read(buffer);
System.out.println("Received: " + new String(buffer.array()).trim());
}
}
}
}
}
四、传统 I/O 与 NIO 的对比
特性 | 传统 I/O | NIO |
---|---|---|
数据处理方式 | 基于流 | 基于缓冲区 |
阻塞模式 | 阻塞 I/O | 非阻塞 I/O 和多路复用 |
线程模型 | 每连接一个线程 | 单线程处理多个连接 |
性能 | 相对较低,适合小型系统 | 高性能,适合高并发场景 |
使用复杂度 | 简单易用 | 需要理解更多底层概念 |
适用场景 | 文件操作、小规模 I/O | 高并发网络、文件大数据处理 |
五、总结
-
通过本文,你了解了 Java I/O 的基础概念、常见类库及其用法,并深入探讨了 NIO 的核心特点与实际应用。在实际开发中:
- 传统 I/O 适用于小规模、简单的 I/O 场景,使用简单直观。
- NIO 更适合处理大规模、高并发的 I/O 场景,如高性能的网络服务器和大文件的异步处理。
希望通过本文的学习,你可以更好地理解和选择适合的 I/O 模型,以提升 Java 开发中的性能和效率!