本ページはプロモーションが含まれています

Java入門

Java HttpClientとは?GET/POSTと非同期の使い方

CATEGORY
REV.
DRAWN BY
TOM
SHEETS
8枚
HttpClient(GET/POSTと非同期)

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

𝕏

「Javaで外部APIと通信したいけど、HttpClientとHttpURLConnectionのどちらを使えばいいのか」。一度は迷うところです。

HTTPクライアントとは、WebサーバーにHTTPリクエストを送り、返ってきたレスポンスを受け取るプログラム(部品)のことです。ブラウザもHTTPクライアントの一種で、Javaから外部APIを呼ぶときは、この役割を担うクラスをコード側に用意します。

HttpClient(java.net.http.HttpClient)は、Java 11から標準搭載されたHTTP通信用のクライアントです。外部ライブラリなしでGET・POST・非同期通信がこなせ、HTTP/2にも対応しています。

Java 11以降なら、まずこれを選べば問題ありません。

以下では、GETとPOSTの基本コード、タイムアウトやヘッダーの設定、sendAsync()による非同期処理、天気APIとの連携例を順に扱います。

Java 21で追加されたclose()の使い方や、HttpURLConnectionから移行するときのつまずきどころも押さえます。

この記事でわかること

  • HttpClientとHttpURLConnectionの違い
  • GET・POSTリクエストの基本コード
  • タイムアウト・ヘッダー・リダイレクト・プロキシ・Cookieの設定
  • sendAsync()とCompletableFutureによる非同期処理
  • 天気API連携・JSONパースの活用例と、Java 21のclose()など利用時の注意点

HttpClientとは?

HttpClientの概要と役割

HttpClientは、Java 11から標準APIとして導入された、HTTP通信を行うためのクライアントです。

HttpClientの登場で、外部ライブラリなしでモダンなHTTP通信を書けるようになりました。

HttpClientの主な役割は、指定したURLに対してHTTPリクエストを送信し、サーバーからのレスポンスを受信することです。

HTTP/1.1はもちろん、より高速なHTTP/2プロトコルにも標準で対応している点が大きな特長です。

同期処理だけでなく、ノンブロッキングI/Oを活かした非同期処理も簡単に行えます。

※ノンブロッキングI/O:データの入出力処理(I/O)が発生した際に、その処理の完了を待たずに(ブロックせずに)次の処理へ進める方式のこと。

通信の待ち時間に別の処理を進められるため、プログラム全体の処理効率が上がります。

HttpURLConnectionとの違い

Javaには以前からHttpURLConnectionというクラスがありました。ただ、設定が煩雑で、HTTP/2や非同期通信に対応していないという課題を抱えていました。

HttpClientはその後継として設計されたAPIです。主な違いは次の表のとおりです。

機能HttpClient (Java 11〜)HttpURLConnection (Java 1.1〜)
APIの設計モダンで直感的(ビルダーパターン)古く、設定が煩雑
プロトコルHTTP/1.1, HTTP/2HTTP/1.1
通信方式同期、非同期の両方に対応同期(ブロッキングI/O)のみ
使いやすさシンプルで柔軟なリクエスト/レスポンス処理扱いにくく、定型的なコードが増えがち
依存性Java標準(外部ライブラリ不要)Java標準

これからJavaでHTTP通信を書くなら、特別な理由がない限りHttpClientを使ってください。APIが使いやすく、非同期処理やHTTP/2に標準で対応しているためです。

HttpClientの基本的な使い方(GET/POST)

HttpClientでの通信は「HttpClientを作る→HttpRequestを組み立てる→送信してHttpResponseを受け取る」の3段階です。流れを図にすると次のとおりです。

HttpClientで通信する3段階と、同期・非同期の分かれ道
1
CLIENT / 作るHttpClientを作るnewHttpClient() / newBuilder()
2
REQUEST / 組み立てるHttpRequestを組み立てるuri() / header() / GET() / POST()
SEND / 送信送信方法は?
同期send()レスポンスが届くまで待ち、HttpResponseを受け取る。statusCode() / body() で中身を取り出す
非同期sendAsync()CompletableFutureをすぐ返す。届いたHttpResponseは thenApply() / thenAccept() で処理する

レスポンスボディの受け取り方(文字列・ファイルなど)は、送信時に渡すBodyHandlersで決めます。以降では、同期のsend()を使ったGETとPOSTから見ていきます。

HttpClientの作成方法

最初に、通信の主体となるHttpClientインスタンスを生成します。最も簡単なのはnewHttpClient()を呼ぶ方法です。

import java.net.http.HttpClient;

// HttpClientのインスタンスを生成
HttpClient client = HttpClient.newHttpClient();

newHttpClient()で作ったインスタンスには、既定の設定(HTTP/2優先・リダイレクト非追従など)が適用されます。細かく設定したい場合は、ビルダーパターンで生成します。

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インスタンスは不変でスレッドセーフです。

アプリケーション内で1つだけ生成し、複数のリクエストで使い回すと、接続を再利用できて効率的です。

GETリクエストの基本例

GETリクエストは、ウェブページやAPIから情報を取得する際に最も一般的に使われるメソッドです。

HttpClientを使ったGETリクエストは、3つのステップで完了します。

  1. HttpRequestの作成: 送信するリクエスト情報を定義します。
  2. リクエストの送信: HttpClientを使ってリクエストを送信します。
  3. HttpResponseの処理: サーバーからのレスポンスを処理します。

GitHubの公開APIからユーザー情報を取得する例です。

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());
    }
}

HttpRequest.newBuilder()で組み立てを始め、uri()で宛先、GET()でメソッドを指定します。

client.send()を呼ぶとリクエストが送られ、結果がHttpResponseとして返ります。

実務上の注意点として、BodyHandlers.ofString()はレスポンスボディ全体を一度にメモリへ読み込みます。

数MB程度のJSON APIなら問題ありません。

ただし画像やCSVなど大きめのファイルをダウンロードする用途では、メモリを圧迫してOutOfMemoryErrorの原因になりえます。

ファイルとして直接保存したい場合はBodyHandlers.ofFile(Path.of("output.bin"))を使います。

レスポンスをストリームのままディスクへ書き出せるため、メモリ使用量を一定に抑えられます。

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点です。

主な違い

  • POST()メソッドを使用し、引数にHttpRequest.BodyPublishers.ofString(json)を指定して送信するデータ(リクエストボディ)を渡しています。
  • header()メソッドでContent-Typeをapplication/jsonに指定し、送信するデータがJSON形式であることをサーバーに伝えています。

APIに送るJSON文字列を手で組み立てるのがつらくなったら、オブジェクトからJSONへの変換をJavaのJSON変換|Jackson・Gson使い方比較で確認してください。

HttpClientのオプション設定

HttpClientは、通信に関するさまざまなオプションを柔軟に設定できます。よく使う5つの設定(タイムアウト・ヘッダー・リダイレクト・プロキシ・Cookie)を紹介します。

タイムアウトの設定方法

サーバーの応答が返ってこないと、プログラムはいつまでも待ち続けます。タイムアウトを設定して、待ち時間に上限を設けましょう。

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がスローされます。

なお、connectTimeout()はHttpClient生成時に接続確立までの上限を設定するものです。

個々のリクエストごとにレスポンス待ちの上限を設定したい場合は、HttpRequest.Builder側のtimeout()メソッドを使います。

両者は役割が異なるため、用途に応じて使い分けます。

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://example.com"))
        .timeout(Duration.ofSeconds(5)) // このリクエスト単体のレスポンス待ち上限
        .GET()
        .build();

ヘッダーの追加方法

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()を複数回呼ぶと、値は上書きされずに追加されます(1つのヘッダーに複数の値が付きます)。値を置き換えたい場合はsetHeader()を使います。

複数のヘッダーをまとめて指定するなら、headers("名前1", "値1", "名前2", "値2")のように名前と値を交互に渡すheaders()が便利です。

リダイレクトの制御

リクエストしたURLが、別のURLへリダイレクトされることがあります。

ポリシーを指定しない場合のデフォルトはNEVERで、リダイレクトには追従せず3xxのレスポンスがそのまま返ります(newHttpClient()で作った場合も同じ)。

追従させたい場合は、followRedirects()でポリシーを指定します。

import java.net.http.HttpClient;

// リダイレクトポリシーを設定
HttpClient client = HttpClient.newBuilder()
        .followRedirects(HttpClient.Redirect.NORMAL) // HTTPS→HTTPへのダウングレード以外は追従する
        .build();

// 他のポリシー
// HttpClient.Redirect.NEVER  - リダイレクトに追従しない(ビルダーで未指定時のデフォルト)
// HttpClient.Redirect.ALWAYS - 常にリダイレクトに追従する

NORMALはHTTPSからHTTPへのダウングレードを除いて追従し、ALWAYSは常に追従します。通常はNORMALを選べば安全です。

プロキシ設定

社内ネットワークなど、プロキシ経由でしか外部通信ができない環境では、HttpClientにプロキシを明示的に設定する必要があります。

ProxySelectorを使うことで、リクエストごとに個別指定しなくても、クライアント全体にプロキシ設定を適用できます。

import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.http.HttpClient;

// プロキシサーバーを明示的に指定
HttpClient client = HttpClient.newBuilder()
        .proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 8080)))
        .build();

プロキシ側で認証が必要な場合は、authenticator()メソッドと組み合わせてAuthenticatorを設定します。

プロキシなしの環境をデフォルトとし、環境変数などで切り替える構成にしておくと、開発環境と本番環境の差を吸収しやすくなります。

Cookieの管理

HttpClientは、デフォルトではCookieを保持しません。

ログイン状態を維持したまま複数リクエストを送るような場合は、CookieManagerを明示的にセットしてください。

import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.http.HttpClient;

// Cookieを自動保持するHttpClientを生成
CookieManager cookieManager = new CookieManager();
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);

HttpClient client = HttpClient.newBuilder()
        .cookieHandler(cookieManager)
        .build();

CookieManagerをセットしておくと、レスポンスのSet-Cookieヘッダーを自動で保存し、同じクライアントからの以降のリクエストに付けて送ります。

セッション管理を伴うAPIやスクレイピング用途では、実質必須の設定です。

HttpClientを使った非同期処理

HttpClientの強力な機能の一つが非同期処理です。

非同期処理を使うと、時間のかかる通信の最中も、他の処理を止めずに進められます。

sendAsync()で非同期通信を行う

非同期でリクエストを送信するには、send()の代わりにsendAsync()メソッドを使用します。

send()がHttpResponseを直接返すのに対し、sendAsync()はCompletableFuture<HttpResponse<T>>を返します。

CompletableFutureは、まだ完了していない非同期処理の結果を保持するコンテナのようなものです。

同期処理と非同期処理でプログラムの流れがどう変わるかを図にすると、次のようになります。

send()とsendAsync()でメインスレッドの動きがどう違うか
1
SYNC / send()リクエスト送信メインスレッド → サーバー
2
レスポンスが届くまで待機待っている間は次の処理へ進めない
3
受け取ってから次の処理へ
1
ASYNC / sendAsync()リクエスト送信メインスレッド → サーバー
2
CompletableFutureをすぐ返し、次の処理へ通信の待ち時間に、別の処理を進める
3
thenAccept等で結果を処理
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&current_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"部分に対応するクラス
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

// APIレスポンスには time・interval・winddirection・is_day なども含まれるため、未定義の項目は無視する
@JsonIgnoreProperties(ignoreUnknown = true)
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);
        }
    }
}

JSONライブラリと組み合わせれば、HttpClientで取得したデータをJavaのオブジェクトとして扱えます。

あわせて読む

HttpClient利用時の注意点

HttpClientは便利ですが、利用する上で知っておくべき点がいくつかあります。

HttpClientとOkHttpはどちらを選ぶべきか

Java標準のHttpClientには外部ライブラリ不要という利点があります。

ただし実務ではOkHttpやApache HttpClient 5系が使われているプロジェクトも依然として多くあります。判断基準はシンプルです。

新規プロジェクトで外部依存を増やしたくない場合、あるいは学習コストを抑えたい場合はHttpClientで十分です。

一方、インターセプター(リクエスト/レスポンスの共通処理を差し込む仕組み)を多用する既存プロジェクトもあります。

Retrofitのような上位ライブラリと組み合わせて使う場合は、エコシステムが成熟しているOkHttpのほうが結果的に書くコード量が少なくなることが多いです。

「標準APIだから常に正解」というわけではなく、既存のチーム標準やライブラリ構成との整合性も含めて選ぶのが実務的な判断です。

バージョンと互換性(Java 11以上)

java.net.http.HttpClientは、Java 11で正式に導入されたAPIです。

そのため、Java 8やJava 10といった古いバージョンでは利用できません。プロジェクトで使用しているJavaのバージョンを必ず確認してください。

Java 21では、HttpClientがAutoCloseableを実装しました。close()などで、クライアントが持つスレッドや接続を明示的に片付けられます。

一時的に使うクライアントなら、try-with-resourcesで書くのが簡単です(Java 21以降でコンパイルできます)。

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class CloseExample {
    public static void main(String[] args) throws Exception {
        // Java 21以降: try-with-resourcesでHttpClientを自動クローズできる
        try (HttpClient client = HttpClient.newHttpClient()) {
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create("https://httpbin.org/get"))
                    .build();

            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println("Status Code: " + response.statusCode());
        } // ここでclose()が呼ばれ、送信済みリクエストの完了を待ってから終了する
    }
}

Java 21で追加された終了用メソッドは次の5つです。

メソッド動き
close()新しいリクエストの受け付けを止め、送信済みのリクエストが終わるまで待ってから終了する
shutdown()新しいリクエストの受け付けを止める。送信済みのリクエストは最後まで処理されるが、このメソッド自体はその完了を待たずにすぐ戻る
awaitTermination(Duration)shutdown()の後に呼び、終了するか指定時間が過ぎるまで待つ
shutdownNow()実行中の処理に割り込みをかけて、すぐに止めようとする
isTerminated()終了処理が完了していればtrueを返す

2026年時点の最新LTSであるJava 25でも、本記事のAPIはそのまま利用できます。

もし古いバージョンで同様の機能が必要な場合は、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 21以降でも、アプリケーション全体で使い回す共有インスタンスは、リクエストのたびにclose()しないでください。

close()は、アプリケーション終了時などクライアントを破棄するタイミングで1回呼べば十分です。

例外処理(IOException/InterruptedException)

ここまでのサンプルコードはmainメソッドにthrows Exceptionを付けて、例外を丸ごと外側へ投げていました。

学習用なら問題ありませんが、実際のアプリケーションでは例外ごとに処理を分けてください。client.send()が投げる主な検査例外は次の2種類です。

  • IOException: 接続失敗・タイムアウト・DNS解決エラーなど、通信経路上の問題で発生する。
  • InterruptedException: リクエスト送信中にスレッドが割り込みを受けた場合に発生する。
try {
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    System.out.println(response.body());
} catch (IOException e) {
    // 通信エラー:リトライやフォールバック処理を検討
    System.err.println("通信に失敗しました: " + e.getMessage());
} catch (InterruptedException e) {
    // 割り込み:スレッドの割り込み状態を復元してから再スロー
    Thread.currentThread().interrupt();
    throw new RuntimeException("リクエストが中断されました", e);
}

InterruptedExceptionをキャッチしたときに何もせず握りつぶすと、スレッドの割り込みシグナルが失われます。

Thread.currentThread().interrupt()で割り込み状態を復元しておくのが定石です。

あわせて読む

よくある質問(FAQ)

Q1. HttpClientとHttpURLConnectionはどちらを使うべきですか?

A1. Java 11以降のプロジェクトであれば、特別な理由がない限りHttpClientを選んでください。ビルダーパターンで直感的に書け、HTTP/2や非同期処理にも標準対応しています。HttpURLConnectionは後方互換のために残っているレガシーAPIという位置づけです。

Q2. Java 8でもHttpClientは使えますか?

A2. 標準のjava.net.http.HttpClientはJava 11以降でのみ利用できます。Java 8環境で同様の機能が必要な場合は、Apache HttpClientやOkHttpといった外部ライブラリを検討してください。

Q3. GETとPOSTの実装で何が変わりますか?

A3. 基本構造は同じです。POSTではHttpRequest.Builderにheader("Content-Type", ...)でデータ種別を指定し、POST(HttpRequest.BodyPublishers.ofString(json))で送信するボディを渡す点が異なります。GETはGET()のみでボディ指定は不要です。

Q4. 非同期処理(sendAsync)はどんな場面で使うべきですか?

A4. 複数のAPIを並行して呼び出したい場合や、UIスレッドを止めたくないアプリケーションで有効です。単純なバッチ処理やCLIツールなど、順番に結果が必要な場合はsend()で同期的に書いたほうがコードがシンプルになります。

Q5. HttpClientインスタンスは毎回生成すべきですか?

A5. いいえ。HttpClientは不変でスレッドセーフなため、アプリケーション起動時に一つだけ生成し、使い回すのがベストプラクティスです。リクエストのたびに生成すると接続プールの再構築コストがかかります。

Q6. タイムアウトはどこで設定すればいいですか?

A6. 接続確立までの上限はHttpClient.Builder.connectTimeout()で設定します。個々のリクエストのレスポンス待ち上限はHttpRequest.Builder.timeout()で設定します。役割が異なるため、両方を組み合わせて使うのが安全です。

まとめ

HttpClientはJava 11から標準搭載されたHTTPクライアントで、GET/POSTの基本から非同期処理、API連携までこなせます。

2026年時点の最新LTSであるJava 25でも同じAPIがそのまま使えるため、ここで紹介したコードは長く使い回せます。

HttpClientの利点と活用場面

HttpClientの利点は明確です。

  • 標準API: 外部ライブラリを追加する手間がありません。
  • モダンな機能: HTTP/2や非同期処理に標準で対応しています。
  • シンプルなAPI: ビルダーパターンにより、直感的で読みやすいコードを記述できます。
  • 柔軟性: タイムアウトやリダイレクトなど、詳細な設定が可能です。

Web APIとの通信、マイクロサービス間の連携、Webスクレイピングなど。

現代のJavaアプリケーション開発におけるHTTP通信の場面では、HttpClientが第一の選択肢です。

NEXT STEP
まずはGETリクエストの例をそのまま動かし、URLを使いたいAPIに差し替えるところから始めてみてください。

途中経過は、また書きます。

REVISIONS / 改訂履歴
REV.DATE内容
42026-10-15図解を描き直し(図面のデザインにそろえた)
32026-10-14雰囲気だけの挿絵を外した
22026-08-072026年8月時点の情報で全体を見直し
12025-07-21初版
NEXT READ本棚から1本

AIで開発を組み直すなら、まずこの5冊から

入門・実践・設計まで、Claude Codeの本をレベル別に5冊選んで比べました。

→ おすすめ5冊を見る

  • この記事を書いた人
  • 最新記事

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

𝕏

Java入門