The parts of Java that behave differently from how they read.
Erasure, initialisation order, overload resolution, the memory model. Read the concept, then prove you have it — and run the code yourself when the answer surprises you.
Object lifecycle
- 0001 Construction and initialisation order Creating an instance runs a fixed sequence: field initialisers and instance blocks in source order, then the constructor body — and the superclass finishes all of that before the subclass starts.
- 0002 Garbage collection and finalizers The collector reclaims objects unreachable from any GC root, on no schedule you can rely on. finalize() is deprecated and unusable for resource release; Cleaner or try-with-resources is the answer.
- 0003 Construction patterns Ways to control instantiation: a singleton guarantees one instance, a static factory names and caches construction, and dependency injection hands collaborators in rather than letting an object build them.
Object methods
- 0004 The equals/hashCode contract Equal objects must have equal hash codes. Break this and the object silently misbehaves in every hash-based collection — it goes into a HashMap and never comes back out.
- 0005 clone, copying and defensive copies Object.clone() produces a shallow copy through a broken protocol. A copy constructor or static factory is clearer, and mutable fields crossing an API boundary need copying in both directions.
General guidelines
- 0006 The string constant pool String literals are interned into a JVM-wide pool, so identical literals are the same object. Strings built at runtime are not pooled unless intern() is called explicitly.
- 0016 Boxing, unboxing and the Integer cache Autoboxing silently converts between primitives and wrappers. Values from -128 to 127 come from a shared cache, so == appears to work on small numbers and fails on large ones.
Class design
- 0007 Functional interfaces, default and static methods An interface with exactly one abstract method can be implemented by a lambda. Default methods let interfaces gain behaviour without breaking implementers, which is how Java retrofitted streams onto Collection.
- 0008 Immutable classes An object whose observable state cannot change after construction. Immutable objects are automatically thread-safe, freely shareable, and safe as map keys.
- 0009 Inheritance vs composition Inheritance couples a subclass to its parent's implementation details, so a parent change can silently break it. Composition delegates to a held instance and depends only on the published contract.
Generics
- 0010 Type erasure Generic type arguments exist only at compile time. The compiler checks them, then erases them to their bounds and inserts casts, so the bytecode carries no record of the parameterisation.
- 0011 Wildcards and PECS ? extends T gives a producer you can only read from; ? super T gives a consumer you can only write to. Producer Extends, Consumer Super.
Enums and annotations
- 0012 Enums as special classes An enum constant is a singleton instance of a compiler-generated final class. Enums can carry fields, implement interfaces, and give each constant its own method body.
- 0013 Annotation retention and targets @Retention decides whether an annotation survives to the class file and to runtime; @Target restricts where it may be written. Only RUNTIME retention is visible to reflection.
Methods
- 0014 Overloading vs overriding Overload resolution picks a method from the static types at compile time. Override dispatch picks the implementation from the runtime type. Mixing them up produces code that calls something you did not expect.
- 0015 Method references A compact lambda that just calls an existing method. Four forms: static, bound instance, unbound instance, and constructor.
Exceptions
- 0017 Checked vs unchecked exceptions Checked exceptions extend Exception and must be declared or handled; unchecked ones extend RuntimeException and need neither. The distinction is about whether the caller can plausibly recover.
- 0018 try-with-resources and suppressed exceptions Any AutoCloseable declared in the try header is closed automatically, in reverse order, even on exception — and a failure during close is attached to the primary exception rather than replacing it.
Concurrency
- 0019 Happens-before A partial ordering on memory operations. If action A happens-before action B, everything A wrote is visible to B. Without such an edge, the JVM is free to reorder and a thread may read stale values indefinitely.
- 0020 Executors, futures and thread pools An ExecutorService decouples task submission from the threads that run them. Future is a handle on a pending result; CompletableFuture adds composition without blocking.
- 0021 Atomic variables and compare-and-swap Atomic classes use a hardware compare-and-swap instruction to update a value without locking: read, compute, and swap only if nothing changed in between.
- 0022 Concurrent collections and wait/notify ConcurrentHashMap and the blocking queues provide thread-safe access without a global lock. wait/notify is the low-level primitive underneath, and is almost always the wrong tool now.
Serialization
Reflection
- 0024 The Reflection API Reflection inspects and invokes types discovered at runtime. It powers every framework you use, and it costs you compile-time safety, performance, and refactoring.
- 0025 Method handles A typed, directly executable reference to a method, resolved once through a Lookup that captures the caller's access rights. Faster than reflection and the basis of invokedynamic.
Dynamic languages
Compiler tooling
- 0027 The Java Compiler API javax.tools exposes javac as a library, so a running JVM can compile source it generated or received, then load the result.
- 0028 Annotation processors A compile-time plugin that reads annotated elements and generates new source files. It runs in rounds during javac, and it can add code but never modify existing code.