Java Course
Learn Java from the basics to concurrency: control flow, OOP, exceptions, generics, collections and streams, with real code examples and quizzes.
45 lessons in 8 categories
Java Basics
String
The first topic in the Java Basics category: `String`'s IMMUTABLE design, the string pool and the `==` vs `equals()` trap, the O(n^2) cost of `+` concatenation vs. `StringBuilder`'s amortized O(1) (with a real, warmed-up measurement), `String.format()`/text blocks (Java 15+), and helper methods like split/join/replace/trim/strip.
Arrays
The second topic in the Java Basics category: arrays' fixed-size, contiguous-memory structure, multi-dimensional arrays (including jagged arrays), the `Arrays` utility class (sort/binarySearch/equals/fill/copyOf), array covariance and the `ArrayStoreException` trap, `Arrays.asList()` being a view, and varargs.
Scanner
The third topic in the Java Basics category: `Scanner`'s token-based reading model, the classic `nextInt()` + `nextLine()` trap, custom delimiters via `useDelimiter()`, reading from a `File`, a real performance difference between `Scanner` and `BufferedReader`, and `InputMismatchException`/`NoSuchElementException` exception handling.
Wrapper Classes & Autoboxing
The fourth topic in the Java Basics category: the object counterparts of primitive types (Integer/Double/Boolean, etc.), autoboxing/autounboxing, Integer caching (-128..127) and the `==` trap, why unboxing a null wrapper throws a real NullPointerException, autoboxing's performance cost in tight loops, and how wrapper classes make generic collections possible.
File Reading
The fifth topic in the Java Basics category (the reading half of File I/O): `Path`/`Files` basics, `Files.readAllLines()`/`Files.readString()`, line-by-line reading with `BufferedReader`, `Files.lines()` being lazy and Closeable, searching a file for a word, and the real difference between `NoSuchFileException` and `FileNotFoundException`.
File Writing
The sixth topic in the Java Basics category (the writing half of File I/O): `Files.writeString()`/`Files.write()`, appending with `StandardOpenOption.APPEND`, writing with `BufferedWriter`, copying files with `Files.copy()`, `Files.createDirectories()`, the standard pattern for deleting a directory tree, and writing a CSV file.
Reflection
Accessing and dynamically using class, field, method, and constructor information at runtime in Java.
Date & Time API
The java.time package in Java; LocalDate/LocalTime/LocalDateTime, Instant, ZonedDateTime/OffsetDateTime, Duration/Period/ChronoUnit, formatting/parsing, time zones, and migrating from legacy Date/Calendar.
Exceptions
Introduction to Exceptions
What an exception actually is -- why it exists (the problems with return-value-based error signaling), a short history of Java's checked exception design decision, the message/cause/stack trace an exception object carries, what happens when an exception goes uncaught, reading a stack trace and propagation, and the common runtime exception triggers (NullPointerException, ArrayIndexOutOfBoundsException, NumberFormatException). The first of 7 lessons in this series -- try/catch mechanics come next.
Try-Catch and Finally
The actual mechanism for handling an exception: the basic try-catch block, multiple catch blocks (matching in order, the superclass/subclass ordering restriction), multi-catch with | (Java 7+), finally running unconditionally every time, and how a return/throw inside finally silently overrides whatever try/catch was about to produce. The 2nd lesson in the Exception Handling series.
Exception Hierarchy
The inheritance relationship between Throwable, Error, Exception, and RuntimeException -- how every exception descends from a single shared root, how a catch block can target any ancestor of a type (polymorphic catching), and how to query that hierarchy at runtime with instanceof. The 3rd lesson in the Exception Handling series.
Checked vs Unchecked Exceptions
The difference between a checked exception's compiler-enforced throws/catch contract and an unchecked exception's (the RuntimeException family) complete lack of that requirement -- when to choose which, wrapping a checked exception as unchecked with the cause parameter, and the restriction that an overriding method's throws declaration can only be NARROWED. The 4th lesson in the Exception Handling series.
Throw and Throws
The difference between the throw statement handing a Throwable instance to the JVM at runtime and the throws declaration being a purely compile-time contract -- fail-fast validation, rethrowing a caught exception as a different type with the cause parameter, and a checked exception propagating unhandled through a call chain. The 5th lesson in the Exception Handling series.
Creating and Throwing Custom Exceptions
Defining your own exception type by extending Exception (checked) or RuntimeException (unchecked) -- adding fields that carry extra context, mirroring Throwable's four standard constructors, and building your own small exception hierarchy with a shared custom base class. The 6th lesson in the Exception Handling series.
Exception Handling Best Practices
Practices built on top of the previous six lessons' mechanics -- the anti-patterns of using exceptions for control flow and swallowing them, catching in the right order from specific to broad, and only catching where a genuinely useful response is possible. The 7th and final lesson in the Exception Handling series, closing with a recap of the whole series.
Generics
Introduction to Generics
Why generics exist (the pre-generics raw type/cast/ClassCastException problem), how type safety is enforced at compile time, generic classes and interfaces, and the type parameter naming convention. The 1st lesson in the Generics series.
Generic Methods
Generic methods that declare their own type parameter independent of whether their class is generic -- method type parameters, multiple type parameters, type inference, and explicit type witnesses. The 2nd lesson in the Generics series.
Bounded Type Parameters
Upper bounds (`<T extends ...>`), multiple bounds joined with `&`, bounding with classes and with interfaces, and how bounding a type parameter expands which methods can be called on it. The 3rd lesson in the Generics series.
Wildcards
The unbounded wildcard (`<?>`), the upper bounded wildcard (`<? extends T>`), the lower bounded wildcard (`<? super T>`), the difference between the three, and the PECS (Producer Extends, Consumer Super) rule -- the most commonly misunderstood part of Java generics, walked through step by step with real examples. The 4th lesson in the Generics series.
Generics with Collections
Generic collections like List<T>, Set<T>, and Map<K,V>, type safety with collections, why List<Object> is NOT List<String> (the full treatment of generics invariance), and type inference with the diamond operator and var. The 5th lesson in the Generics series.
Type Erasure and Generic Limitations
What type erasure is, what happens to generic types at runtime, why `new T()` and generic arrays aren't allowed, static members and generics, and how generics' runtime limitations actually surface in practice (raw types, unchecked warnings, heap pollution). The 6th and final lesson in the Generics series.
Control Flow
if / else
The first topic in the new Control Flow category: the basics of `if`/`else`, `else if` chains, nested conditions, the six comparison operators (`==`/`!=`/`<`/`>`/`<=`/`>=`) with decimal number and `String` comparison pitfalls, the `&&`/`||`/`!` logical operators and short-circuit evaluation, and the ternary operator.
switch
The second topic in the Control Flow category: classic `case`/`break` syntax and the fall-through trap, the modern arrow (`->`) syntax, switch EXPRESSIONS and `yield`, multiple labels with enum exhaustiveness checking, and using `switch` with `String`/enum.
for Loop
The third topic in the Control Flow category: the classic three-part (init;condition;update) `for` syntax, exiting early with `break`, skipping a step with `continue`, deliberate/unintentional infinite loops, multiple variables via comma-separated init/update, and classic index-based array iteration.
Enhanced for Loop
The fourth topic in the Control Flow category: the for-each syntax over arrays/collections, its three real limitations (no index access, the loop variable being a copy plus structural changes throwing ConcurrentModificationException, and no parallel iteration of multiple collections), and when to use it versus classic `for`.
while & do-while Loops
The fifth topic in the Control Flow category: basic `while` syntax, `do-while`'s run-at-least-once body, while vs do-while comparison, break/continue, a Scanner-based input validation loop, and a worked example: the Number Guessing Game.
Nested Loops
The sixth and final topic in the Control Flow category: basic nested for loops, 2D array traversal, how unlabeled break/continue only affect the innermost loop, labeled break/continue, and O(n²) performance awareness.
Collections
Lists
The first topic in the Collections category: the `List` interface, the performance difference between `ArrayList` and `LinkedList` shown with a real warmed-up measurement, immutable lists via `List.of()`/`Collections.unmodifiableList()`/`List.copyOf()`, safe iteration with `Iterator`/`ListIterator`, sorting with `List.sort(Comparator)`, and `subList()`/`toArray()`.
Sets
The second topic in the Collections category: the `Set` interface and its no-duplicates guarantee, the differences between `HashSet`/`LinkedHashSet`/`TreeSet`, `NavigableSet` methods, why the `equals()`/`hashCode()` contract is critical for `HashSet` (with a real bug example), set operations (union/intersection/difference), and a real performance measurement comparing List/HashSet/TreeSet.
Maps
The third topic in the Collections category: the `Map<K,V>` interface (each key once, mapped to a value), the differences between `HashMap`/`LinkedHashMap`/`TreeMap`, `NavigableMap` methods, immutable maps (`Map.of()`/`Map.copyOf()`), Java 8's modern API (`getOrDefault()`/`computeIfAbsent()`/`merge()`), and a real performance measurement comparing `entrySet()` and `keySet()+get()`.
Queues & Collections Utility
The final topic in the Collections category: the `Queue`/`Deque` interfaces (offer()/poll()/peek() vs add()/remove()/element()), why `ArrayDeque` is preferred over `LinkedList`/`java.util.Stack` as both a queue and a stack, a real discovery that `PriorityQueue` is heap-based and its toString() is NOT sorted, and the `Collections` utility class's static methods like sort/shuffle/max/binarySearch.
Object-Oriented Programming
Interface
Defining behavior contracts in Java; default/static/private methods and sealed interfaces.
Abstract Class
Abstract classes that share common state and behavior in Java; constructors, the Template Method pattern, and abstract class vs interface.
Inheritance
Inheritance in Java; extends, the constructor chain, method overriding, field hiding, upcasting/downcasting, and composition vs inheritance.
Polymorphism
Polymorphism in Java; method overloading, overload resolution rules, covariant return types, polymorphism vs inheritance, and the Strategy pattern via composition.
Concurrency
Threads
Threads in Java; creating threads (Thread/Runnable), the thread lifecycle, thread methods, race conditions, synchronized, volatile, wait/notify, Atomic classes, ReentrantLock, and deadlock.
Functional Interfaces & Streams
Lambda Expressions
The full syntax of lambda expressions: parameter-writing rules, expression body vs. block body, the compiler inferring a lambda's type from context (target typing), capturing effectively final variables, and how a lambda differs from an anonymous inner class.
Built-in Functional Interfaces
The ready-made interfaces in java.util.function: Predicate<T>, Function<T,R>, Consumer<T>, Supplier<T>, UnaryOperator<T>, BinaryOperator<T>. The four method reference forms: Class::staticMethod, object::instanceMethod, Class::instanceMethod, Class::new.
Stream API Fundamentals
The fundamentals of the Stream API: what a Stream is, moving from a Collection to a Stream, the three stages of a pipeline (source/intermediate/terminal). Intermediate operations: filter(), map(), flatMap(), distinct(), sorted(), peek(), limit(), skip(). Lazy evaluation and a stream's single-use nature.
Terminal Operations
The terminal operations that trigger a stream pipeline and produce a result: forEach(), reduce(), count(), min()/max(), findFirst()/findAny(), anyMatch()/allMatch()/noneMatch(), toList()/toArray(). Short-circuiting behavior and count()'s source-size optimization.
Collectors
The ready-made collect() recipes the Collectors class offers: toList()/toSet(), joining(), groupingBy() (with counting()/mapping() as downstream collectors), partitioningBy(), toMap(). The three parts of a Collector: supplier, accumulator, combiner.
Optional
Optional<T>: expressing the possibility of an absent value in the type system. Creating with of()/ofNullable()/empty(), orElse() vs. orElseGet() (eager vs. lazy), orElseThrow(), map()/flatMap(), ifPresent()/ifPresentOrElse(), filter().
Primitive & Parallel Streams
Avoiding autoboxing cost with IntStream/LongStream/DoubleStream, the boxed()/mapToInt() bridges. Running in parallel with parallelStream(), the forEach() vs. forEachOrdered() ordering difference, a non-thread-safe shared state pitfall, when to use it, and why it is not always faster.