A language tour
Write once, run anywhere. Three billion devices. The language that made the JVM, and then the JVM outlasted the language wars.
01 — The JVM
Java's promise in 1995 was radical: compile once, run anywhere. The Java Virtual Machine abstracts the hardware. The same .class file runs on Windows, Linux, macOS, Android, and a Blu-ray player's firmware. No other platform achieved this breadth.
"Java is the most popular programming language in the world — not because it's the best at anything in particular, but because it's reliably good at everything."
— James Gosling, creator of Java// The program every Java developer has written public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, world!"); } } // javac HelloWorld.java → HelloWorld.class (bytecode) // java HelloWorld → runs on any JVM anywhere // // The same .class file runs unchanged on: // Windows x64, Linux ARM, macOS Apple Silicon, // Android (via ART), embedded JVMs in printers...
The verbosity of public static void main(String[] args) became famous — and later, Java 21 introduced void main() as a preview feature, finally trimming the ceremony from the simplest program.
02 — Generics
Java's generics — added in Java 5 — let you write type-safe collections and algorithms without sacrificing reuse. They're not as powerful as Rust's or Haskell's, but they catch a whole class of runtime errors at compile time and remain one of Java's great contributions to mainstream OOP.
import java.util.*; import java.util.function.*; // A generic pair — works for any two types record Pair<A, B>(A first, B second) { public Pair<B, A> swap() { return new Pair<>(second, first); } } // Bounded type parameters — T must be Comparable public static <T extends Comparable<T>> T max(List<T> list) { return list.stream().max(Comparator.naturalOrder()).orElseThrow(); } // Diamond operator infers type arguments var scores = new HashMap<>(); // Map<String,Integer> inferred scores.put("Alice", 95); scores.put("Bob", 88);
Java records (Java 16) are the modern replacement for plain data classes. One line declares the type with a canonical constructor, equals, hashCode, and toString — no Lombok required.
03 — Streams & Lambdas
Java 8 added lambdas and the Stream API — a delayed but decisive step toward functional style. Streams let you express data transformations as pipelines of lazy operations. Combined with method references, the result is remarkably expressive for a statically typed language.
import java.util.*; import java.util.stream.*; record Employee(String name, String dept, double salary) {} var employees = List.of( new Employee("Alice", "Eng", 120_000), new Employee("Bob", "Eng", 95_000), new Employee("Carol", "HR", 80_000), new Employee("Dave", "Eng", 110_000) ); // Average salary per department var avgByDept = employees.stream() .collect(Collectors.groupingBy( Employee::dept, Collectors.averagingDouble(Employee::salary) )); // {Eng=108333.33, HR=80000.0}
Method references like Employee::dept are not string-based reflection — they're typed lambdas. The compiler checks that dept exists and returns the right type. Refactoring is safe.
04 — Sealed Classes & Pattern Matching
Modern Java — from version 17 onward — has sealed classes and pattern matching in switch. Finally, the exhaustive type hierarchies you once needed visitors and double-dispatch for can be expressed cleanly, with compiler-checked completeness.
// Sealed interface — only these implementations are allowed sealed interface Shape permits Circle, Rectangle, Triangle {} record Circle(double radius) implements Shape {} record Rectangle(double w, double h) implements Shape {} record Triangle(double b, double h) implements Shape {} double area(Shape s) { return switch (s) { case Circle c -> Math.PI * c.radius() * c.radius(); case Rectangle r -> r.w() * r.h(); case Triangle t -> 0.5 * t.b() * t.h(); // No default needed — sealed = exhaustive }; }
Because Shape is sealed, the compiler knows exactly which subtypes exist. The switch expression is exhaustive without a default branch — adding a new subtype forces you to handle it everywhere.
05 — Virtual Threads
Java 21's virtual threads — Project Loom — bring structured concurrency to Java without changing the programming model. You write blocking code; the JVM schedules millions of lightweight threads on a small OS thread pool. The simplicity of synchronous code, the scalability of async.
import java.util.concurrent.*; // Spawn 100,000 virtual threads — lightweight, cheap try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { IntStream.range(0, 100_000).forEach(i -> executor.submit(() -> { // Blocking I/O — the JVM parks the virtual thread, // frees the OS thread for other work var result = callDatabase(i); process(result); }) ); } // executor.close() waits for all tasks // Structured concurrency (Java 21 preview) try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { var user = scope.fork(() -> fetchUser(userId)); var order = scope.fork(() -> fetchOrder(orderId)); scope.join().throwIfFailed(); // Both calls happened in parallel }
Virtual threads are not green threads or coroutines — they're full Java threads with their own stack, monitored by the debugger, visible in stack traces. The programming model is unchanged; only the implementation is different.
06 — The Whole Picture
Banking systems, enterprise software, Android apps, big data pipelines — Java is everywhere you don't see it.
Java code from 2001 still compiles and runs on Java 21. No other major language has maintained this discipline for so long.
Ahead-of-time native compilation via GraalVM produces instant-startup binaries from Java code. No JVM warmup required.
The world's largest repository of reusable libraries. If a problem exists, a battle-tested Java library almost certainly solves it.
IntelliJ IDEA's Java support is the benchmark for IDE intelligence. Refactoring, analysis, and completion that actually works.
Records, sealed classes, pattern matching, virtual threads, text blocks — Java releases every 6 months and means it.