Java, from your first program to mastery

Eleven stages in dependency order, with what to learn, what to build, and how to tell you actually finished each one. Roughly 400 hours of deliberate work end to end — most people take six to eighteen months, and skipping stages is what makes Java feel harder than it is.

Stages
11
Topics
94
Hours of work
~401
Deep dives
5

How to use this

Do not read this like a checklist. Each stage ends with a milestone you have to build, and the milestone is the test — if you cannot produce it without copying, the stage is not finished no matter how many topics you have read. Where a topic links to a deep dive, read it when you hit that topic, not before.

  1. 1

    Run your first program without an IDE

    beginner~6h

    Understand what actually happens between a .java file and output on your screen. Doing this once by hand means IDE errors stop being magic.

    You have finished this stage when

    Compile and run a multi-class program from a terminal using javac and java, with no IDE open, and explain what the .class files are.

    • JDK vs JRE vs JVM

      Which one you install, and why 'JRE' barely exists as a separate download any more.

    • Installing a JDK and picking a version

      Choose an LTS — 21 or 25. Know why you would not start on 8.

    • javac and java on the command line

      Compile to .class, run by class name, understand what -cp does.

    • The main method signature

      Why it must be public static void main(String[]) and what each keyword is doing.

    • JShell for experiments

      Test a line of Java in two seconds without a class or a build.

    • Choosing an IDE

      IntelliJ IDEA Community or VS Code. Configure it after you can build without it.

  2. 2

    Values, types and control flow

    beginner~30h

    The mechanical core of the language. The one idea that matters more than all the syntax: the difference between a primitive and a reference.

    You have finished this stage when

    A command-line program that reads numbers from the user, validates the input, and prints a formatted table of results — with no crash on bad input.

    • Primitives and their ranges

      int, long, double, boolean, char. Why int overflows silently at ~2.1 billion.

    • References vs primitives

      A variable holds a value or an address. This single distinction explains most beginner bugs.

    • Integer vs int, and autoboxing

      Where boxing happens invisibly, and why == on Integer breaks above 127.

    • double is not for money

      0.1 + 0.2 != 0.3. Use BigDecimal for currency, and know why.

    • var, and where it is allowed

      Local variables only. It is inference, not dynamic typing.

    • Operators and precedence

      Integer division truncating, % on negatives, ++ in expressions, short-circuit && and ||.

    • if, switch, and switch expressions

      Old switch falls through; a switch expression with -> does not and can return a value.

    • Loops, break and continue

      for, while, do-while, enhanced for. When an index loop is the wrong tool.

    • Arrays

      Fixed length, zero-indexed, default-initialised. Arrays.toString and 2D arrays.

    • Strings for beginners

      Immutable, compared with equals not ==, and formatted with printf or formatted().

  3. 3

    Methods and how a program is organised

    beginner~20h

    Turn one long main method into named pieces, then into files and packages. This is where code starts being readable by someone else.

    You have finished this stage when

    Refactor your stage-2 program into at least three classes in two packages, with no logic left in main beyond wiring.

    • Methods, parameters, return values

      Java passes everything by value — including references, which is why the object can still be mutated.

    • Overloading

      Same name, different parameters. How the compiler picks, and why widening beats boxing.

    • static vs instance

      What belongs to the class and what belongs to an object. Why static is not free of consequences.

    • Scope and shadowing

      Block, method, field. Why a parameter named the same as a field silently wins.

    • Packages and imports

      Directory structure is the package name. What a wildcard import actually costs.

    • Access modifiers

      public, protected, package-private, private. Default to the most restrictive that compiles.

    • Javadoc

      Document the contract and the why, not the syntax.

  4. 4

    Objects and OOP that holds up

    beginner~45h

    The stage most tutorials rush and most interviews probe. Aim for the reasoning behind each mechanism, not the four-bullet definition.

    You have finished this stage when

    Model a small domain — orders, payments, whatever — using interfaces for behaviour and records for data, with no inheritance used purely for reuse.

    • Classes, fields, constructors

      Constructor chaining with this(), why a field should usually be final.

    • Encapsulation, and why getters are not the point

      Hiding state means protecting invariants, not adding a getter per field.

    • Inheritance and super

      extends, method overriding, @Override, and why a constructor cannot be overridden.

    • Polymorphism

      One reference type, many runtime types. Dynamic dispatch is the mechanism.

    • Abstract classes vs interfaces

      State and partial implementation vs contract and multiple inheritance of behaviour.

    • Composition over inheritance

      Inheritance couples you to a superclass forever. Prefer delegating to a field.

    • Object's methods

      Deep dive →

      toString, equals, hashCode, getClass. Everything you write inherits these.

    • Records

      A semantic claim that a type IS its data — equals, hashCode and toString come free.

    • Enums

      Type-safe constants that can hold fields and methods, and switch exhaustively.

    • Immutability in practice

      final fields are not enough — you need defensive copies on the way in and out.

    • static nested vs inner classes

      An inner class holds a hidden reference to its outer instance. That is a leak waiting to happen.

  5. 5

    The core library you will use every day

    intermediate~50h

    Collections and generics. This is the densest part of real Java work and the densest part of any interview.

    You have finished this stage when

    An in-memory repository that stores records, looks them up by key in O(1), returns them sorted several ways, and groups them — all without a database.

    • List, Set, Map — choosing between them

      Pick by the question you need to answer, not by familiarity.

    • ArrayList vs LinkedList

      Deep dive →

      Measure it. The folklore about mid-list insertion is wrong.

    • HashMap internals

      Deep dive →

      Buckets, hashing, collisions, resize, treeify. The most-asked question in Java.

    • Generics and type parameters

      Compile-time safety, and why List<Object> is not a supertype of List<String>.

    • Type erasure and wildcards

      Generics vanish at runtime. PECS: extends to read, super to write.

    • Iteration and ConcurrentModificationException

      Why removing during a for-each throws — and the case where it silently does not.

    • Comparable and Comparator

      Natural order vs supplied order, and why an inconsistent compare() throws from inside the sort.

    • String, StringBuilder and concatenation

      Deep dive →

      Immutability, the pool, and what + actually compiles to on a modern JDK.

    • Optional, used properly

      A return type. Never a field, never a parameter.

    • java.time

      LocalDate, Instant, ZonedDateTime, Duration. Never touch Date or Calendar again.

  6. 6

    Failure, files and resources

    intermediate~30h

    Where the difference between a ten-minute incident and a two-day one is decided. Handling failure well is a senior skill you can start now.

    You have finished this stage when

    An importer that reads a CSV or JSON file, reports every bad row with its line number, and finishes the good rows instead of dying on the first problem.

    • Checked vs unchecked exceptions

      Deep dive →

      The mechanism, and why modern frameworks abandoned checked exceptions.

    • try, catch, finally

      How finally can silently discard the exception you needed to see.

    • try-with-resources and suppressed exceptions

      It preserves the original failure. That is correctness, not convenience.

    • Designing a custom exception

      Extend RuntimeException, always accept a cause, carry fields instead of a formatted string.

    • Reading NullPointerExceptions

      Helpful NPE messages name the exact expression that was null — if you compiled with debug info.

    • java.nio.file

      Path, Files.readString, Files.lines, and why you should not use File any more.

    • Streams of bytes and characters

      InputStream vs Reader, and why an explicit charset is not optional.

    • Serialization, and why to avoid it

      Java serialization is a security liability. Use JSON with a library instead.

  7. 7

    Modern Java

    intermediate~45h

    The largest change in how Java is written. Everything from Java 8 onward that you are now expected to know by default.

    You have finished this stage when

    Rewrite your stage-5 repository using streams — then identify the places where the loop was clearer and put those back, with a reason.

    • Lambdas and functional interfaces

      A lambda is an implementation of a single-method interface. No functional interface declares throws.

    • Method references

      Four forms. The unbound instance form silently changes what the first parameter means.

    • Streams and laziness

      Nothing runs until a terminal operation. peek() proves it in one line.

    • Collectors

      groupingBy, partitioningBy, joining, toMap — and how toMap throws on duplicate keys.

    • When not to use a stream

      Loops win for early exit, index arithmetic, and debuggability.

    • parallelStream and its traps

      It uses one shared pool for the whole JVM. One slow task starves everything.

    • switch expressions and pattern matching

      Return values, no fall-through, exhaustiveness, and instanceof with a binding.

    • Sealed types

      A closed set of subtypes lets the compiler prove your switch is complete.

    • Text blocks

      Multi-line strings, and how incidental indentation is stripped.

    • The module system, in practice

      Mostly why reflection into java.* now needs --add-opens.

  8. 8

    Concurrency

    advanced~60h

    Where senior rounds are decided. Every question here has a wrong answer that sounds right, and correctness cannot be checked by running it once.

    You have finished this stage when

    Fetch from several sources in parallel with a bounded pool, a timeout per task, and no shared mutable state — then explain why it is correct rather than saying it works.

    • Threads, and why you rarely create one

      Thread, Runnable, join. Then never do this by hand again.

    • Races, atomicity and visibility

      Three different problems. count++ is not atomic; volatile does not make it so.

    • synchronized and locks

      What the monitor protects, why lock scope matters, and how deadlock happens.

    • The Java memory model

      happens-before is the only correct mental model. 'It works on my machine' is not one.

    • Concurrent collections

      ConcurrentHashMap over synchronizedMap, and why HashMap is still not thread-safe.

    • ExecutorService and pool sizing

      An unbounded queue turns a pool into a memory leak with a rejection policy that never fires.

    • CompletableFuture

      Composing async work, and how thenApply vs thenApplyAsync changes which thread runs your code.

    • Virtual threads

      They fix blocking I/O, not CPU-bound work. Know what pinning was.

    • ThreadLocal and pooled threads

      Pooled threads never die, so the value never becomes unreachable. That is the leak.

  9. 9

    The JVM underneath

    advanced~45h

    Stop guessing about performance and memory. The goal here is a measurement habit, not a set of facts.

    You have finished this stage when

    Take a heap dump from a program that leaks, find the retaining path, fix it, and prove the fix with a second dump.

    • Class loading and initialisation

      When static initialisers run, and what a ClassLoader leak looks like.

    • Heap, stack and Metaspace

      What lives where, and why escape analysis makes the rule less absolute than tutorials say.

    • Garbage collection

      Generational collection, G1 as the default, and why you should not tune before measuring.

    • Memory leaks in a managed language

      Reachability, not allocation. Static collections, listeners, ThreadLocals, ClassLoaders.

    • Heap dumps and profiling

      JFR, VisualVM, jcmd, jmap. Learn the tools before you need them at 2am.

    • JIT compilation

      Interpretation, C1, C2, inlining, warm-up. Why your first benchmark result is a lie.

    • Benchmarking with JMH

      Dead-code elimination and warm-up are why hand-rolled timing loops mislead.

    • Reading a stack trace properly

      Caused by, suppressed, and where the real frame is.

  10. 10

    Build, test and ship

    advanced~40h

    Everything around the language that a job actually requires. A developer who cannot write a test is not employable at this level.

    You have finished this stage when

    A project on Maven or Gradle with a test suite that fails the build when broken, running in CI on every push.

    • Maven or Gradle

      Dependencies, the build lifecycle, and what transitive dependency resolution does.

    • Dependency and version management

      Version conflicts, BOMs, and auditing what you actually shipped.

    • JUnit 5

      Lifecycle, assertions, parameterised tests, and testing that an exception is thrown.

    • Writing tests worth having

      Test behaviour, not implementation. A test that mirrors the code proves nothing.

    • Mockito, and when not to mock

      Mock the boundary you do not own. Mocking your own domain is a design smell.

    • Testcontainers

      Test against a real database in Docker instead of an in-memory imitation.

    • Logging

      SLF4J with an implementation, parameterised messages, levels that mean something, no PII.

    • Continuous integration

      Build, test and static analysis on every push, failing loudly.

  11. 11

    Judgement

    master~30h

    What separates someone with eight years from someone with two years and six repetitions. None of this is syntax, and none of it is memorisable.

    You have finished this stage when

    Review a stranger's pull request and justify every comment with a mechanism or a measurement — no 'best practice' without a because.

    • API design

      Make the wrong call hard to write. Names, defaults, and what you can never take back.

    • An error-handling strategy

      One decision per layer about what it throws, what it wraps, and what it logs.

    • Reading the JDK source

      It is on your machine. Reading HashMap.java is faster than reading ten articles about it.

    • Knowing what changed and when

      Which LTS you are on, what the next one changes, and what breaks on migration.

    • Measuring instead of believing

      Profile before optimising, benchmark properly, and be willing to be wrong.

    • Explaining a mechanism out loud

      If you cannot explain why, you have memorised it. This is exactly what interviews test.

    • What to learn after Java

      Spring Boot, JPA and SQL, HTTP and REST, Docker, and one other language on the JVM.

Preparing for an interview instead?

The roadmap teaches the language. If you already know Java and have a round booked, the questions are indexed by how many years of experience you are interviewing at.