When an interviewer asks what is new in Java, listing every release note is not the goal. They want evidence that you can read and write Java used in modern codebases. Six feature groups carry most of that conversation: lambdas, streams, Optional, records, sealed types with pattern matching, and virtual threads.
Know what each feature replaces, show one small example, and state its limit, which is stronger than reciting version numbers alone.
Lambdas and functional interfaces in Java 8
A functional interface has one abstract method. Java 8 lambdas let you supply that behaviour without an anonymous-class wrapper.
Before Java 8, sorting names by length could look like this:
names.sort(new Comparator<String>() {
@Override
public int compare(String a, String b) {
return Integer.compare(a.length(), b.length());
}
});With a lambda:
names.sort((a, b) -> Integer.compare(a.length(), b.length()));The target type is Comparator<String>, so the compiler knows the parameter types and the method contract. Standard functional interfaces include Predicate<T>, Function<T,R>, Consumer<T> and Supplier<T>.
The interview trap is to say that a lambda is simply a shorter anonymous class. Both can provide behaviour, but their scoping and identity semantics are not identical. In a lambda, this refers to the enclosing instance. Lambdas also work naturally as inputs to stream operations.
Streams: a pipeline, not a collection
Streams arrived in Java 8. A stream processes elements through a pipeline and does not store the data itself. Consider a list of scores:
List<Integer> scores = List.of(42, 71, 58, 85, 39);
List<Integer> adjusted = scores.stream()
.filter(score -> score >= 50)
.map(score -> score + 5)
.sorted()
.toList();The exact result is [63, 76, 90]. Filtering keeps 71, 58 and 85. Mapping adds 5 to produce 76, 63 and 90. Sorting produces 63, 76 and 90. Rechecking the original list confirms that the pipeline did not mutate it.
Intermediate operations such as filter(), map() and sorted() are lazy. They build a pipeline. A terminal operation such as toList(), collect(), count() or reduce() triggers traversal.
Two follow-ups appear repeatedly:
Can a stream be reused? No. After a terminal operation, the stream is consumed. Create a new stream from the source.
Are parallel streams always faster? No. Splitting, coordination and ordering have costs. The data size, operation cost and shared state determine whether parallel execution helps.
Streams are excellent for transformations with clear, side-effect-free steps. A simple loop is often clearer when control flow, checked exceptions or mutation dominates the work.
Optional and the null-handling contract
Optional<T> also arrived in Java 8. It represents a result that may be present or absent, making that possibility visible in a return type.
Optional<User> user = repository.findByEmail(email);
String displayName = user
.map(User::displayName)
.orElse("Guest");Prefer orElseGet() when producing the fallback is expensive, because its supplier runs only when the optional is empty. Avoid calling get() without a presence check, because that recreates the unsafe assumption the type was meant to remove.
Do not turn every field, parameter and local variable into an Optional. Its clearest mainstream use is as a return type for “a value may be absent”. It is not a universal replacement for sensible validation or empty collections.
Records and compact data carriers
Records became a permanent language feature in Java 16. They replace much of the boilerplate in classes whose main job is to carry data:
record Candidate(String name, int score) {}That declaration provides component accessors, a canonical constructor, and value-based equals(), hashCode() and toString() behaviour. You can add validation in a compact constructor:
record Candidate(String name, int score) {
Candidate {
if (score < 0) throw new IllegalArgumentException("negative score");
}
}A record is shallowly immutable. Its component fields cannot be reassigned, but a component can still refer to a mutable list. A strong interview answer mentions that distinction instead of promising deep immutability.
Sealed classes and pattern matching
Sealed classes became permanent in Java 17. They restrict which types may extend or implement a type, making a closed domain model explicit.
sealed interface Payment permits Card, Upi {}
record Card(String lastFour) implements Payment {}
record Upi(String handle) implements Payment {}Java 21 made pattern matching for switch permanent. The compiler can use the closed hierarchy to check whether the cases cover every permitted subtype:
String label = switch (payment) {
case Card card -> "Card ending " + card.lastFour();
case Upi upi -> "UPI " + upi.handle();
};The modelling pitch is more important than the syntax. Sealing says, “these are the allowed variants”, records express compact values, and pattern matching consumes the variants without manual casts. Pattern matching for instanceof, permanent since Java 16, removes the older test-then-cast repetition in the same spirit.
Virtual threads in Java 21
Virtual threads became permanent in Java 21. They are lightweight threads managed by the Java runtime and are useful when an application needs very many concurrent tasks that spend substantial time waiting for I/O.
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
Future<String> result = executor.submit(() -> fetchProfile(userId));
System.out.println(result.get());
}The code keeps the familiar thread-per-task style without requiring one operating-system thread per task. That can improve scalability for blocking network or database work.
Virtual threads do not make CPU-bound code compute faster. They also do not remove the need to control downstream pressure on a database or API. Concurrency may become cheaper, but scarce external resources remain scarce.
Which Java version should you claim?
Claim the version you have actually used, then describe the features you can explain. Java 8 introduced lambdas, streams and Optional; Java 10 introduced local variable type inference with var; Java 16 finalised records; Java 17 finalised sealed classes; and Java 21 finalised pattern-switch and virtual threads.
var does not make Java dynamically typed. The compiler still infers one static local-variable type, and it is best used when the right side makes that type obvious.
Read the job description for the employer's baseline. Many teams standardise on an LTS release, but “LTS” alone does not tell you which one that codebase runs. It is honest to say, “I worked on Java 17 and have built small examples with Java 21 virtual threads,” if that is true.
For broader interview context, CS Fundamentals connects language knowledge to the underlying ideas, while Placement Preparation keeps the coding and interview layers in one plan.
The short version and next step
Start with Java 8's functional style, then add modern data modelling and concurrency. Explain a lambda's target interface, a stream's lazy pipeline, Optional as an absence contract, record immutability, sealed exhaustiveness and the I/O use case for virtual threads.
Use the Complete Java Course for the language path and the Coding and Skill Development route for wider preparation. In an interview, one correct example plus one honest limitation is the modern-Java answer that sounds like experience rather than memorisation.




