Last updated: October 2026
Java 8 came out in 2014. So why do interviewers in 2026 still ask "Java 8 interview questions"?
Because Java 8 changed how Java is written. Lambdas, streams and Optional are everywhere in modern Spring Boot code, and the newer versions (11, 17, 21) build on them. When an interviewer says "Java 8 questions", they really mean "show me you can write modern, functional-style Java". Almost every Java developer interview in India, from 2 years of experience to 8, includes at least one "solve this with streams" coding question.
This guide covers 40 questions on Java 8 features only. For core concepts like OOP, collections and multithreading basics, see our Java interview questions and core Java interview questions.
Java 8 interview questions: the quick answer
- What gets tested: lambdas, functional interfaces, method references, the Stream API, collectors (groupingBy, partitioningBy, toMap), Optional, default and static interface methods, the java.time API and CompletableFuture.
- Typical format: 4–6 concept questions, then 2–3 stream coding problems on a shared editor ("find the second-highest salary from a list of employees using streams").
- What wins: writing streams that are readable, knowing when not to use them, and explaining lazy evaluation.
- Bonus points: knowing what came after Java 8:
var(10), records (16), pattern matching and virtual threads (21).
We use this simple class in the examples:
record Employee(String name, String dept, double salary, int age) {}
// (In a Java 8 codebase this would be a regular class with getters.)
List<Employee> emps = List.of(
new Employee("Asha", "Engineering", 120000, 29),
new Employee("Ravi", "Engineering", 95000, 34),
new Employee("Meera", "Sales", 70000, 26),
new Employee("Karan", "Sales", 82000, 31),
new Employee("Divya", "HR", 65000, 41)
);
Lambdas and functional interfaces (1–10)
1. What are the main features introduced in Java 8?
Lambda expressions, functional interfaces, method references, the Stream API, Optional, default and static methods in interfaces, the new Date-Time API (java.time), CompletableFuture, the Nashorn JavaScript engine (since removed), and improvements like Collectors, forEach and Map.merge/compute.
2. What is a lambda expression?
A short way to write an anonymous function: parameters, an arrow, and a body. It lets you pass behaviour as data.
// Before Java 8
Comparator<String> c1 = new Comparator<String>() {
public int compare(String a, String b) { return a.length() - b.length(); }
};
// Java 8
Comparator<String> c2 = (a, b) -> a.length() - b.length();
3. What is a functional interface?
An interface with exactly one abstract method, so a lambda can implement it. It can still have default and static methods. @FunctionalInterface is optional but makes the compiler enforce the rule. Examples: Runnable, Comparator, Callable.
4. What are the main built-in functional interfaces in java.util.function?
| Interface | Method | Takes → returns | Example use |
|---|---|---|---|
| Predicate<T> | test | T → boolean | filter |
| Function<T,R> | apply | T → R | map |
| Consumer<T> | accept | T → void | forEach |
| Supplier<T> | get | nothing → T | orElseGet, lazy values |
| BiFunction<T,U,R> | apply | T, U → R | merging two values |
| UnaryOperator<T> | apply | T → T | replaceAll |
| BinaryOperator<T> | apply | T, T → T | reduce |
5. Can a functional interface extend another interface?
Yes, as long as the total number of abstract methods is still one. If it inherits an abstract method and adds none, it's still functional. Methods matching Object's public methods, like equals, don't count.
6. What is "effectively final", and why do lambdas need it?
A local variable that isn't reassigned after it's set. Lambdas can only use local variables that are final or effectively final, because the lambda captures the value and may run later, possibly on another thread. Instance fields don't have this restriction.
7. What is a method reference?
A shorter form of a lambda that just calls an existing method. There are four kinds:
Function<String, Integer> parse = Integer::parseInt; // static method
Consumer<String> print = System.out::println; // method of a specific object
Function<String, String> upper = String::toUpperCase; // method of an arbitrary object
Supplier<List<String>> make = ArrayList::new; // constructor
8. How is a lambda different from an anonymous class?
A lambda has no state or name of its own, and this inside it refers to the enclosing class. An anonymous class creates a new class with its own this, can have fields, and can implement interfaces with several methods. Lambdas are compiled using invokedynamic, not as separate class files.
9. How do you compose functions and predicates?
Predicate<Employee> isEng = e -> e.dept().equals("Engineering");
Predicate<Employee> isSenior = e -> e.age() > 30;
emps.stream().filter(isEng.and(isSenior.negate())); // engineers aged 30 or under
Function<Integer, Integer> doubleIt = x -> x * 2;
Function<Integer, Integer> plusOne = x -> x + 1;
doubleIt.andThen(plusOne).apply(5); // 11
doubleIt.compose(plusOne).apply(5); // 12
10. How do you handle checked exceptions inside lambdas?
Standard functional interfaces don't declare checked exceptions, so you either catch the exception inside the lambda (and wrap it in an unchecked exception), write your own functional interface that throws Exception, or extract a helper method that handles it. Mention that heavy try-catch blocks inside streams hurt readability.
Stream API (11–24)
11. What is a stream?
A sequence of elements that supports functional operations like filter, map and reduce. A stream doesn't store data; it pulls from a source (a collection, array or I/O), can be used only once, and doesn't change its source.
12. What is the difference between a Collection and a Stream?
A collection holds data in memory and you can iterate it many times. A stream computes elements on demand, is lazily evaluated, can be infinite, and is consumed once. Collections are about data; streams are about computation.
13. What are intermediate and terminal operations?
Intermediate operations return a new stream and are lazy: filter, map, flatMap, sorted, distinct, limit, skip, peek. Terminal operations trigger processing and produce a result: collect, forEach, reduce, count, min, max, anyMatch, findFirst.
14. What is lazy evaluation in streams?
Nothing runs until a terminal operation is called. Then elements flow through the pipeline one at a time, and short-circuiting operations like findFirst or limit can stop early without processing the whole source.
Stream.of("a", "bb", "ccc")
.filter(s -> { System.out.println("filter " + s); return s.length() > 1; })
.findFirst();
// prints "filter a", "filter bb" and stops
15. What is the difference between map and flatMap?
map transforms each element into exactly one element. flatMap transforms each element into a stream and flattens all of them into one stream.
List<List<String>> skills = List.of(List.of("Java", "SQL"), List.of("AWS"));
List<String> all = skills.stream().flatMap(List::stream).collect(Collectors.toList());
// [Java, SQL, AWS]
16. What is the difference between findFirst and findAny?
findFirst returns the first element in encounter order. findAny may return any element; in parallel streams it's faster because it doesn't need to respect order. On a sequential stream, both usually return the first element.
17. What does reduce do?
It combines elements into one value using an identity and an accumulator.
int sum = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum); // 10
Optional<Integer> max = Stream.of(4, 9, 2).reduce(Integer::max);
For sums and counts, prefer specialised methods like mapToInt(...).sum(); they're clearer and avoid boxing.
18. What are primitive streams?
IntStream, LongStream and DoubleStream avoid boxing overhead and add methods like sum(), average(), range() and summaryStatistics().
double avg = emps.stream().mapToDouble(Employee::salary).average().orElse(0);
IntStream.rangeClosed(1, 5).forEach(System.out::print); // 12345
19. Group employees by department.
Map<String, List<Employee>> byDept =
emps.stream().collect(Collectors.groupingBy(Employee::dept));
Map<String, Long> countByDept =
emps.stream().collect(Collectors.groupingBy(Employee::dept, Collectors.counting()));
Map<String, Double> avgSalaryByDept =
emps.stream().collect(Collectors.groupingBy(Employee::dept,
Collectors.averagingDouble(Employee::salary)));
20. What is the difference between groupingBy and partitioningBy?
partitioningBy splits into exactly two groups using a predicate, with keys true and false. groupingBy can create any number of groups using a classifier function.
Map<Boolean, List<Employee>> highEarners =
emps.stream().collect(Collectors.partitioningBy(e -> e.salary() > 80000));
21. How do you convert a list to a map, and what about duplicate keys?
Map<String, Double> salaryByName =
emps.stream().collect(Collectors.toMap(Employee::name, Employee::salary));
// With duplicate keys, toMap throws IllegalStateException unless you pass a merge function:
Map<String, Double> maxSalaryByDept =
emps.stream().collect(Collectors.toMap(Employee::dept, Employee::salary, Math::max));
That exception on duplicate keys is a favourite follow-up question.
22. How do you sort with streams, including multiple fields?
List<Employee> sorted = emps.stream()
.sorted(Comparator.comparing(Employee::dept)
.thenComparing(Employee::salary, Comparator.reverseOrder()))
.collect(Collectors.toList());
23. What are parallel streams, and when should you avoid them?
parallelStream() splits the work across the common ForkJoinPool. It can help with large, CPU-heavy, independent operations on easily split sources like arrays and ArrayLists. Avoid it for small data, I/O-bound or blocking tasks, ordered operations, shared mutable state, and inside web servers where the common pool is shared. Always measure before using it.
24. What does peek do, and should you use it?
peek runs an action on each element as it passes, without changing it. It's meant for debugging. Don't rely on it for business logic: it may not run at all if the terminal operation doesn't need the elements, as with count() on a sized stream in newer Java versions.
Stream coding questions (25–32)
These come up again and again. Practise until you can write them without an IDE.
25. Find the second-highest salary using streams.
Optional<Double> second = emps.stream()
.map(Employee::salary)
.distinct()
.sorted(Comparator.reverseOrder())
.skip(1)
.findFirst();
26. Find the highest-paid employee in each department.
Map<String, Optional<Employee>> topByDept = emps.stream()
.collect(Collectors.groupingBy(Employee::dept,
Collectors.maxBy(Comparator.comparingDouble(Employee::salary))));
27. Count the frequency of each character in a string.
Map<Character, Long> freq = "programming".chars()
.mapToObj(c -> (char) c)
.collect(Collectors.groupingBy(Function.identity(),
LinkedHashMap::new, Collectors.counting()));
// {p=1, r=2, o=1, g=2, a=1, m=2, i=1, n=1}
28. Find the first non-repeated character in a string.
Character first = "swiss".chars()
.mapToObj(c -> (char) c)
.collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()))
.entrySet().stream()
.filter(e -> e.getValue() == 1)
.map(Map.Entry::getKey)
.findFirst().orElse(null); // 'w'
The LinkedHashMap keeps insertion order. Without it, the "first" is random, which is a classic bug.
29. Find duplicate elements in a list.
Set<Integer> seen = new HashSet<>();
Set<Integer> dups = Stream.of(1, 4, 2, 4, 5, 1)
.filter(n -> !seen.add(n))
.collect(Collectors.toSet()); // [1, 4]
Mention that this relies on a side effect, so it's fine for sequential streams but not parallel ones. The groupingBy-and-count approach is safer.
30. Join a list of names into a comma-separated string.
String names = emps.stream().map(Employee::name).collect(Collectors.joining(", ", "[", "]"));
// [Asha, Ravi, Meera, Karan, Divya]
31. Sum the even numbers in a list.
int sumEven = List.of(1, 2, 3, 4, 5, 6).stream()
.filter(n -> n % 2 == 0)
.mapToInt(Integer::intValue)
.sum(); // 12
32. Get the names of the top 3 earners.
List<String> top3 = emps.stream()
.sorted(Comparator.comparingDouble(Employee::salary).reversed())
.limit(3)
.map(Employee::name)
.collect(Collectors.toList());
Optional, interfaces, Date-Time and concurrency (33–40)
33. What is Optional, and why was it added?
A container that may or may not hold a value. It makes "this might be missing" explicit in a method's return type and pushes callers to handle the empty case instead of hitting a NullPointerException.
Optional<Employee> e = findByName("Asha");
String dept = e.map(Employee::dept).orElse("Unknown");
34. What is the difference between orElse and orElseGet?
orElse(value) always evaluates its argument, even when the Optional has a value. orElseGet(supplier) only calls the supplier when it's empty. If the default is expensive, like a database call, use orElseGet.
35. What are the best practices for Optional?
Use it as a return type. Don't use it for fields, method parameters or collections (return an empty list instead). Avoid calling get() without checking; prefer map, filter, orElse, orElseThrow and ifPresent.
36. What are default methods in interfaces, and why were they added?
Methods with a body inside an interface, marked default. They let library authors add new methods to existing interfaces without breaking every class that implements them. That's how forEach was added to Iterable and stream() to Collection.
37. What happens if a class implements two interfaces with the same default method?
It won't compile until the class overrides the method. Inside the override, it can call a specific one with InterfaceName.super.method(). Class methods always win over interface defaults.
38. What are static methods in interfaces?
Utility methods that belong to the interface itself, called as InterfaceName.method(), such as Comparator.comparing(). They aren't inherited by implementing classes.
39. What was wrong with java.util.Date, and what replaced it?
Date and Calendar are mutable (not thread-safe), have confusing APIs (months start at 0), and mix date and time-zone handling. Java 8's java.time API has immutable, thread-safe classes: LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Instant, Duration, Period and DateTimeFormatter.
LocalDate joining = LocalDate.of(2026, 4, 1);
long daysWorked = ChronoUnit.DAYS.between(joining, LocalDate.now());
ZonedDateTime ist = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));
40. What is CompletableFuture?
A Future you can complete manually and chain without blocking. It's used to run async tasks and combine their results.
CompletableFuture<User> user = CompletableFuture.supplyAsync(() -> userService.get(id));
CompletableFuture<List<Order>> orders = CompletableFuture.supplyAsync(() -> orderService.forUser(id));
CompletableFuture<Profile> profile = user.thenCombine(orders, Profile::new)
.exceptionally(ex -> Profile.empty());
Key methods: thenApply (transform), thenCompose (chain another async call), thenCombine (merge two), allOf (wait for many) and exceptionally (handle errors). For I/O-heavy work, pass your own executor instead of relying on the common pool.
Common mistakes candidates make
- Writing a 12-line stream where a simple loop would be clearer.
- Modifying external state inside
maporfilter. - Forgetting that
toMapthrows on duplicate keys. - Using
Optional.get()without checkingisPresent(). - Calling
parallelStream()to "make it faster" without measuring. - Not knowing that a stream can be consumed only once.
A 5-day Java 8 prep plan
| Day | Focus | Practice |
|---|---|---|
| 1 | Lambdas, functional interfaces, method references | Rewrite 10 anonymous classes as lambdas |
| 2 | Stream basics: filter, map, flatMap, reduce, sorted | 15 small stream exercises |
| 3 | Collectors: groupingBy, partitioningBy, toMap, joining | Solve questions 25–32 above without an IDE |
| 4 | Optional, default methods, the java.time API | Refactor null checks into Optional |
| 5 | CompletableFuture, parallel streams, Java 11–21 highlights | Mock interview with 3 stream problems |
Which roles ask Java 8, and what they pay
Java 8 questions are standard for Java developers, Spring Boot backend developers, full stack Java developers and automation engineers who write Selenium in Java. See the full stack developer salary guide and Weekday's salary pages for 2026 numbers.
Related prep: Spring Boot interview questions, microservices interview questions, DSA interview questions and Java developer fresher resume examples.
Frequently asked questions
Why do interviews still ask about Java 8 in 2026?
Because its features, streams, lambdas and Optional, are the foundation of modern Java, and many production systems still run on Java 8 or 11. Interviewers use "Java 8" as shorthand for functional-style Java.
What are the most asked Java 8 coding questions?
Second-highest salary, group by department, character frequency, first non-repeated character, duplicates in a list, and sorting by multiple fields, all using streams.
Should I also prepare Java 11, 17 and 21 features?
Yes, at a high level: var (10), new String methods and the HTTP client (11), records, sealed classes and text blocks (17), and virtual threads, pattern matching for switch and sequenced collections (21). Expect "what's new since Java 8?" at product companies.
Are streams faster than loops?
Not necessarily. For simple operations, a plain loop is often as fast or faster. Streams win on readability and composability, and parallel streams can win on large CPU-bound work. Say this in interviews; it shows maturity.
Is Java 8 asked for freshers?
Yes, basic lambdas, streams and functional interfaces come up even in fresher interviews at product companies. Service companies usually focus on core Java, with a few Java 8 questions.
Ready for your next Java role? Find Java and backend developer jobs on Weekday, and run your resume through our free ATS resume checker first.




