java.net.http.HttpClient
Send synchronous requests.
java.net.http.HttpClient is a free Java Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Java Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
The Modern HTTP Client
Since Java 11, the JDK ships a modern HTTP client in the java.net.http package. It replaces the old HttpURLConnection with a clean, fluent API that supports HTTP/1.1 and HTTP/2.
The three core types are:
- HttpClient — sends requests and manages config (timeouts, proxies, redirects).
- HttpRequest — an immutable description of what to send.
- HttpResponse — the result, including status, headers, and body.
Creating an HttpClient
You build a client with the static newBuilder() method. A single client can be reused for many requests, and it is thread-safe.
Here we configure HTTP/2 and a connect timeout. newHttpClient() is also available for a quick default instance.
import java.net.http.HttpClient;
import java.time.Duration;
public class Main {
public static void main(String[] args) {
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(10))
.build();
System.out.println("Client version: " + client.version());
}
}Building a Request
HttpRequest is immutable and built with a fluent builder. At minimum you supply a URI; by default it performs a GET.
You convert a string to a URI with URI.create(...).
import java.net.URI;
import java.net.http.HttpRequest;
public class Main {
public static void main(String[] args) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com/data"))
.GET()
.build();
System.out.println("Method: " + request.method());
System.out.println("URI: " + request.uri());
}
}Sending Synchronously
The send method blocks the calling thread until the response arrives. It needs a BodyHandler that tells the client how to interpret the response body.
BodyHandlers.ofString() reads the body into a String.
import java.net.URI;
import java.net.http.*;
public class Main {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com"))
.build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Status: " + response.statusCode());
}
}Reading the Status Code
The response exposes statusCode() as an int. Codes in the 2xx range mean success, 3xx are redirects, 4xx are client errors, and 5xx are server errors.
Note: the HTTP client never throws on a 4xx/5xx response — you must check the code yourself.
public class Main {
public static void main(String[] args) {
int status = 404;
String label;
if (status >= 200 && status < 300) label = "Success";
else if (status >= 300 && status < 400) label = "Redirect";
else if (status >= 400 && status < 500) label = "Client error";
else label = "Server error";
System.out.println(status + " -> " + label);
}
}Reading the Body
With BodyHandlers.ofString(), response.body() returns a String. Other handlers exist for bytes, files, and streams (covered later).
This snippet simulates the body content you would receive.
public class Main {
public static void main(String[] args) {
String body = "{\"message\":\"hello\"}";
System.out.println("Body length: " + body.length());
System.out.println("Body: " + body);
}
}Handling Redirects
By default the client does not follow redirects. Configure this on the builder with followRedirects(...).
Options in HttpClient.Redirect are NEVER, ALWAYS, and NORMAL (follow except HTTPS to HTTP downgrades).
import java.net.http.HttpClient;
public class Main {
public static void main(String[] args) {
HttpClient client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
System.out.println("Redirect policy: " + client.followRedirects());
}
}Per-Request Timeouts
The connect timeout lives on the client. A request timeout (how long to wait for a response) lives on the request via timeout(...).
If it expires, send throws an HttpTimeoutException.
import java.net.URI;
import java.net.http.HttpRequest;
import java.time.Duration;
public class Main {
public static void main(String[] args) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com"))
.timeout(Duration.ofSeconds(5))
.build();
System.out.println("Timeout set: " + request.timeout().isPresent());
}
}Inspecting Response Headers
The response carries an HttpHeaders object accessible via headers(). Header lookups are case-insensitive and may return multiple values, so firstValue(...) returns an Optional<String>.
import java.util.Optional;
public class Main {
public static void main(String[] args) {
// Simulating a header lookup result
Optional<String> contentType = Optional.of("application/json");
System.out.println("Content-Type: " + contentType.orElse("unknown"));
}
}Checked Exceptions
The synchronous send declares two checked exceptions:
- IOException — network or protocol failure.
- InterruptedException — the blocked thread was interrupted.
You must catch or declare both. Async calls (next lesson) avoid this by returning a future.
import java.io.IOException;
public class Main {
public static void main(String[] args) {
try {
// client.send(...) would go here
throw new IOException("connection refused");
} catch (IOException e) {
System.out.println("Network problem: " + e.getMessage());
}
}
}A Reusable Client
Best practice: create one HttpClient and reuse it across the application. It pools connections and is fully thread-safe.
Creating a new client per request wastes resources and defeats HTTP/2 multiplexing.
import java.net.http.HttpClient;
public class Main {
private static final HttpClient SHARED = HttpClient.newHttpClient();
public static void main(String[] args) {
System.out.println("Shared client ready: " + (SHARED != null));
}
}Quick Check
Test your understanding of the synchronous HTTP client.
Recap
You learned the basics of the JDK HTTP client:
- HttpClient.newBuilder() configures version, timeout, and redirects; reuse one instance.
- HttpRequest.newBuilder().uri(...).GET() builds an immutable request.
- client.send(request, BodyHandlers.ofString()) blocks and returns an
HttpResponse<String>. - Read results with
statusCode(),body(), andheaders(). - Non-2xx codes do not throw — check them yourself.
Frequently asked questions
Is the “java.net.http.HttpClient” lesson free?
Yes — the full text of “java.net.http.HttpClient” is free to read here on the web, and the Java Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Java Academy course, upgrade to CoddyKit PRO.
What will I learn in “java.net.http.HttpClient”?
Send synchronous requests. You practise Java Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Java Academy?
No prior experience is required. Java Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “java.net.http.HttpClient” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Java Academy lesson?
Yes. Every Java Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- java.net.http.HttpClient
- Async Requests
- Request Bodies and Headers
- Handling Responses