在Java中,I/O操作(输入/输出操作)是非常基础且重要的一部分。Java提供了两种主要的I/O API:传统的IO(java.io包)和新IO(NIO,java.nio包)。这两者在设计理念、性能和使用场景上有显著的不同。本文将深入探讨Java IO与NIO的对比,并提供一些高级用法的示例代码。
1. Java IO与NIO的基本概念
1.1 Java IO
Java IO API是基于流(Stream)的模型。它以同步和阻塞的方式进行数据读写操作。每次I/O操作都会阻塞调用线程,直到操作完成。这种方式简单直观,但在高并发场景下性能表现不佳。
1.2 Java NIO
Java NIO(Non-blocking IO)引入了一种基于缓冲区(Buffer)和通道(Channel)的模型。NIO支持非阻塞模式,可以在进行I/O操作时不中断线程,从而提高了性能和可扩展性。NIO还引入了选择器(Selector)用于管理多个通道的非阻塞I/O操作。
2. Java IO与NIO的对比
| 特性 | Java IO | Java NIO |
|---|---|---|
| 模型 | 基于流 (Stream) | 基于缓冲区 (Buffer) 和通道 (Channel) |
| 阻塞/非阻塞 | 阻塞I/O | 非阻塞I/O |
| 数据处理 | 字节/字符流 | 缓冲区中读取和写入数据 |
| 性能 | 简单,但在高并发下性能较差 | 在高并发和大数据处理场景下性能更佳 |
| API复杂度 | 较为简单 | 复杂度较高 |
3. Java IO的高级用法
3.1 文件读写操作
import java.io.*;
public class FileIODemo {
public static void main(String[] args) {
String filePath = "example.txt";
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
writer.write("Hello, Java IO!");
} catch (IOException e) {
e.printStackTrace();
}
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.2 网络通信(Socket编程)
import java.io.*;
import java.net.*;
public class SocketIODemo {
public static void main(String[] args) {
// Server
new Thread(() -> {
try (ServerSocket serverSocket = new ServerSocket(8080)) {
Socket socket = serverSocket.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
writer.println("Hello, Client!");
System.out.println("Client says: " + reader.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}).start();
// Client
new Thread(() -> {
try (Socket socket = new Socket("localhost", 8080)) {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
System.out.println("Server says: " + reader.readLine());
writer.println("Hello, Server!");
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
4. Java NIO的高级用法
4.1 文件读写操作
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class FileNIODemo {
public static void main(String[] args) {
String filePath = "example_nio.txt";
Path path = Paths.get(filePath);
String content = "Hello, Java NIO!";
// Write to file using NIO
try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put(content.getBytes());
buffer.flip();
fileChannel.write(buffer);
} catch (IOException e) {
e.printStackTrace();
}
// Read from file using NIO
try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
fileChannel.read(buffer);
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
System.out.println(new String(bytes));
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.2 网络通信(Socket编程)
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class SocketNIODemo {
public static void main(String[] args) {
new Thread(SocketNIODemo::startServer).start();
new Thread(SocketNIODemo::startClient).start();
}
public static void startServer() {
try (Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open()) {
serverChannel.bind(new InetSocketAddress(8081));
serverChannel.configureBlocking(false);
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()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
ByteBuffer buffer = ByteBuffer.wrap("Hello, Client!".getBytes());
client.write(buffer);
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(256);
client.read(buffer);
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
System.out.println("Client says: " + new String(bytes));
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void startClient() {
try (SocketChannel clientChannel = SocketChannel.open(new InetSocketAddress("localhost", 8081))) {
clientChannel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(256);
while (clientChannel.read(buffer) <= 0) {
// Wait for server response
}
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
System.out.println("Server says: " + new String(bytes));
buffer.clear();
buffer.put("Hello, Server!".getBytes());
buffer.flip();
while (buffer.hasRemaining()) {
clientChannel.write(buffer);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
5. 结论
Java IO和NIO各有优缺点,在具体应用中应根据需求选择合适的API。传统的IO适合简单、低并发的场景,而NIO在高并发、大数据处理的场景中表现更佳。希望本文通过详细的对比和代码示例,能帮助读者更好地理解Java IO与NIO的区别与应用。









