「Javaで外部APIと通信したいけど、どのライブラリを選べばいいのだろう?」
「昔ながらのHttpURLConnectionは使いにくいし、外部ライブラリを追加するのも少し面倒…」
私自身、以前はApacheのHttpClientやHttpURLConnectionを利用していましたが、Java 11から標準搭載されたjava.net.http.HttpClientの存在を知り、その使いやすさと機能に感銘を受けました。
外部ライブラリを追加する手間なく、直感的なHTTP通信が実現できるのです。
この記事を読めば、JavaでHTTP通信をしたいけれど、どのライブラリを使うべきか迷っている方が、Java標準のHttpClientの基本的な使い方から、非同期処理やAPI連携といった実践的な活用法まで理解できます。
HttpClientとは?

まず、HttpClientがどのようなもので、どのような役割を担うのかを見ていきましょう。
HttpClientの概要と役割
HttpClientは、Java 11から標準APIとして導入された、HTTP通信を行うためのクライアントです。このAPIの登場により、外部ライブラリに依存することなく、モダンなHTTP通信を実装できるようになりました。
HttpClientの主な役割は、指定したURLに対してHTTPリクエストを送信し、サーバーからのレスポンスを受信することです。HTTP/1.1はもちろん、より高速なHTTP/2プロトコルにも標準で対応している点が大きな特長です。同期処理だけでなく、ノンブロッキングI/Oを活かした非同期処理も簡単に行えます。
※ノンブロッキングI/O:データの入出力処理(I/O)が発生した際に、その処理の完了を待たずに(ブロックせずに)次の処理へ進むことができる方式のこと。これにより、プログラムは待ち時間を有効活用し、全体の処理効率を高めることができます。
HttpURLConnectionとの違い
Javaには以前からHttpURLConnectionというクラスが存在しました。しかし、HttpURLConnectionにはいくつかの課題がありました。
HttpClientは、これらの課題を解決するために設計された、後継のAPIと位置づけられます。主な違いは以下の表のとおりです。
| 機能 | HttpClient (Java 11〜) | HttpURLConnection (Java 1.1〜) |
| APIの設計 | モダンで直感的(ビルダーパターン) | 古く、設定が煩雑 |
| プロトコル | HTTP/1.1, HTTP/2 | HTTP/1.1 |
| 通信方式 | 同期、非同期の両方に対応 | 同期(ブロッキングI/O)のみ |
| 使いやすさ | シンプルで柔軟なリクエスト/レスポンス処理 | 扱いにくく、定型的なコードが増えがち |
| 依存性 | Java標準(外部ライブラリ不要) | Java標準 |
これからJavaでHTTP通信を実装するならば、特別な理由がない限りHttpClientの利用が推奨されます。理由としてはAPIが使いやすく、現代的なアプリケーションに不可欠な非同期処理やHTTP/2に標準で対応しているからです。
JavaのHttpClientの基本構文

それでは、具体的なjava http clientの使い方を見ていきましょう。基本的なGETリクエストとPOSTリクエストの例を紹介します。
HttpClientの作成方法
最初に、通信の主体となるHttpClientインスタンスを生成します。最も簡単な方法はnewHttpClient()メソッドを使うことです。
import java.net.http.HttpClient;
// HttpClientのインスタンスを生成
HttpClient client = HttpClient.newHttpClient();この方法で生成されたインスタンスは、基本的な設定が適用されます。より詳細な設定を行いたい場合は、ビルダーパターンを用いて生成します。
import java.net.http.HttpClient;
import java.time.Duration;
// ビルダーを使ってHttpClientを生成
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2) // 使用するHTTPバージョンを指定
.connectTimeout(Duration.ofSeconds(10)) // 接続タイムアウトを10秒に設定
.build();HttpClientインスタンスは不変でスレッドセーフです。そのため、アプリケーション内で一つだけ生成し、それを複数のリクエストで使い回すことがパフォーマンス上推奨されています。
GETリクエストの基本例
GETリクエストは、ウェブページやAPIから情報を取得する際に最も一般的に使われるメソッドです。HttpClientを使ったGETリクエストは、3つのステップで完了します。
HttpRequestの作成: 送信するリクエスト情報を定義します。- リクエストの送信:
HttpClientを使ってリクエストを送信します。 HttpResponseの処理: サーバーからのレスポンスを処理します。
以下に具体的なコードを示します。
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class GetExample {
public static void main(String[] args) throws Exception {
// 1. HttpClientのインスタンスを作成
HttpClient client = HttpClient.newHttpClient();
// 2. HttpRequestを作成 (GETリクエスト)
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.github.com/users/google"))
.header("Accept", "application/vnd.github.v3+json")
.GET() // GETメソッドを指定
.build();
// 3. リクエストを送信し、レスポンスを受信
// BodyHandlers.ofString()はレスポンスボディを文字列として受け取る
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// レスポンスの情報を出力
System.out.println("Status Code: " + response.statusCode());
System.out.println("Response Body: " + response.body());
}
}このコードは、GitHubのAPIにGETリクエストを送信し、ユーザー情報を取得する例です。HttpRequest.newBuilder()でリクエストの構築を開始し、uri()で行き先を指定し、GET()でメソッドを確定させています。client.send()でリクエストが実行され、HttpResponseオブジェクトが返却されます。
POSTリクエストの送信例
POSTリクエストは、サーバーにデータを送信して新しいリソースを作成する際などに使用します。例えば、フォームのデータを送信したり、JSON形式のデータをAPIに送信したりする場合です。
POSTリクエストでは、どのデータを送信するかをBodyPublishersを使って指定する必要があります。
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class PostExample {
public static void main(String[] args) throws Exception {
// 送信するJSONデータ
String json = "{\"name\":\"Taro Yamada\", \"job\":\"Developer\"}";
// 1. HttpClientインスタンスを作成
HttpClient client = HttpClient.newHttpClient();
// 2. HttpRequestを作成 (POSTリクエスト)
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/post"))
.header("Content-Type", "application/json") // 送信するデータの種類を指定
.POST(HttpRequest.BodyPublishers.ofString(json)) // POSTメソッドとリクエストボディを指定
.build();
// 3. リクエストを送信し、レスポンスを受信
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// レスポンスの情報を出力
System.out.println("Status Code: " + response.statusCode());
System.out.println("Response Body: " + response.body());
}
}GETリクエストとの主な違いは2点です。
HttpClientのオプション設定

HttpClientは、通信に関するさまざまなオプションを柔軟に設定できます。ここでは代表的な設定を3つ紹介します。
タイムアウトの設定方法
ネットワークの問題などでサーバーからの応答が長時間返ってこない場合、プログラムが停止してしまうのを防ぐためにタイムアウト設定は重要です。HttpClientでは、接続タイムアウトを簡単に設定できます。
import java.net.http.HttpClient;
import java.time.Duration;
// 接続タイムアウトを10秒に設定
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();connectTimeout()メソッドにjava.time.Durationオブジェクトを渡すことで、指定した時間内に接続が確立されなかった場合にHttpConnectTimeoutExceptionがスローされます。
ヘッダーの追加方法
APIキーの指定やコンテンツタイプの指定など、リクエストヘッダーの追加は頻繁に行われます。HttpRequest.Builderのheader()メソッドを使えば簡単です。
import java.net.URI;
import java.net.http.HttpRequest;
// 複数のヘッダーを追加する例
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com"))
.header("X-API-KEY", "your_api_key_here") // カスタムヘッダー
.header("Accept", "application/json") // Acceptヘッダー
.build();同じキーでheader()メソッドを複数回呼び出すと、最後の値で上書きされます。複数の値を設定したい場合は、headers()メソッドを使用します。
リダイレクトの制御
リクエストしたURLが別のURLにリダイレクトされることがあります。HttpClientのデフォルトの動作(NORMAL)では、安全なリダイレクト(例: HTTPからHTTPS)は自動的に追従します。
この動作はfollowRedirects()メソッドで変更可能です。
import java.net.http.HttpClient;
// リダイレクトポリシーを設定
HttpClient client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.ALWAYS) // 常にリダイレクトに追従する
.build();
// 他のポリシー
// HttpClient.Redirect.NEVER - リダイレクトに追従しない
// HttpClient.Redirect.NORMAL - デフォルトの動作(同一プロトコルなど安全な場合のみ)リダイレクトを一切許可しない場合はNEVER、HTTPからHTTPSへの変更などに関わらず常に追従させたい場合はALWAYSを指定します。
HttpClientを使った非同期処理

HttpClientの強力な機能の一つが非同期処理です。これにより、時間のかかる通信処理中にアプリケーションの他の処理をブロックすることなく、効率的なプログラムを記述できます。
sendAsync()で非同期通信を行う
非同期でリクエストを送信するには、send()の代わりにsendAsync()メソッドを使用します。send()がHttpResponseを直接返すのに対し、sendAsync()はƒCompletableFuture<HttpResponse>を返します。
CompletableFutureは、まだ完了していない非同期処理の結果を保持するコンテナのようなものです。
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;
public class AsyncExample {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.github.com/"))
.build();
// 非同期でリクエストを送信
CompletableFuture<HttpResponse<String>> future =
client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
System.out.println("リクエストを送信しました。レスポンスを待っています...");
// レスポンスが返ってきた後の処理を定義
future.thenAccept(response -> {
System.out.println("Status Code: " + response.statusCode());
System.out.println("Response Body: " + response.body().substring(0, 80) + "...");
});
// 非同期処理の完了を待つ (サンプルコードのため)
future.join();
}
}sendAsync()を呼び出すと、リクエストはバックグラウンドスレッドで実行され、プログラムは即座に次の行に進みます。これにより、ユーザーインターフェースが固まったり、他の処理が待たされたりするのを防ぐことができます。
CompletableFutureとの組み合わせ方
CompletableFutureの真価は、非同期処理の結果に対して連続的な処理を定義できる点にあります。
thenApply(): 結果を受け取り、変換して別のCompletableFutureを返す(例: レスポンスボディの文字列をJSONオブジェクトに変換する)。thenAccept(): 結果を受け取って消費する(例: 画面に表示する)。返り値はない。thenRun(): 結果に関係なく、処理完了後に特定のRunnableを実行する。exceptionally(): 例外が発生した場合の処理を定義する。
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;
public class CompletableFutureChain {
public static void main(String[] args) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.github.com/users/google"))
.build();
CompletableFuture<Void> future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body) // HttpResponseからボディ(文字列)を抽出
.thenApply(body -> { // ボディを加工 (ここでは大文字に変換)
System.out.println("ボディを大文字に変換します。");
return body.toUpperCase();
})
.thenAccept(processedBody -> { // 加工後のボディをコンソールに出力
System.out.println("処理結果:");
System.out.println(processedBody.substring(0, 100) + "...");
})
.exceptionally(e -> { // エラーハンドリング
System.err.println("エラーが発生しました: " + e.getMessage());
return null;
});
System.out.println("非同期処理のチェーンを定義しました。");
// 処理の完了を待機
future.join();
}
}このようにメソッドチェーンを使うことで、非同期処理の各ステップを宣言的かつ直感的に記述することが可能です。
HttpClientの活用例

HttpClientはさまざまな場面で活用できます。ここでは、特に一般的な2つの活用例を見ていきましょう。
API通信での利用(例:天気API)
多くのWebサービスが、外部からデータを取得するためのAPIを公開しています。ここでは、無料で利用できる気象API「Open-Meteo」を使って、東京の現在の天気を取得する例を紹介します。
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class WeatherApiExample {
public static void main(String[] args) throws Exception {
// 東京の緯度経度を指定してAPIのURLを構築
String apiUrl = "https://api.open-meteo.com/v1/forecast" +
"?latitude=35.6895&longitude=139.6917¤t_weather=true";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(apiUrl))
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
System.out.println("東京の現在の天気情報:");
System.out.println(response.body());
} else {
System.out.println("天気情報の取得に失敗しました。Status Code: " + response.statusCode());
}
}
}このように、APIの仕様に従ってURLを組み立て、GETリクエストを送信するだけで、簡単に外部サービスのデータを利用できます。
JSONレスポンスのパース(Jackson/Gson連携)
APIから返されるデータの多くはJSON形式です。受け取ったJSON文字列をそのまま扱うのは不便なため、通常はJavaのオブジェクトに変換(パースまたはデシリアライズ)します。
この変換には、JacksonやGsonといったライブラリがよく利用されます。ここではJacksonを使った例を示します。(build.gradleやpom.xmlにJacksonライブラリの依存関係を追加する必要があります)
まず、JSONの構造に対応するJavaクラス(POJO)を定義します。
// CurrentWeather.java
// APIレスポンスの"current_weather"部分に対応するクラス
public class CurrentWeather {
public double temperature;
public double windspeed;
public int weathercode;
}次に、HttpClientで受け取ったレスポンスボディをJacksonでパースします。
import com.fasterxml.jackson.databind.ObjectMapper;
// (他のimport文は省略)
public class JsonParseExample {
public static void main(String[] args) throws Exception {
// (APIリクエスト部分は前の例と同じ)
String apiUrl = "..."; // 天気APIのURL
// ... request, responseの取得 ...
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
String responseBody = response.body();
// JacksonのObjectMapperを生成
ObjectMapper mapper = new ObjectMapper();
// JSON文字列をパースしてJavaオブジェクトに変換
// ここではレスポンス全体から"current_weather"ノードを抽出してパース
CurrentWeather weather = mapper.readTree(responseBody)
.get("current_weather")
.traverse(mapper)
.readValueAs(CurrentWeather.class);
System.out.println("現在の気温: " + weather.temperature + "℃");
System.out.println("現在の風速: " + weather.windspeed + " km/h");
System.out.println("天気コード: " + weather.weathercode);
}
}
}このようにライブラリと組み合わせることで、HttpClientで取得したデータを効率的に扱うことが可能です。
HttpClient利用時の注意点

HttpClientは非常に便利ですが、利用する上で知っておくべき点がいくつかあります。
バージョンと互換性(Java 11以上)
java.net.http.HttpClientは、Java 11で正式に導入されたAPIです。そのため、Java 8やJava 10といった古いバージョンでは利用できません。プロジェクトで使用しているJavaのバージョンを必ず確認してください。
もし古いバージョンで同様の機能が必要な場合は、Apache HttpClientやOkHttpといった外部ライブラリを検討する必要があります。
スレッド安全性と再利用のポイント
HttpClientインスタンスは、不変(immutable)であり、スレッドセーフに設計されています。これは、複数のスレッドから同時に同じインスタンスを使っても問題が発生しないことを意味します。
リクエストごとにHttpClient.newHttpClient()を呼び出して新しいインスタンスを生成するのは非効率です。HttpClientは内部で接続プールなどのリソースを管理しているため、インスタンスを生成するコストは決して小さくありません。
アプリケーションの起動時にHttpClientインスタンスを一つだけ生成し、それをアプリケーション全体で共有して使い回すのがベストプラクティスです。
// アプリケーションで共有するHttpClientインスタンス
public class AppHttpClient {
public static final HttpClient INSTANCE = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(10))
.build();
private AppHttpClient() {}
}
// 利用する側のコード
public class SomeService {
public void doSomething() throws Exception {
HttpRequest request = ...;
// 共有インスタンスを使ってリクエストを送信
HttpResponse<String> response = AppHttpClient.INSTANCE.send(request, HttpResponse.BodyHandlers.ofString());
}
}まとめ
この記事では、Java 11から標準となったHttpClientについて、基本的な使い方から応用までを解説しました。
HttpClientの利点と活用場面
HttpClientの利点は明確です。
- 標準API: 外部ライブラリを追加する手間がありません。
- モダンな機能: HTTP/2や非同期処理に標準で対応しています。
- シンプルなAPI: ビルダーパターンにより、直感的で読みやすいコードを記述できます。
- 柔軟性: タイムアウトやリダイレクトなど、詳細な設定が可能です。
Web APIとの通信、マイクロサービス間の連携、Webスクレイピングなど、現代のJavaアプリケーション開発におけるHTTP通信の場面では、HttpClientが第一の選択肢となるでしょう。
HttpClientを使いこなせば、Javaでの開発がより一層スムーズで楽しいものになります。ぜひ、あなたのプロジェクトで活用してみてください。
