跳到正文
Elaine Blog
返回

文件、网络与 NIO 基础

Java 基础

I/O(Input/Output,输入输出)把程序连接到文件、网络和其他进程。它最容易出错的地方不是 API 名称,而是字符编码、 资源关闭、超时和“数据太大不能一次放进内存”。

Table of contents

Open Table of contents

先做一个流式文件复制器

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.*;

public class 文件复制器 {
    static void 复制(Path source, Path target) throws IOException {
        try (var input = FileChannel.open(source, StandardOpenOption.READ);
             var output = FileChannel.open(target, StandardOpenOption.CREATE,
                 StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) {
            var buffer = ByteBuffer.allocateDirect(16 * 1024);
            while (input.read(buffer) != -1) {
                buffer.flip();
                while (buffer.hasRemaining()) output.write(buffer);
                buffer.clear();
            }
        }
    }
}

**通道(Channel)**是数据读写连接,**缓冲区(Buffer)**是数据暂存区。读取后调用 flip(),把缓冲区从写入模式切换 为读取模式;写完后调用 clear(),准备下一轮。write() 不保证一次写完,所以要检查 hasRemaining()

普通业务代码优先使用 Files.copyFiles.newBufferedReader 等高层 API。只有需要理解 I/O 边界、定制协议或测得高层 API 不满足需求时,才直接操作通道和缓冲区。

字节不等于字符

文件和网络传输的是字节;文本需要按**字符编码(Charset)**在字符与字节之间转换。显式使用 UTF-8:

var text = java.nio.file.Files.readString(path, java.nio.charset.StandardCharsets.UTF_8);

不要依赖操作系统默认编码。Java 18 起标准 API 默认字符集统一为 UTF-8,但显式编码仍能让协议和文件契约一目了然, 也便于维护旧环境。

给网络请求设置边界

var client = java.net.http.HttpClient.newBuilder()
    .connectTimeout(java.time.Duration.ofSeconds(2)).build();
var request = java.net.http.HttpRequest.newBuilder(uri)
    .timeout(java.time.Duration.ofSeconds(3)).GET().build();
var response = client.send(request,
    java.net.http.HttpResponse.BodyHandlers.ofString(java.nio.charset.StandardCharsets.UTF_8));

连接超时限制建立连接的等待,请求超时限制整个请求。两者都要配置。响应体大小不受这段代码限制;面对不可信服务时, 应使用流式处理并限制字节数。

阻塞与非阻塞

阻塞 I/O会让当前线程等待操作完成;非阻塞 I/O允许线程在暂时无数据时处理其他任务。非阻塞不等于自动更快, 它用状态机复杂度换取更少的等待线程。普通文件工具和低并发内部调用先用阻塞 API;连接数量很大且等待占主导时,再评估 异步或事件驱动模型。

下一步

阅读注解、反射与动态代理,理解框架如何在运行时装配行为。


分享这篇文章:

上一篇
Lambda、Stream 与副作用边界
下一篇
注解、反射与动态代理