Core Java concepts explained in depth, one at a time.
How WeakHashMap wraps each key in a WeakReference so entries are removed automatically once nothing outside the map holds the key strongly, and why a value referencing its own key or an interned key defeats that.
How TreeMap keeps keys in ascending order via a red-black tree, why key equality there is decided by compareTo() rather than equals()/hashCode(), and the O(log n) cost that pays for.
How HashMap's hash-table storage gives average O(1) get/put with no iteration-order guarantee, and how put() silently replaces the value of an existing key.
How LinkedHashMap adds insertion-order (or access-order) iteration on top of HashMap's lookup performance, and how overriding removeEldestEntry() turns it into a bounded LRU cache.
How Comparator decouples ordering from a type entirely (unlike the one fixed Comparable.compareTo()), how comparing()/thenComparing() build multi-key comparators without a hand-written compare(), and what the static algorithms on Collections (sort, shuffle, unmodifiable/synchronized/checked views) actually guarantee.
The generic root interface of the Collections Framework — what add, remove, and bulk operations guarantee, and why several of them can throw at runtime instead of failing to compile.
Why Queue defines two parallel families of methods — one that throws on failure, one that reports it — and what that means for empty and fixed-length queues.
How PriorityQueue orders elements by comparator instead of insertion order via a heap, and why iterating it does not visit elements in priority order the way repeated poll() does.
How Deque extends Queue into a double-ended queue that can also act as a stack, and how its addFirst/addLast, peek, and remove families each split into a throwing and a reporting version.
How ArrayDeque's array-backed, capacity-unrestricted storage makes it the JDK-recommended stack/queue over legacy Stack or LinkedList when null elements aren't needed.
How Set forbids duplicates, how SortedSet and NavigableSet layer ordering and closest-match lookups on top of it, and why their range views are backed by the original set.
How TreeSet keeps elements in ascending order via a tree structure, why uniqueness there is decided by compareTo() rather than equals(), and the O(log n) cost that pays for.
How HashSet's hash-table storage gives average O(1) add/contains/remove with no iteration-order guarantee, and what breaks lookups when an element's hash-relevant fields mutate.
How LinkedHashSet adds insertion-order iteration on top of HashSet's lookup performance by threading a linked list through the hash table's entries.
How List builds a positional, duplicate-allowing sequence on top of Collection — indexed access, sublists, sort, and the unmodifiable lists produced by List.of().
JEP 431's SequencedCollection/SequencedSet/SequencedMap give every collection with a real encounter order — List, Deque, LinkedHashSet, TreeSet, LinkedHashMap, TreeMap — one uniform getFirst/getLast/addFirst/addLast/reversed() API, deliberately excluding HashSet, HashMap, and PriorityQueue since none has a stable encounter order to expose.
How LinkedList's node-based storage inverts ArrayList's trade-off — O(n) indexed access in exchange for cheap insert/remove — while implementing List, Deque, and Queue at once.
How ArrayList's resizable-array storage gives O(1) indexed access at the cost of O(n) inserts/removes away from the end, and how to manage its capacity directly.
What each interface is for, why Iterable forces you to implement Iterator, and why the JDK collections are built this way.
VarHandle (JEP 193) lets a program apply atomic and volatile-strength access to a plain field or array element, at a chosen memory-ordering strength, without wrapping it in a class like AtomicInteger.
ThreadLocal gives each thread its own isolated copy of a variable via a per-thread map, which is powerful but leaks on pooled threads unless paired with remove, and InheritableThreadLocal copies that value into child threads only at creation time.
The formal happens-before rules from JLS Chapter 17 that determine when a write in one thread is guaranteed visible to a read in another.
Why each thread's stack — its local variables, parameters, and call frames — is private to that thread and never a source of race conditions, while references stored on the stack can still point to shared, racy state on the heap.
How thenApply, thenCompose, thenCombine, allOf, and the exception handlers compose async work without blocking — and the nested-future, common-pool, and swallowed-exception traps that come with them.
ScopedValue (finalized in Java 25 by JEP 506) replaces ThreadLocal's mutable, leak-prone per-thread slot with an immutable binding whose lifetime is exactly the dynamic extent of a run()/call() lambda — no set(), no remove() to forget, and automatic inheritance by subtasks forked in a StructuredTaskScope.
StructuredTaskScope (still preview in Java 25, JEP 505) makes a fan-out of concurrent subtasks a try-with-resources block that owns their lifetime — no task can outlive its scope, siblings are cancelled automatically on failure, and the whole operation shows up nested in a thread dump instead of as a flat pool of Futures.
Covers the JCIP formula for computing an optimal thread pool size and the ThreadPoolExecutor API for work queues, saturation policies, thread factories, and extension hooks.
Concrete techniques for shrinking a contended lock's impact — narrowing its scope, splitting or striping it, avoiding hot fields, and when to swap it for a read-write lock or atomic instead.
Compares graceful shutdown() vs aggressive shutdownNow(), the awaitTermination()/close() idiom, poison pills for queue-based consumers, uncaught exception handlers, and JVM shutdown hooks and daemon threads.
How Thread.interrupt() actually works as a cooperative status flag, why swallowing InterruptedException is the most common concurrency bug, and how to cancel tasks correctly via interruption policy and Future.cancel().
How BlockingQueue's blocking put/take turn the producer-consumer pattern into a few lines of code, and why bounded queues provide real backpressure instead of just delaying an OutOfMemoryError.
Why a compound action built from thread-safe calls on a synchronized collection can still race, and how ConcurrentHashMap and CopyOnWriteArrayList solve it with weakly consistent iteration and atomic compound-action methods instead.
How lock-ordering deadlocks happen between threads, and how consistent lock ordering, open calls, and timed lock attempts prevent and diagnose them.
The shared internal machinery — a state field, a thread queue, and a handful of overridable hooks — that ReentrantLock, Semaphore, and CountDownLatch are all built on top of.
Explains why unsynchronized reads across threads can see stale or torn values, what volatile actually guarantees, and how to publish an object to another thread safely.
A divide-and-conquer engine for true multi-core parallelism, where a ForkJoinPool runs recursively-split RecursiveAction/RecursiveTask work via work-stealing.
The java.util.concurrent toolkit — executors and thread pools, Callable/Future, coordination primitives like CountDownLatch and Semaphore, explicit Locks, and atomic variables — as the higher-level alternative to hand-rolled Thread and synchronized code.
Why Thread.suspend/resume/stop were deprecated for corrupting shared state, the cooperative-shutdown pattern that replaced them, and why virtual threads solve a different problem (thread cost) rather than that same correctness problem.
String stores text as 16-bit UTF-16 code units, so characters outside the Basic Multilingual Plane like most emoji need a two-char surrogate pair to represent a single Unicode code point.
Deep dive into multi-level Collectors (groupingBy, partitioningBy, toMap, teeing, custom Collector.of) and the mechanics and pitfalls of parallel streams.
String literals are automatically interned into a shared pool, so == is true between two literals with the same content but false against a new String(...) with identical content — a direct, predictable consequence of where each string came from, not a language quirk.
How default, static, and private interface methods let an interface carry behavior so a published API can grow without breaking implementers, and the exact rules Java uses when two inherited defaults collide.
Since JDK 25 a constructor may run statements before super(...) or this(...), splitting its body into a prologue that validates arguments and initializes its own fields and an epilogue that can finally use the object under construction.
The four kinds of class-inside-a-class — static nested, inner, local, and anonymous — differ by how much enclosing context each captures, and knowing which you wrote tells you what it can reach, what it keeps alive in memory, and whether a lambda or record would say it better.
The switch statement and the switch expression are two constructs sharing one keyword: colon form falls through and produces nothing, while an arrow-form expression runs exactly one arm, produces a value via yield when the arm is a block, and must be provably exhaustive or the compiler rejects it.
How the triple-quote text block writes multi-line SQL, JSON, and HTML without \n escapes or + concatenation — and the precise incidental-whitespace algorithm behind it, where the closing delimiter's own column decides how much indentation is stripped, trailing spaces vanish unless fenced with \s, and line terminators are normalized to \n. Still exactly a String at runtime, with no interpolation and no way to tell it apart from a regular literal.
var lets the compiler infer a local variable's static type from its initializer — it is not dynamic typing and not Object, and combining it with the diamond operator quietly yields ArrayList<Object>.
Overload resolution happens at compile time based on the declared type of arguments, not runtime type — unlike overriding — and that gap shapes how method signatures should be designed.
Varargs implicitly allocate an array on every call and, when combined with generics, can let a ClassCastException slip past the compiler — here's why, and what @SafeVarargs actually promises.
Use constant-specific method bodies for per-constant behavior, and EnumSet/EnumMap instead of bit fields and ordinal-indexed arrays.
Why a stream is a lazy, single-use conduit rather than a data structure, how the pipeline shape (source → intermediate ops → terminal op) makes filter-then-findFirst short-circuit instead of scanning everything, how Collectors and the primitive stream specializations work under the hood, and when parallel streams actually help.
How a lambda expression implements a functional interface's single abstract method, the expression- vs. block-body syntax forms, the java.util.function catalogue, the effectively-final rule behind variable capture, and the four kinds of method references.
Why objects cost more than primitives (identity, header, heap allocation, GC pressure), how Value Classes let the JVM drop that overhead for types that are just their data, and why List<int> still doesn't exist because of generics' type erasure.
How a record turns a data-holder class into one line (canonical/compact constructors, free equals/hashCode/toString, shallow immutability), and how sealed classes/interfaces replace "anyone can extend this" with an explicit, exhaustive permits list — the pairing that makes closed hierarchies safe to model.
How parameterized types move casts and type-mismatch checks from runtime to compile time, what bounded types and PECS wildcards (extends/super) actually constrain, and why erasure means there's really only one class file per generic type at runtime.
How instanceof, switch, and record patterns let you test an object's structure and extract its data in one step, instead of casting after a type check.
Object.clone() and the Cloneable marker interface perform a shallow, constructor-bypassing copy with structural pitfalls around inheritance and final fields, which copy constructors and static factories avoid.
Data-Oriented Programming deliberately separates immutable, transparent data (records inside a sealed hierarchy) from the logic that acts on it, so an exhaustive switch with record patterns lets the compiler guarantee every variant is handled.
Optional exists to make 'this might not have a value' part of a method's return type instead of a null a caller has to remember to check — and why the JDK itself warns against using it as a field, a parameter, or inside a collection, plus the isPresent()-then-get() anti-pattern and the orElse-vs-orElseGet eagerness trap that trip up most first uses.
Implementing Serializable bypasses your constructor's invariant checks and exposes internals as permanent API — see why, and how readObject discipline, the serialization proxy pattern, and ObjectInputFilter contain the risk.
How to decide when a failure should be an exception at all, and whether a new exception type should be checked or unchecked.
Validate a method's or constructor's parameters at the top of the body and throw immediately with a clear exception, instead of letting a bad argument propagate into confusing failures or corrupted object state.
Extending a class you don't control is fragile because it depends on undocumented internal call patterns, and composition with forwarding avoids that risk — the same single-inheritance limitation also explains why interfaces, not abstract classes, are usually the right way to define a public type.
Making a class truly immutable takes more than final fields — it means copying mutable arguments on the way in and mutable fields on the way out, so no other reference into an object's state ever escapes.
The documented contracts behind Object's three most-overridden methods, why breaking symmetry or skipping hashCode() fails silently instead of at compile time, and how records satisfy all three for free.
Compare the three ways to write a Java singleton — public field, static factory, single-element enum — and the private-constructor idiom for noninstantiable utility classes.
Named static methods can replace or supplement constructors for clearer, cacheable, subtype-flexible object creation, and the Builder pattern tames classes with many optional fields.
Java's structured mechanism for run-time errors — try/catch/finally, throw/throws, checked vs unchecked exceptions, custom exception subclasses, and chained exceptions.
Covers Arrays.sort's primitive-vs-Comparator split, the silent undefined behavior of binarySearch on unsorted data, and why equals/toString need their deep variants for nested arrays.
Explains the strong/soft/weak/phantom reference hierarchy, ReferenceQueue polling, and how Cleaner replaces Object.finalize() for deterministic native-resource cleanup.
Covers the JCA/JCE provider architecture and the correct use of MessageDigest, SecureRandom, Cipher, Signature, and KeyStore, including why plain hashing and Random are unsafe substitutes for password hashing and secure token generation.
How the JVM loads, links, and initializes classes through a parent-first delegation hierarchy, and what happens when you write a custom class loader or load the same class twice.
String.format/printf/Formatter share one conversion engine — width vs. precision mean different things per conversion, the comma/+/space/( flags cover most real formatting needs, and n$/< argument indexing lets one argument be reused across specifiers without repeating it.
Scanner tokenizes input from System.in, a String, or a file via hasNextX()/nextX() pairs, with one classic trap: nextInt() leaves the trailing newline unconsumed, so a following nextLine() reads an empty string instead of the next real line.
System.getenv() (OS environment, inherited by child processes), System.getProperty() (JVM-scoped, set via -D), and Runtime (live facts like availableProcessors() and Runtime.version()) are three distinct channels for asking a JVM what it's running on and how it was configured.
The JDK's built-in java.util.logging is rarely called directly; real code logs through the SLF4J facade against a swappable backend (usually Logback), using {} placeholders and MDC for structured, correlatable output.
jlink links JDK and application modules into a self-contained runtime image containing only what's reachable, with jdeps discovering the real module list first — shipping a stripped-down Java runtime instead of a full JDK.
GraalVM bundles a JIT (the Graal compiler) plus native-image, an ahead-of-time compiler that turns a Java app into a self-contained native executable — trading a closed-world assumption over reflection, proxies, and resources for near-instant startup and a much smaller footprint.
Beyond inspect-and-invoke reflection: method handles with lookup-time access checks, setAccessible against the module system's InaccessibleObjectException and --add-opens, and three ways to manufacture a class at runtime — a custom ClassLoader, the in-memory JavaCompiler, and the standard java.lang.classfile Class-File API.
Using the incubating jdk.incubator.vector API to express array arithmetic as explicit SIMD lane operations instead of relying on the JIT to auto-vectorise a scalar loop.
How java.math.BigDecimal and BigInteger give arbitrary precision and explicit rounding where double and long silently lose money or overflow.
How Locale identifies a language and region, how ResourceBundle expands one request into a chain of candidate .properties files, and why the JVM default locale is tried before the language-neutral base bundle.
The java.lang.foreign API, standard since JDK 22, lets pure Java call native library functions and manage off-heap memory with arena-scoped lifetimes, replacing JNI's hand-written C glue code.
The JDK still has no JSON support, so JSON work means picking a library and one of three processing models — data binding, the tree model, or streaming — all of which Jackson provides as the de facto standard.
What module-info.java actually declares — requires, requires transitive, exports, opens, uses/provides — and why a non-exported package is invisible to other modules even when every type in it is public. Covers the unnamed module (where most application code still runs, with none of the enforcement), automatic modules as a migration bridge, jlink for minimal runtime images, and the honest trade-offs: forbidden split packages, the tension between opens and strong encapsulation, and how slow real-world adoption has been.
java.time replaced the mutable, thread-unsafe, 0-indexed-month java.util.Date and Calendar with a family of small immutable types that each model exactly one idea — why an Instant has no getYear(), why Duration and Period genuinely cannot be the same type, what "plus one day" means across a DST transition, and why a static final DateTimeFormatter is safe where a shared SimpleDateFormat was a production bug.
Java's typographical rules (package, class, method, field, and type-parameter casing) and grammatical rules (noun phrases for classes, verb phrases for action methods, is/has for booleans) that keep code readable and predictable.
How to write effective Javadoc doc comments — contracts, tags, summary descriptions, and inheritance — for every exported class, method, and field.
How a component scan actually works underneath: a recursive directory walk over compiled .class files, Class.forName + isAnnotationPresent to find marker-annotated classes, and why real frameworks read bytecode metadata via ASM instead of loading every candidate class.
How JShell lets you evaluate expressions, declare variables/methods/classes, and get immediate feedback without a full class/main-method wrapper — the result-variable convention, forward references, essential /commands, and how it differs from single-file source-code launching.
The Comparable<T> natural-ordering contract and its equals-consistency recommendation, AutoCloseable vs. Closeable idempotency rules, StackWalker as the lazy, modern replacement for stack-trace inspection, and ProcessBuilder/Process/ProcessHandle for launching and managing OS processes.
How @Retention/RetentionPolicy control whether an annotation survives past source, the @Target/@Inherited/@Repeatable meta-annotations, reading annotations at runtime via AnnotatedElement, and the built-in @Deprecated/@SafeVarargs/@FunctionalInterface/@Documented annotations.
Why Pattern.compile() and Matcher exist as two separate objects (compile once, match many), the difference between find()/matches()/lookingAt(), and how capturing groups — numbered and named — pull substrings out of a match instead of just testing yes/no.
Explains the buffer-oriented Channel model in java.nio.channels, covering ByteBuffer state, direct vs heap buffers, FileChannel zero-copy and memory-mapped I/O, non-blocking sockets, and scatter/gather reads and writes.
URI is pure syntax, URL additionally knows how to locate and open a resource, and URN names a resource without saying where to find it — and since JDK 20 the JDK steers construction through URI.toURL() rather than URL's now-deprecated constructors.
Splitting a ServerSocket's accept loop from its per-client conversation so a server actually serves clients concurrently, and using Unix domain sockets (JEP 380, JDK 16) as a faster, filesystem-permissioned, strictly local alternative to TCP loopback.
The JDK ships an officially supported HTTP server in the jdk.httpserver module — HttpServer, HttpExchange, HttpsServer with an SSLContext for TLS, and SimpleFileServer/jwebserver — giving a zero-dependency HTTP endpoint that the docs scope explicitly to testing, development, and debugging rather than production.
How session state is faked on top of stateless HTTP: a random session ID, a Set-Cookie/Cookie round trip, and a server-side map keyed by that ID — plus what's missing from the minimal version (expiration, invalidation, externalized storage) and current cookie security attributes (Secure, SameSite).
How ServerSocket/Socket implement the accept-connect-read-write primitive under every web server, and the literal byte-level shape of an HTTP/1.1 request/response (request line, headers, blank-line terminator, Content-Length/chunked bodies, status code classes).
The byte-stream vs. character-stream split (InputStream/OutputStream vs. Reader/Writer), how Closeable/AutoCloseable/Flushable and try-with-resources guarantee resource cleanup with suppressed exceptions, buffering and PrintWriter, and how Serializable/ObjectInputFilter turn an object graph into bytes while guarding against deserialization attacks.
How java.nio.file replaces java.io.File with informative, specific exceptions instead of boolean returns, adds DirectoryStream for closeable/filterable directory iteration, first-class symbolic-link support, and WatchService for reacting to filesystem changes instead of polling.
How HttpClient's builder pattern replaced HttpURLConnection's low-level, awkward API — synchronous send() vs. asynchronous sendAsync() returning a CompletableFuture, and how BodyHandlers decides what shape the response body arrives in.