httpclient.newbuilder() 必须调用 build() 才能创建实例,否则调用 send() 或 sendasync() 会抛 nullpointerexception;sendasync() 应配合 thenapply() 等非阻塞处理,避免 get() 阻塞;bodyhandlers.ofstring() 默认 utf-8,需手动解析响应头 charset;http/2 需服务端支持 tls 和 alpn,单请求无多路复用优势。

HttpClient.newBuilder() 必须调用 build() 才能创建实例
不调用 build() 就直接用 newBuilder() 返回的对象发请求,会抛 NullPointerException——因为 builder 本身不是客户端,只是配置载体。常见写法错误是链式调用后忘了收尾。
正确姿势:
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build(); // 这行不能少
- 所有配置(超时、代理、SSL 上下文等)都必须在
build()前完成,之后不可变 - 同一个
HttpClient实例可复用,内部自带连接池和线程调度,别每次请求都新建 - 如果漏掉
build(),IDE 不报错,但运行时send()或sendAsync()会立即 NPE
sendAsync() 返回 CompletableFuture,别直接 get() 阻塞主线程
用 sendAsync() 却立刻调 get(),等于白用异步——它会卡住当前线程,失去非阻塞意义。典型场景是 WebFlux 或 Vert.x 环境里混用阻塞调用,拖垮吞吐。
正确做法是用 thenApply()、exceptionally() 做后续处理:
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.exceptionally(t -> {
System.err.println("请求失败: " + t.getMessage());
return null;
});
-
sendAsync()底层由ForkJoinPool.commonPool()调度,不建议在高并发 I/O 场景依赖它;生产环境应显式传入自定义Executor - 如果必须同步等待结果,请确认这是调试或 CLI 工具场景,而非服务端逻辑
- 注意
CompletableFuture的异常不会自动抛出,不加exceptionally()或handle()会静默失败
BodyHandlers.ofString() 默认用 UTF-8,但响应头可能声明其他编码
服务器返回 Content-Type: text/html; charset=GBK,但代码里只写 BodyHandlers.ofString(),结果中文全乱码——因为 JDK 默认忽略响应头里的 charset,硬编码 UTF-8。
解决方法只有手动解析响应头再构造处理器:
HttpResponse.BodyHandler<string> handler = responseInfo -> {
String contentType = responseInfo.headers().firstValue("Content-Type").orElse("");
String charset = "UTF-8";
if (contentType.contains("charset=")) {
charset = contentType.split("charset=", 2)[1].split(";")[0].trim();
}
return HttpResponse.BodyHandlers.ofString(Charset.forName(charset));
};</string>
-
BodyHandlers.ofString()没有重载支持动态 charset,这是 JDK 的设计限制,不是用法问题 - 二进制内容(如图片、PDF)别用
ofString(),改用ofByteArray()或流式处理ofInputStream() - 如果响应体很大,避免
ofString()全部加载进内存;考虑ofPublisher()配合Flow.Subscriber
HTTP/2 支持需要服务器配合,本地测试别默认以为能跑起来
Java 11 HttpClient 声称支持 HTTP/2,但本地启一个 Spring Boot 默认 Tomcat,发请求仍是 HTTP/1.1——因为 Tomcat 9.0.1+ 才默认启用 HTTP/2,且需配置 TLS(HTTP/2 over TLS 是主流,明文 h2c 很少被支持)。
验证是否真走 HTTP/2:
HttpResponse<string> resp = client.send(request, BodyHandlers.ofString()); System.out.println(resp.version()); // 输出 HTTP_2 或 HTTP_1_1</string>
- OpenJDK 11 的 HTTP/2 实现依赖
java.net.http.HttpClient内置的 ALPN 处理器,不兼容旧版 BoringSSL 或某些自签名证书 - 用
curl -v https://example.com对比确认服务端是否真开了 HTTP/2,别只信客户端日志 - HTTP/2 的多路复用优势,在单请求场景下毫无体现;只有并发多个请求到同一 host 时才明显降低延迟
HTTP/2 和字符集这两块最容易在上线后才发现问题:一个是协议协商无声降级,一个是响应头和实际编码不一致。它们都不报错,只默默给你错的数据或慢的连接。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











