Java 25 runs a single source file with one command and a bare void main, with no class wrapper to write first. Learn what the JDK contains, why Java 25 is the version to learn, and how to read compiler errors and exceptions.
Every Java variable holds either a primitive value or a reference to an object. Knowing which one explains silent overflow, pass-by-value, == versus equals, and why two Integers holding 128 aren’t equal.
Java’s if and loops look like C’s, with a few rules that catch real bugs. The old switch falls through by default, and switch expressions fix that while making the compiler check every enum value.
A Java class describes what an object holds and what it can do, and a constructor decides how each object starts life. Learn fields, this, constructors, private, static and final by running small programs.
Java strings never change, so every edit builds a new one. Learn the String methods and their traps, why += in a loop copies everything, StringBuilder, text blocks, Unicode, and arrays with the Arrays helpers.
A Java record declares a small immutable data class in one line, and the compiler writes its constructor, accessors, equals, hashCode and toString. Learn what records give you, what they refuse, and where they fall short.
Interfaces say what a type can do, inheritance hands a class everything its parent does, and composition wraps an object instead. Learn default methods, super, abstract classes and the fragile base class trap by running small programs.
Sealed types give Java a closed list of subtypes, and pattern matching lets a switch test and take apart each one. Together they turn a forgotten case into a compile error instead of a silent bug.
Java splits exceptions into checked ones the compiler makes you handle and unchecked ones it doesn’t. Learn try, catch and finally, how to throw and wrap exceptions well, and how try-with-resources closes things in reverse order.
A Java HashMap finds keys with hashCode and equals, so a broken contract or a key changed after put makes entries vanish. Learn the rules, why TreeSet uses compareTo instead, and which collection to pick.
Generics let the compiler check what goes into a list, a box or a method, and then erase that information before the program runs. Learn bounds, wildcards, PECS and how to read a JDK signature by running small programs.
Java lambdas turn a small piece of behaviour into a value, and streams chain those values into lazy pipelines. See how a pipeline really runs, which collectors to reach for, and when a plain loop reads better.
Java lets any reference be null, so a missing value can crash code far from where it went missing. Objects and Optional make a missing value visible, and java.time makes you say which moment, in which city, you mean.
Build a two-module Java project with nothing but javac, jar and java. See how packages, imports, the classpath, JAR files and module-info.java fit together, and what Maven and Gradle add on top.
Where Java keeps your variables and objects, how the garbage collector decides what to free, why a garbage-collected program can still leak or run out of memory, and how the JIT makes code faster the longer it runs.
Two Java threads that update the same variable can silently lose writes. See why count++ is a race, how synchronized, volatile, atomics and locks fix it, and how to force and detect a deadlock.
Java’s executors run your tasks on a fixed pool of threads and hand back a Future. Learn submit, invokeAll, cancellation, CompletableFuture chains, timeouts, semaphores and blocking queues, with output that never flakes.
Java can run a hundred thousand blocking tasks on a handful of OS threads. Learn how virtual threads mount and unmount, what still pins them, why ScopedValue replaces ThreadLocal, and how StructuredTaskScope cancels work that fails.
Java’s JDK can both call and serve HTTP with no libraries. Learn HttpClient, sync and async, then build a small JSON tasks service on the built-in HttpServer with virtual threads, correct status codes and a clean shutdown.
Build a small JDK-only test runner from an annotation and reflection, test package-private code with –patch-module, then ship a Java service as a modular JAR, a jlink runtime image and a jpackage installer.