What are the three core types in java.net.http, and how do they fit together to make an HTTP call?
answer
- HttpClient = reusable engine + connection pool
- HttpRequest = one immutable call (uri/method/headers/body)
- HttpResponse<T> = statusCode/headers/body
- BodyPublisher sends body, BodyHandler shapes the reply
- newHttpClient() / newBuilder() / send()
basics
~10 sYou build an HttpClient once, create an HttpRequest describing the URL/method/headers, then call client.send(request, handler). The result is an HttpResponse holding the status code, headers, and body.
solid answer
~40 sThe API revolves around three immutable types. HttpClient is the long-lived, reusable, thread-safe object that holds configuration (HTTP version, redirect policy, connection pool, executor) — you build one and share it. HttpRequest describes a single call: URI, method, headers, and an optional body supplied by a BodyPublisher. HttpResponse<T> is the result, exposing statusCode(), headers(), and body() where T is whatever a BodyHandler produced (a String, byte[], Path, Stream<String>, etc.). The typical flow: HttpClient.newHttpClient(), then HttpRequest.newBuilder(uri).GET().build(), then client.send(request, BodyHandlers.ofString()). Builders make requests/clients immutable and safe to reuse. The handler is what turns raw response bytes into the typed body, so the same request can yield a String or be streamed to a file just by swapping the handler.
go deeper
Can name the three types and write the basic GET-to-String flow, knowing the status code lives on the response.
Explains immutability, the BodyPublisher vs BodyHandler split, and why the client is shared/thread-safe.
Discusses connection pooling, HTTP/2 default, and that non-2xx is not an exception; reasons about handler choice (ofString vs ofFile vs streaming) for memory.
Frames the API as a clean separation of configuration (client), intent (request), and delivery (handler), and weighs it against alternatives (OkHttp, WebClient) for a platform standard.
## What is HTTP and why this API exists HTTP (HyperText Transfer Protocol) is the request/response protocol the web runs on: a *client* sends a request (a method like GET, a URL, headers, optionally a body) to a *server*, which returns a *response* (a numeric status code such as 200, response headers, and a body). Before Java 11, the standard way to do this from Java was `HttpURLConnection`, a clunky 1990s API. `java.net.http` (incubated in Java 9, standardized in Java 11) is the modern replacement. ## The three core types **1. `HttpClient`** — the *engine*. It is configured once and reused for many requests. It holds: - the HTTP protocol version to prefer (HTTP/2 by default, falling back to HTTP/1.1), - a **connection pool** (reusable TCP/TLS connections so you don't pay handshake cost every call), - redirect policy, proxy, authenticator, and an `Executor` (thread pool) for async work. You create it with `HttpClient.newHttpClient()` (defaults) or `HttpClient.newBuilder()....build()`. It is **immutable and thread-safe**, so one instance should be shared across your application — creating a new client per request wastes the connection pool and threads. **2. `HttpRequest`** — a *value object* describing ONE call. Built via `HttpRequest.newBuilder()`: - `.uri(URI.create("https://..."))` — where to send it, - the method: `.GET()`, `.POST(bodyPublisher)`, `.PUT(...)`, `.DELETE()`, or `.method(name, publisher)`, - `.header(name, value)` / `.headers(...)` — request headers, - `.timeout(Duration)` — per-request timeout. A **`BodyPublisher`** supplies the request body for methods that have one (e.g. `BodyPublishers.ofString(json)`). `HttpRequest` is immutable once built. **3. `HttpResponse<T>`** — the *result*. Its type parameter `T` is the shape of the body you asked for. Key methods: - `statusCode()` → an `int` (200, 404, 500…), - `headers()` → an `HttpHeaders`, - `body()` → the typed body `T`, - `uri()` (final URI after redirects), `version()`. The `T` is decided by a **`BodyHandler<T>`** you pass to `send`. `BodyHandlers.ofString()` gives `HttpResponse<String>`; `BodyHandlers.ofByteArray()` gives `byte[]`; `BodyHandlers.ofFile(path)` streams straight to disk and gives `Path`. The handler decouples *what request you make* from *how you want the body delivered*. ## Putting it together ```java HttpClient client = HttpClient.newHttpClient(); // reuse this HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.example.com/data")) .GET() .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); int code = response.statusCode(); // e.g. 200 String body = response.body(); // the response text ``` ## Mental model Think of `HttpClient` as a *telephone* (set up once, dial many times), `HttpRequest` as the *message you want to send*, the `BodyHandler` as *how you want the reply written down*, and `HttpResponse` as *the reply itself*. The builder pattern and immutability mean each piece is safe to reuse and free of hidden mutable state.
- Why is HttpClient meant to be shared rather than created per call?It owns a connection pool and an executor; reusing it lets TCP/TLS connections be kept alive and reused, avoiding repeated handshakes, and avoids spinning up redundant thread pools.
- Does a 404 or 500 response cause send() to throw?No. Those are valid HTTP responses; send() returns normally and you inspect statusCode(). Exceptions are thrown for I/O failures, timeouts, or interruption — not for non-2xx statuses.
saying these in an interview costs you the question
- Creating a new HttpClient per request (wastes the connection pool and threads)
- Thinking HttpResponse exposes the body type without a BodyHandler
- Confusing BodyPublisher (request body) with BodyHandler (response body)
- Believing the client throws on a 404/500 — non-2xx statuses are normal responses, not exceptions