warming up your workspace

Object-Oriented Java

Learn Java by building the machinery everyone else imports: a growable ArrayList and a HashMap from raw arrays, the equals/hashCode contract, inheritance and dynamic dispatch done right, generics with bounded wildcards, the classic design patterns built against real scenarios, a hand-rolled Streams pipeline, and an order-processing engine capstone. The language of a million backends, from first principles.

10 projects, 250 hands-on levels, run in your browser.

Syllabus

  • Java Foundations: Java's reputation for verbosity hides a small, strict core: a handful of primitive types with exact rules, objects for everything else, and a compiler that refuses to guess. This project builds fluency with that core, integer arithmetic and its truncations, strings and their immutability, arrays, control flow, and the static methods that organize it all, the ground every later project stands on.
  • Classes & Objects: A class is a contract: private state, public behavior, and invariants the outside world cannot break. This project builds the discipline, constructors that establish validity, setters that defend it, the equals/hashCode pact that collections depend on, toString for humans, and composition, objects built FROM objects, which carries more weight in real Java than inheritance ever does.
  • Inheritance & Polymorphism: One variable, many shapes: a reference typed Animal can hold a Dog, and the method that runs is the DOG's, decided at runtime. This project builds Java's dispatch machinery, extends and super, overriding, abstract classes that define the skeleton, interfaces that define the capability, and the instanceof checks that make casting safe instead of reckless.
  • Collections from Scratch: ArrayList, LinkedList, HashMap, TreeMap: the four structures under almost every Java program, built here from raw arrays and nodes so their costs stop being folklore. Amortized growth, pointer surgery, hash buckets and rehashing, binary search trees, and then the java.util originals, which you will never again use blindly.
  • Generics: Before generics, every container held Object and every read was a cast waiting to explode. Type parameters moved those errors to compile time. This project builds the machinery: generic classes and methods, bounds that unlock comparisons, the wildcard rules summarized as PECS, and a type-safe rebuild of the containers from Project 4.
  • Exceptions & Contracts: An exception is a contract violation with a stack trace: the method that cannot keep its promise says so loudly instead of returning garbage. This project builds the discipline, throwing the right type, catching at the right level, finally and try-with-resources for cleanup that always happens, and custom exception classes that turn failures into information.
  • Design Patterns I: Creational & Structural: Patterns are not architecture astrology: each one is a recurring solution to a recurring pain. Telescoping constructors hurt, so Builder. `new` hard-wires classes together, so Factory. Feature combinations explode, so Decorator. Interfaces disagree, so Adapter. Trees of parts need uniform treatment, so Composite. Built here against real scenarios, each pain first, then its cure.
  • Design Patterns II: Behavioral: Structural patterns arrange objects; behavioral patterns arrange DECISIONS. Strategy swaps the algorithm at runtime, Observer broadcasts changes to whoever subscribed, Command turns actions into objects you can queue and undo, State makes mode-dependent behavior explicit, and Template Method locks the skeleton while subclasses fill the steps. The machinery under every framework you will ever use.
  • Functional Java: Java grew functions as values: lambdas implementing one-method interfaces, references naming existing methods, and the Streams pipeline that turned loops into declarations. This project builds the muscle, the core functional interfaces, composition, and then the punchline: a mini-Streams engine of your own (map, filter, reduce, collect), so the real java.util.stream never feels like magic again.
  • Capstone: An Order-Processing Engine: Ten projects of machinery, one system: a small e-commerce core with proper value objects, a strategy-driven pricing engine (standard, bulk, member tiers), an event bus notifying observers of every state change, a command-based workflow with an undo history, and a facade that ties it together. The kind of object design that runs real businesses, at a size you can hold in your head.

Key concepts

  • Abstract class: A class that declares WHAT without saying HOW: abstract methods force subclasses to implement them, while concrete methods and fields provide shared machinery.…
  • Adapter: A class that implements YOUR interface and delegates to a foreign one, translating between mismatched APIs. Translation, not surgery, lets two incompatible lib…
  • Aliasing: Two variables referring to one object: int[] b = a does not copy, both names point at the same array, and a write through one shows through the other. A real c…
  • Amortized cost: The average cost per operation over a sequence, even when individual ones are expensive. Doubling-array growth copies occasionally, but the per-append average…
  • Autoboxing: Automatic conversion between a primitive and its wrapper (int to Integer) so primitives ride inside generic containers. Beware the Integer cache: small boxed v…
  • AutoCloseable: The interface (one method, close()) that makes an object usable in try-with-resources. Implement it for files, sockets, sessions, anything that must be release…
  • Binary search tree: Left smaller, right bigger: search, insert, and delete halve the problem at each step, O(log n) when balanced. An in-order walk emits sorted output; sorted inp…
  • Bounded type parameter: <T extends Comparable<T>> constrains T so generic code may call the bound's methods (compareTo). Bounds trade generality for capability, exactl…
  • Builder: Chained, named setters ending in build(), curing the telescoping-constructor problem and validating the whole configuration once. Lets the product stay immutab…
  • Cast: Explicitly converting a value's type. A widening cast (int to long) is safe; a narrowing cast ((int) 3.9 is 3) truncates and can lose data, which is why Ja…
  • Checked vs unchecked: Checked exceptions (extends Exception) must be caught or declared, the compiler enforces handling. Unchecked exceptions (extends RuntimeException) signal progr…
  • Collector: A recipe for accumulating a stream into a result: toList, joining, groupingBy with counting. The terminal operation that turns a pipeline back into a concrete…
  • Command: Turning an action into an object you can queue, log, and undo. Add undo() and a history stack, and you have the machinery behind every undo button and task que…
  • Comparable: Implementing Comparable gives a class a natural ordering via compareTo, which returns negative, zero, or positive. Collections.sort and TreeMap use it.
  • Comparator: An external ordering strategy passed to sort: compare two elements by ANY rule, length, then alphabetical, whatever. The library's most-used strategy objec…
  • Composite: Treating a GROUP of objects like a SINGLE one: a Bundle implements the same interface as an Item and sums its children, so bundles nest inside bundles for free…
  • Composition: Building objects FROM other objects (a Car HAS an Engine) and delegating to them. Composition is more flexible than inheritance and is the right default for mo…
  • Constructor: The special method run by new to initialize a fresh object. It must leave the object in a VALID state, validating arguments and establishing invariants before…
  • Custom exception: A domain-specific exception type (InsufficientFundsException with the missing amount) that turns a failure into structured information. Family hierarchies let…
  • Decorator: A wrapper that IS the interface and HOLDS the interface, adding behavior by delegating to the inner object. Stacking wrappers gives combinatorial features with…
  • Default method: An interface method with a body, built on the interface's abstract methods. It lets an interface evolve without breaking every implementor, and provides re…
  • Defensive copy: Copying a mutable object on the way in (constructor) and on the way out (getter) so callers cannot reach in and mutate your private state. Without it, returnin…
  • Delegation: An object forwarding a call to a held collaborator (Car.start() calls engine.start()). The mechanism behind composition, decorators, and adapters.
  • Design pattern: A named, reusable solution to a recurring design problem. Patterns are not architecture for its own sake, each one cures a specific pain you can point at.
  • Downcasting: Recovering a specific type from a general reference ((Dog) animal). Safe only behind an instanceof check; otherwise it throws ClassCastException at runtime.
  • Dynamic array (ArrayList): A growable array that keeps capacity ahead of size and DOUBLES when full, giving amortized O(1) appends. Indexing is O(1); mid-list insert and remove are O(n)…
  • Dynamic dispatch: The single most important rule: the variable's TYPE decides what you may CALL, but the object's RUNTIME type decides which override RUNS. Fields and st…
  • Encapsulation: Hiding state behind a controlled interface: private fields plus validating methods mean an object's invariants cannot be broken from outside. The core prom…
  • equals vs ==: == asks 'are these the SAME object?'; equals() asks 'do these have the SAME content?'. For value comparison always use equals(), == on objects…
  • Exception: A contract violation with a stack trace: a method that cannot keep its promise throws instead of returning garbage. Caught and handled, or it unwinds the stack…
  • Facade: One simple class hiding a tangle of collaborators behind a clean API: the OrderEngine wires catalog, pricing, events, and workflow so callers see one front doo…
  • Factory Method: Returning an implementation behind an interface based on a description, instead of hard-wiring new ConcreteClass() everywhere. A registry of suppliers lets new…
  • Functional interface: An interface with exactly one abstract method, so a lambda can implement it. The core four: Function (maps), Predicate (tests), Supplier (produces), Consumer (…
  • Generics: Type parameters (Box , Map<K, V>) that move type errors from runtime to compile time and eliminate casts. One class, every payload type, checked by the c…
  • Hash collision: When two distinct keys map to the same bucket. The chain keeps them apart as long as comparison uses equals; a bad hashCode that collides everything degrades t…
  • Hash map: An array of buckets indexed by a key's hash code: O(1) average lookup. Collisions chain within a bucket (compared by equals), and the table rehashes, doubl…
  • Immutable object: An object whose state cannot change after construction: final fields, no setters, defensive copies. Immutable objects are automatically thread-safe and freely…
  • In-order traversal: Visiting a binary tree left-subtree, node, right-subtree. On a BST this yields the values in sorted order, free sorting from the structure.
  • Inheritance: A subclass extends a superclass, inheriting its non-private members and optionally overriding them. Powerful but tight coupling, prefer composition unless ther…
  • Instance member: A field or method that belongs to each OBJECT: every instance carries its own copy of instance fields, and instance methods operate on this . The default, and…
  • instanceof: Tests whether an object is (a subtype of) a given type before casting. Modern Java fuses the check and cast into a pattern variable: if (a instanceof Dog d) bi…
  • Integer division: Dividing two ints discards the remainder and truncates toward zero: 7 / 2 is 3, not 3.5. The remainder lives in the % operator. A frequent source of off-by-fra…
  • Integer overflow: When an int exceeds 2147483647 it silently wraps to a negative value. The fix is to widen to long BEFORE the arithmetic, since the inputs, not the result, must…
  • Interface: A pure capability contract: 'any class that does this'. A class may implement many interfaces (where it can extend only one class), and default methods…
  • Invariant: A condition an object guarantees at all times (a balance never goes negative, a temperature stays in range). Setters that reject bad input are how invariants a…
  • Iterable: Implement Iterable and the for-each loop works on YOUR class: iterator() returns a fresh Iterator each time. The bridge between a custom collection and the lan…
  • Iterator: hasNext()/next() as a contract for stepping through a sequence. Iterators can wrap iterators (filtering), and need no backing storage (lazy infinite sequences).
  • Lambda: An inline implementation of a one-method (functional) interface: x -> x * 2 IS a Function. Lambdas turned strategies, listeners, and callbacks from classes…
  • Lazy evaluation: Deferring expensive work until (or unless) it is needed, by passing a Supplier instead of a value. getOrCompute returns the cache without ever calling the supp…
  • Linked list: Nodes pointing at nodes: O(1) prepend, but O(n) to walk to an index. Insert and delete are cheap once you hold the node, the trade-off against the array's…
  • Load factor: The ratio of entries to buckets that triggers rehashing, classically 0.75. Below it, lookups stay fast; above it, chains lengthen.
  • Memoization: Caching a function's results by argument so repeated calls return instantly. A functional optimization: wrap the function, check the cache, compute once pe…
  • Method overloading: Several methods sharing one name, distinguished by their PARAMETER types. The compiler picks the match at compile time by the static argument types, this is re…
  • Method overriding: A subclass redefining an inherited method with the same signature. @Override asks the compiler to verify the signature matches. Unlike overloading, overriding…
  • Method reference: A shorthand for a lambda that just calls an existing method: String::toUpperCase is s -> s.toUpperCase() . Cleaner than the equivalent lambda when no extra…
  • Node: A small object holding a value and one or more links (next, left, right). Nodes are the building blocks of linked lists and trees; rewiring their links is &#39…
  • null: The absence of a reference. Dereferencing it throws NullPointerException, Java's most common runtime failure. Fail fast by rejecting nulls at the boundary;…
  • Object (the root): Every class extends Object, so any reference fits an Object variable and inherits equals, hashCode, toString, and getClass. The top of the type hierarchy.
  • Observer: Publish/subscribe: a subject broadcasts changes to whoever subscribed, without knowing who they are. The wiring inside every UI toolkit and event bus; listener…
  • Optional: A container that explicitly models 'maybe absent', making the null check unavoidable instead of forgettable. For EXPECTED absence; exceptions are for b…
  • PECS: Producer Extends, Consumer Super: use ? extends T for a source you read from, ? super T for a sink you write to. The mnemonic that makes wildcard signatures (l…
  • Polymorphism: One reference type, many runtime shapes: an Animal variable can hold a Dog or a Cat, and each answers sound() in its own voice. Dispatch is the mechanism that…
  • Primitive type: One of Java's eight built-in value types (int, long, double, boolean, char...). Primitives hold raw values directly, not object references, so they cannot…
  • reduce / fold: Collapsing a sequence into one value by repeatedly combining: acc = combine(acc, element), left to right from a seed. Sum, concatenate, max, all reductions.
  • Reference type: Any non-primitive type: a variable holds a reference to an object on the heap, not the object itself. Two references can point at the same object (aliasing), a…
  • Rehashing: Growing the bucket array (typically doubling) and re-placing every entry at its new index when chains get too long. Keeps the O(1) promise as the map fills.
  • Singleton: One shared instance, enforced by a private constructor and a static accessor. Useful for genuinely single resources, dangerous as hidden global state.
  • State pattern: One class per mode, each knowing its own transitions and behavior, replacing mode-flag if-chains. The order knows whether it can be cancelled; the StatefulOrde…
  • static: A static field or method belongs to the CLASS, not to any instance: one shared copy, callable without an object. Constants, factories, and utility helpers are…
  • Strategy: Injecting the algorithm as an object so it can be swapped at runtime: pricing rules, comparators, policies as plug-ins. Every Comparator is a strategy.
  • Stream: A declarative pipeline over a sequence: filter, map, reduce, collect, replacing explicit loops. Lazy and composable; intermediate operations build the pipeline…
  • String immutability: Strings never change: every 'modification' returns a NEW string and leaves the original untouched. This makes strings safe to share, but concatenating…
  • StringBuilder: A mutable character buffer for building strings efficiently: append in place, call toString() once at the end. The idiomatic fix for loop concatenation.
  • super: References the superclass: super(...) calls the parent constructor (which always runs first), and super.method() invokes the parent's version of an overrid…
  • Template Method: A final method locks the algorithm's skeleton while abstract steps (and optional hooks) let subclasses fill the gaps. Subclasses customize the parts, never…
  • The equals contract: equals() must be reflexive, symmetric, transitive, consistent, and false for null. Override it for value semantics following the recipe: identity shortcut, ins…
  • The hashCode contract: Whenever you override equals you MUST override hashCode, and equal objects must produce equal hash codes. Break it and HashMap silently loses your keys.
  • this: A reference to the current object inside an instance method or constructor. this.field disambiguates a field from a shadowing parameter; this(...) chains to an…
  • toString: Overriding toString() controls how an object prints in logs, debuggers, and string concatenation. A good toString turns 'Object@1a2b' into something hu…
  • TreeMap: A sorted map backed by a balanced binary search tree: keys stay in order, lookups are O(log n), and range queries are efficient. Trades HashMap's O(1) for…
  • try / catch / finally: try guards risky code; catch handles a thrown type (specific before general); finally ALWAYS runs, on return, throw, or fall-through, making it the home of cle…
  • try-with-resources: try (Resource r = ...) auto-closes anything AutoCloseable when the block exits, normally or by exception, in reverse order of opening. The modern replacement f…
  • Type erasure: Generics exist only at compile time, the runtime sees raw types: no new T[] , no instanceof List<String> , and List and List are the SAME class. Casts an…
  • Utility class: A class that is just a bundle of static helpers with no instances (java.lang.Math is one). A private constructor signals that you should call its methods, not…
  • Validation pattern: The boundary rule: result objects and collected-error lists for EXPECTED failures (user input), exceptions for BROKEN contracts (programmer error). Forms repor…
  • Value object: A small immutable object defined by its data, not its identity: two Money objects with the same cents are equal. Proper equals/hashCode/toString make it behave…
  • Varargs: A trailing int... nums parameter lets a method accept any number of arguments; inside, it is simply an array. Convenient, but only one varargs parameter, alway…
  • Wildcard: ? stands for an unknown type: ? extends T for a producer you only read, ? super T for a consumer you only write, ? for read-as-Object. The flexibility generics…