Java for Backend Developers

See what Java 25 gives a back-end developer out of the box: static types, a large standard library, cheap threads and a runtime built for long-lived services.

Syntaximport com.sun.net.httpserver.HttpServer; void main() throws Exception { var server = HttpServer.create(new InetSocketAddress("127.0.0.1", 8080), 0); server.createContext("/health", exchange -> { ... }); server.start(); }

Java is a compiled, statically typed language that runs on the Java Virtual Machine (JVM). For back-end work that combination matters more than any single feature: the compiler rejects whole classes of mistakes before a request ever arrives, and the JVM is built to run one process for weeks while it keeps optimising the code that is actually hot.

Why teams pick it for services

  • Types that carry the design. Records, enums and sealed interfaces describe your API's data. When you add a new case, the compiler points at every switch that has not handled it yet.
  • A standard library that covers the basics. An HTTP client, a small HTTP server, java.time, BigDecimal, collections, concurrency and JDBC all ship with the JDK. You add libraries for JSON, database drivers and frameworks, not for the basics.
  • Threads that scale. Virtual threads (final since Java 21) let you write plain blocking code, one thread per request, and still handle thousands of requests waiting on a database or another service.
  • A stable platform. Code written years ago still compiles. A new feature release arrives every six months, and every two years one of them is a long-term support (LTS) release. Java 25, released in September 2025, is the current LTS and the version this course uses.

What it costs

Java is more verbose than JavaScript or Python, although less than its reputation: compact source files, var, records and switch expressions removed most of the ceremony. A JVM also takes longer to start and uses more memory at rest than a small Go or Node.js process. For a service that runs all day this rarely matters; for very short-lived jobs it can, and later lessons cover the AOT cache and smaller runtimes that help.

How this course fits with Spring Boot

Most Java services in production use a framework such as Spring Boot. This course deliberately stays on the JDK: you build an HTTP API, talk to a database with JDBC and handle errors by hand. When you move on to the Spring Boot course you will know what the framework is doing for you, which is exactly what you need when something breaks at 2 a.m.

The example below is a complete service: an HTTP server with one JSON endpoint, called by the JDK's own HTTP client, in a single file with no dependencies.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Build an order or billing API where records and sealed types model each request and response exactly.
  • Run an I/O-heavy service that calls databases and other APIs, using virtual threads instead of callback chains.
  • Write a small internal tool or health-check client with only the JDK's HTTP client and no dependencies.

More examples

Sealed types make the compiler check every outcome

The switch is exhaustive over a sealed interface, so the compiler, not a code review, catches the endpoint that forgot to map a new outcome to a status code.

Example · java
sealed interface Lookup permits Found, Missing {}
record Found(String json) implements Lookup {}
record Missing(String id) implements Lookup {}

Lookup findUser(String id) {
    return id.equals("42") ? new Found("{\"id\":42,\"name\":\"Ada\"}") : new Missing(id);
}

int statusFor(Lookup result) {
    // No default branch: add a third Lookup type and this stops compiling until you handle it.
    return switch (result) {
        case Found found -> 200;
        case Missing missing -> 404;
    };
}

void main() {
    for (String id : List.of("42", "7")) {
        IO.println("GET /users/" + id + " -> " + statusFor(findUser(id)));
    }
}

// Output:
// GET /users/42 -> 200
// GET /users/7 -> 404

Ten thousand blocking calls on virtual threads

Each task blocks for 100 ms, which would take over 16 minutes one after another, yet the whole batch finishes in seconds because a sleeping virtual thread does not hold an operating system thread. This is the model servers use to handle many slow I/O calls with simple blocking code.

Example · java
void main() throws Exception {
    var completed = new AtomicInteger();
    long start = System.nanoTime();

    // One virtual thread per task, like one thread per incoming request.
    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        for (int i = 0; i < 10_000; i++) {
            executor.submit(() -> {
                Thread.sleep(Duration.ofMillis(100)); // stands in for a database call
                return completed.incrementAndGet();
            });
        }
    } // close() waits for every task

    long millis = (System.nanoTime() - start) / 1_000_000;
    IO.println("completed: " + completed.get());
    IO.println("faster than running them one by one: " + (millis < 10_000 * 100));
}

// Output:
// completed: 10000
// faster than running them one by one: true

Money and dates from the standard library

BigDecimal gives exact decimal arithmetic for prices and java.time handles calendar dates, both without a dependency. The last line shows why money is never stored in a double.

Example · java
record Line(String sku, int quantity, BigDecimal unitPrice) {
    BigDecimal total() {
        return unitPrice.multiply(BigDecimal.valueOf(quantity));
    }
}

void main() {
    var lines = List.of(
        new Line("BOOK-1", 2, new BigDecimal("19.99")),
        new Line("PEN-7", 3, new BigDecimal("1.50")));

    BigDecimal subtotal = lines.stream().map(Line::total).reduce(BigDecimal.ZERO, BigDecimal::add);
    LocalDate issued = LocalDate.of(2026, 3, 1);

    IO.println("Subtotal: " + subtotal);
    IO.println("Due: " + issued.plusDays(30));
    IO.println("Why not double: " + (0.1 + 0.2));
}

// Output:
// Subtotal: 44.48
// Due: 2026-03-31
// Why not double: 0.30000000000000004

Discussion

  • Be the first to comment on this lesson.