
Learn to clone the two course repos—advanced java programming and spring boot—and import them into IntelliJ as a project from existing sources with language level set to 21 preview.
Clone the spring boot cars repo and import as a maven project in IntelliJ, run on port 8080, and test with postman by creating a car.
Introduce lambda expressions and the single abstract method rule, map functional interfaces to lambdas, and explore predicates, BiPredicate, consumer, function, unary and binary operators, and method references.
Explore lambdas through a custom generic functional interface, implementing an isNegative predicate that returns boolean and demonstrates the arrow syntax and delayed execution.
Explore how the predicate functional interface uses its test method with lambdas to evaluate booleans for integers and strings, including a generic check method.
Explore predicate and BiPredicate functional interfaces from the java.util.function package, and see how their test methods use concise lambdas to return boolean results like string contains city or length comparison.
Explore the Java supplier functional interface, which supplies values with no input using the get method. See generics, lambda execution, and examples with string builders, local time, and random doubles.
Explore the consumer and bi-consumer functional interfaces, using accept to process inputs, iterate lists, and populate maps, while noting void returns and key-value outputs like capitals.
Explore how the function interface uses two generics and an apply method to map input to output, with two inputs, lengths, and concatenation in BiFunction.
Explore unary and binary operators as functional interfaces in Java: unary operators require matching input and output types, while binary operators use two inputs of the same type.
Understand how final and effectively final constrain local variables used by a lambda; captured variables must be final or effectively final, preserving a snapshot of their value.
Discover how method references can replace simple lambdas to make code more concise, using bound, static, constructor, and runtime-bound forms, with forEach and consumer interfaces.
Explore bound method references in Java with a string, using a supplier and a predicate to convert to lowercase and check starts with, illustrating binding to instance and lambda simplification.
Discover how unbound method references replace lambdas in Java, converting strings to uppercase and combining two strings with a two-argument function, while understanding how the first parameter supplies the instance.
Explore static method references in Java by using collections sort with a consumer. See compiler inference and compare lambdas and method references on an integer list.
Explore constructor references in Java using suppliers and functions, compare lambda and method reference versions, and see how the new keyword creates objects like string builder and ArrayList.
Explore how method references rely on surrounding context to map to supplier, function, or bi-function, revealing how the compiler infers the abstract method and input-output behavior.
Explore streams and stream pipelines in Java, from source to intermediate and terminal operations. See how filter, peek, and count enable efficient, concise data processing with lambdas.
Explore stream laziness and lazy evaluation, where terminal operations trigger pipelines and process only what is needed with filter, map, anyMatch, and limit.
Learn how to create streams from arrays, collections, maps, and files, use terminal operations like count and forEach, and generate infinite streams with Stream.generate and Stream.iterate, including limits.
Explore terminal operations in streams, distinguishing reductions such as reduce and collect from non reductions like find any, find first, and matches, with optional handling and short-circuiting.
Explore the reduced terminal operation in Java streams, including identity, accumulator, and optional results for an empty stream, and learn how parallel streams use a combiner.
Learn how collect performs mutable reductions using a string builder to accumulate stream elements into maps, lists, and sets. Control the process with supplier, accumulator, and combiner for parallel collection.
See how to use collectors.toMap in streams to build maps from strings to their lengths, handle duplicate keys with a merge function, and choose hash or tree maps.
Use collectors.groupingBy to group elements by a key function, producing a map of lists. Apply downstream collectors like toSet or a tree map to shape values and keys.
Explore terminal operation partitioning by using collect with a boolean key to create true and false groups, applying predicate-based splits and downstream collectors like list or set.
Explore intermediate stream operations such as filter, distinct, and limit, and how they produce streams. See how predicates and stateful distinct enable lazy evaluation and short-circuiting.
Explore intermediate stream operations in Java: map transforms data, flatMap flattens nested streams, and sorted orders elements (with comparators, filters, and limits) to run a pipeline.
Explore primitive streams in Java, including int, long, and double streams, and distinguish them from object streams, then create them from arrays and map integers to int primitives.
Explore primitive streams and how int, long, and double streams return optional for max, min, and average, and how to obtain summarizing statistics like count and sum.
Explore primitive streams with their double, int, and long functional interfaces, including suppliers, consumers, predicates, functions, and unary and binary operators, featuring apply and test methods.
Discover how primitive streams map from one stream to another using map and map to object, double, long, or int, focusing on function signatures.
Showcases mapping object streams to primitive streams using map to double, int, and long, converting strings to uppercase and applying the function via apply, with forEach as the terminal action.
Map a primitive int stream to various stream types using mapToObj, mapToDouble, and mapToLong, illustrating functional mapping with apply and for each on numbers 1, 2, and 3.
Explore how to use Java Optional to handle absent values, including primitive optionals, isPresent, ifPresent, get, orElse, and safe patterns for averaging arrays.
Explore parallel streams in Java to process stream elements concurrently, by splitting into sub streams, and compare with sequential streams to understand how ordering affects map and sum results.
Explore java collections, including the collection interface, set, list, and queue, plus maps such as hash map and tree map; learn common operations like add, remove, size, and forEach.
Learn common collection methods in Java using a string ArrayList: add duplicates, remove first or predicate-based elements, iterate with for-each, clear, and use asList to create immutable lists.
Explore the list, an ordered zero-based collection that allows duplicates, with array list offering fast iteration and access, and linked list for easy insertion and deletion.
Explore how arrays and lists relate in Java, including Arrays.asList backing, then manipulate ArrayList, Stack, and LinkedList with additions, removals, and transformations.
Explore the set interface and its implementations: hash set for no duplicates, linked hash set for insertion order, and tree set for natural or comparator-based sorting.
Explore how sets work in code, comparing tree sets, hash sets, and linked hash sets. See how duplicates vanish, ordering varies, and how hashCode and equals affect retrieval.
Explore maps in Java, including hash map, linked hash map, tree map, and hash table. Learn key concepts, methods, and how order and nulls are handled.
Learn how maps work with a treemap sorted by keys, perform put, get, containsKey, and iterate over key sets, using replace and remove with null-value handling and name-length based values.
Explore queue and deque concepts, including fifo ordering, natural or comparator-based priorities, and stack-like behavior, with linked list, priority queue, and array deque implementations, including offer and poll.
Examine queue and deque implementations in code, using a linked-list queue and an array-backed deck to illustrate offer, add, remove, pull, and peak semantics and their exception behaviors.
Discover how priority queues implement the queue interface to order elements by natural order or custom comparators, demonstrated with strings, numbers, and a Book class sorted by title or price.
Learn how to safely modify collections while iterating, using list iterators, remove operations, copy-on-write wrappers, and streams to avoid concurrent modification exceptions across lists, sets, and maps.
Sort collections and arrays using comparable and comparator interfaces with utility classes. Use comparable for natural ordering and comparator for alternate orders, and perform binary search on a sorted list.
Analyze a product class with id, a constructor, a getter for id, toString, hashCode, and equals based on id; implement compareTo for natural ordering and sort with Collections.sort by id.
Sort dogs by name and by age using comparable and comparator, sorting arrays and lists with Arrays.sort and Collections.sort, including method references and reverse order.
Learn to sort cats without natural order by using comparator.comparing to sort by name, then break ties with age via a method reference.
Explore how to resolve TreeSet comparison issues in Java by using custom Comparator, lambda expressions, and a key extractor to sort workers by ID and avoid class cast exceptions.
Discover binary search on sorted collections, using natural order for strings and a comparator for custom objects. See how sorting, compareTo, and equals ensure correct positions and results.
Learn how java generics enforce type safety at compile time by replacing raw collections with typed containers and avoiding class cast exceptions, while exploring type erasure and the diamond operator.
Explore how generics enforce type safety in Java through polymorphism limits, and master wildcards with extends, super, and unbounded wildcards to safely use lists.
Explore generics and type erasure, and learn how extends provides a read-only upper bound while super offers a modifiable lower bound for safe operations.
Learn to declare custom generic classes and interfaces with formal type parameters in angled brackets, use the diamond operator and type inference, and apply generics to movable interfaces and classes.
Explore generic methods and type parameters, declaring markers like T, U, and V before the return type. Learn to declare, pass, and return multiple types in methods.
Override equals and hashCode to enable meaningful, content-based comparisons in collections like hash maps. Use the equivalence operator for identical references and implement safe equals with instanceof checks.
Override hashCode whenever you override equals, because equal objects must have the same hash code in hash-based collections, using the same instance variables in hashCode as in equals.
Explore how hashCode and equals determine bucket placement in a hash map, and how safe downcasting with instanceof and proper key comparison enable correct retrieval and updates.
Explore how mutating fields used in hash code and equals can break map key lookups, illustrating the hash code contract with a dog whose name change alters its hash code.
Explore concurrency by comparing parallel processing with multitasking, and examine worker threads, executor service, and locking. Define processes, threads, and tasks, and identify non deterministic execution and data race risks.
Create threads by extending the thread class, implementing runnable (run method), or using a lambda, and manage start, current thread, and naming under non deterministic scheduling.
Learn how to use sleep to pause a countdown thread and join to wait for its completion, ensuring the final boom prints after the countdown finishes.
Explore the executor service interface and its thread pools—single, cached, fixed, and single-thread—compare runnable and callable, scheduling tasks, and learn to submit with a future for asynchronous results.
Create a one-thread ExecutorService with a fixed thread pool and execute a task asynchronously; submit a callable to obtain a Future and get with a 500 ms timeout before shutdown.
Demonstrates submitting tasks in a collection of callables using an ArrayList, and compares invoke any and invoke all with a single thread executor and a four-thread fixed thread pool.
Explore scheduled executor service patterns by using schedule, scheduleAtFixedRate, and scheduleWithFixedDelay with a single thread or thread pool, observe futures, delays, and thread reuse.
Explore thread safety by diagnosing a race condition in a database with four threads, then compare atomic classes, the synchronized keyword, and the LOC interface, with a code-based demonstration.
Examine a data race in Java as ten threads increment a non-atomic counter, causing data corruption when threads interrupt each other and miss increments.
Explore atomic classes in the API, focusing on atomic integer, long, and boolean to perform indivisible operations on primitives, with methods like get, set, compareAndSet, and pre/post increments and decrements.
Explore using an atomic integer with an executor service to increment ten times with five threads, avoiding duplicates, though order remains non deterministic, and observe the ten digits across runs.
Use the synchronized keyword to guard critical sections by acquiring an object's lock. Instance methods lock on this, while static methods lock on the class literal, ensuring mutual exclusion.
Explore how the synchronized keyword enforces atomicity across static and instance methods, on class objects, and with manual locks and an intrinsic monitor, ensuring thread safety and ordered output.
Explore how the lock interface provides a flexible alternative to synchronized, with blocking lock and non-blocking tryLock, explicit locking/unlocking, and re-entrant mutual exclusion to solve data races.
Fix the database with the lock interface instead of the synchronized keyword, and demonstrate a reentrant lock controlling a shared counter via a critical section with try lock and unlock.
Explore concurrent collections and concurrent collections API, including skiplist and copy and write collections, blocking queues, and synchronized collections, and observe how a concurrent hash map prevents concurrent modification exceptions.
This diagram shows concurrency collections in Java, such as blocking queue, concurrent linked queue, copy on write array list, and concurrent skip list set and map via concurrent map interface.
Shows concurrent skip list set and map, sorted by natural order, as the concurrent equivalents of tree set and tree map, with country and name examples.
Explore copy on write collections, including copy on write array list and copy on write array set, with snapshot style iteration and optimization for many reads and few writes.
Understand blocking queues and the linked blocking queue, using timeouts in milliseconds, seconds, or minutes for offer and pull, and see head removal and tail insertion in a timed example.
Explore synchronized collections versus concurrent collections, using the collections utility to create synchronized versions of non-concurrent maps and lists and avoid concurrent modification exceptions in multithreaded code.
Explore threading problems such as race condition, deadlocks, liveliness, and starvation, using a bank withdrawal example to show how synchronization and locks prevent overdrawn accounts.
Demonstrates how deadlock occurs when two threads hold locks and wait for each other, and shows preventing it by acquiring the locks in a consistent order.
Explore concurrency issues such as livelock and starvation, where busy threads fail to progress like deadlock, and learn how detection algorithms address these conditions.
Explore localization in Java by using locale objects, local resource bundles, and Java APIs to parse and format messages, dates, and numbers, and create locales via constructors, constants, or builders.
Localize numbers and currencies across locales using number and currency format instances. Format numbers and parse strings with locale-aware methods, including the present instance for percentages.
Demonstrate number and currency formatting across locales using number format and currency instance methods; compare English Ireland, US, Italy, France, and UK formats and parse with the Paris method.
Learn to localise numbers with custom formats using the decimal format class. Use # to omit missing digits and 0 to pad zeros, shown with 77,000.17 and euro output.
localizing dates and times varies by locale; use locale-aware formats and style options (short, medium, long, full) to format dates and times, customizing by passing a locale.
Learn how Locale.Category enums separate display from formatting in Java, enabling you to set a default locale for language and country display. Format numbers, currencies, and dates using locale-specific rules.
Explore resource bundles and property files, using getBundle to load locale-specific data (keys and values) and understand the search order across German Germany, German, English Ireland, and default bundles.
Explore how resource bundles are searched, and how a hierarchy locks in for property files. Understand the exact search order and how locale and package name influence key lookups.
Explore JDBC basics for accessing relational data with Java and SQL, using create, read, update, delete operations on a sample Dharavi banking table to illustrate inserts, selects, and updates.
Explore a banking example setup in NetBeans with Derby, an app schema, bank table, including a primary key (branch code + account number) and creating, dropping, and inserting records.
Learn how to connect to a database using JDBC, building a JDBC URL with protocol, sub protocol, and host details, and choosing driver manager vs data source for connections.
Learn how prepared statements, a sub interface of statement, are created from a connection to execute select, insert, update, or delete commands, returning result sets or update counts.
Map bank account fields in a Java bean to a database row, then retrieve a specific record by branch code and account number using a prepared statement with bind variables.
Retrieve all bank accounts by executing a prepared statement select, iterate the result set to create bank account objects, and return them in an array list.
Learn how to delete a single bank account or all accounts using a prepared statement and execute update in Java, binding branch code and account number.
Learn to insert a bank account record using a prepared statement, bind variables and setString, setDouble, setBoolean, setInt, setLong, then execute update to persist the new record.
Update a bank account using a prepared statement with a composite key (branch code and account number) to change the customer name and address.
Discover how to use callable statements to call stored procedures, including in, out, and in-out parameters, and execute them with read addresses and set string bindings, comparing with prepared statements.
Master resource leaks with try-with-resources, which automatically closes database resources in reverse order of initialization (result set, prepared statement, then connection), ensuring proper resource cleanup.
Explore java.io fundamentals and nio.2 in java, learning to read and write text and binary data with streams, readers, writers, and buffering, including console interactions and password handling.
Explore nio.2 in java 8–25 and its role in spring boot 3 applications, presented in simple terms.
Explore local variable type inference (LVTI) in Java with var, inferring types for locals initialized on declaration, noting it applies inside blocks and not for class fields.
Explore private interface methods introduced in Java 9, including static and non static access, to reduce duplication and improve encapsulation by hiding implementation details within interfaces.
This course has been selected by Udemy for inclusion in Udemy Business — Udemy’s curated collection of high-quality courses used by organisations worldwide.
“Udemy Business is a curated (carefully chosen) selection of high-quality Udemy courses — like yours.” — Udemy
“Only 3% of all courses are chosen for Udemy Business.” — Udemy
The course is fully updated to Java 25 and covers Java 8, 11, 17, 21, and 25 — showing how modern Java evolved across these releases.
Since being selected for Udemy Business, the course has been significantly expanded and updated, including:
• Spring Boot 3
• Java 21 and Java 25 content
• 2 Full OCP Certification Practice Tests
• 5 Additional OCP Practice Quizzes
• All code now available on my GitHub repositories
Who this course is for (important)
This is an advanced Java course.
It is designed for:
Developers who already know Java fundamentals
Learners who want to properly understand modern Java (8 → 25)
Those preparing for Oracle OCP certifications
Professionals who want deep understanding
Note: Java fundamentals are not covered (classes, interfaces, operators, basic syntax).
The course starts immediately with Java 8 lambdas and streams.
What makes this course different
Many Java courses:
Stop at Java 8 or Java 11
Treat newer versions as bolt-ons
Focus on syntax rather than reasoning
You will:
Learn how Java evolved from Java 8 through Java 11, 17, 21, and 25
Understand why language features exist, not just how to use them
Work through assignments that evolve across Java versions
Learn with both real-world development and certification in mind
Topics are explained using:
Clear theory
Extensive, annotated code examples
Assignments that reinforce understanding
Video explanations for all practice questions
What students say
(A small selection — more reviews are available below)
“Great course, great explanation. The best course in Java I have seen so far.” — Kaylan AD
“I am nearing retirement. I have learned more about lambdas here than in the rest of my career.” — Robert R
“This course is a jewel… I passed the OCA and hope to pass the OCP now.” — Yigit K.
“Definitely the best Java course I’ve ever taken… especially the Java 21 features.” — Anna
“Excellent detail, perfect pace, and outstanding explanations.” — Ricardo Q.
"So far its the best course for java professional who want to start from new features of java 8" — Ankit M.
Course overview
Java currently has five Long-Term Support (LTS) releases:
Java 8, 11, 17, 21, and 25 — and this course covers all of them.
This course is the successor to my other Udemy Business course:
“Java 8 OCA (1Z0-808) Certification – Master the Fundamentals”.
As a result:
Java fundamentals are assumed
The course begins with advanced Java 8 topics
Each later Java version builds on the previous one
Java 8 – Advanced Topics
Lambda Expressions
Streams
Generics
Collections
Concurrency
JDBC
File I/O and NIO.2
Serialisation
Localisation
Assignments are included for lambdas, streams, and collections.
Java 11
Modules (examined in detail with examples)
Local Variable Type Inference (LVTI)
Private interface methods
Annotations
Security
Java 17
Sealed classes
Records
Switch expressions and pattern matching
Text blocks
A significant assignment covering all Java 17 topics is included.
Java 21
Unnamed classes and instance main methods (preview features)
Record patterns
Pattern matching for switch
Sequenced collections
Virtual Threads
A Java 21-specific assignment (building on the Java 17 assignment) is included.
Java 25
Markdown documentation
Unnamed variables and unnamed patterns
Module import declarations
Compact source files and instance main methods (finalised)
Flexible constructor bodies
Scoped values
Stream gatherers
A Java 25-specific assignment (building on the Java 21 assignment) is included.
Spring Boot 3 Application (updated to Java 25)
Configuring a Spring Boot application and dependencies
Understanding how RESTful applications work
Dependency Injection / Inversion of Control
Architecture overview
Code explained in detail
Testing with Postman
An assignment is used to reinforce the material, and its solution is explained in detail.
OCP Certification Preparation
2 Full OCP Practice Tests
50 questions each
120-minute time limit
68% pass mark (as per Oracle exams)
Every question has a detailed video explanation
Even if you do not plan to take the certification, the solution videos provide excellent learning opportunities.
5 OCP Practice Quizzes
69 MCQ questions in total
Every question explained with a video solution
This course supports preparation for:
Java 8 OCP (1Z0-809)
Java 11 OCP (1Z0-819)
Java 17 OCP (1Z0-829)
It also works very well alongside the Enthuware OCP Certification tool.
About the instructor
I am a PhD-qualified University lecturer and have been teaching since 2002.
For over 13 years, I have taught advanced Java on a bespoke Masters programme on behalf of a highly regarded software company.
I have co-authored two books:
Learn Java with Projects
Java Memory Management
I love teaching, and this course brings together:
My academic background
My industry experience
My passion for clear, structured explanations
A strong attention to detail, particularly suited to Oracle Java certifications
Final note
This course is designed to give you:
A deep understanding of modern Java
Confidence in real-world development
Strong preparation for Oracle OCP exams
If you already know Java fundamentals and want to truly master Java 8 through Java 25, this course is for you.
More student feedback
“Extraordinary Explanation.” — Darshan P.
“The course is amazing, learned a lot of good things.” — Rakesh L.
“This course provides an exceptional hands-on learning experience… invaluable resource for Java developers.” — Naveen KG
“Sean is technically superior in Java; he’s the best I know.” — Axel B.
“This course is a jewel… Sean answers questions… I passed the OCA… hope to pass the OCP now.” — Yigit K.
“Easy to follow and straight to the point.” — Daniel M.
“Great how detailed it is.” — Ali A.
“Excellent – especially like the detail and pace.” — Alan C.
“One of the best courses so far… perfect talking speed… content is excellent.” — Ricardo Q.
“Very useful exercises and really interesting syllabus.” — Laura
“Clearly explained, easy to follow and understand. Good code examples.” — Ugyen N.
“Fantastic methodology and organization.“ — Eliabe S.