0% found this document useful (0 votes)
7 views19 pages

Java Handbook GPT Genrated

The document outlines key language feature updates from Java 8 to Java 21, highlighting functional interfaces, lambda expressions, method references, the Stream API, default and static methods in interfaces, and Base64 encoding. Each feature is explained with motivation, syntax, usage examples, best practices, and interview tips. The content emphasizes the evolution of Java towards a more functional programming style, improving code readability and maintainability.

Uploaded by

Dhruv Dixit
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views19 pages

Java Handbook GPT Genrated

The document outlines key language feature updates from Java 8 to Java 21, highlighting functional interfaces, lambda expressions, method references, the Stream API, default and static methods in interfaces, and Base64 encoding. Each feature is explained with motivation, syntax, usage examples, best practices, and interview tips. The content emphasizes the evolution of Java towards a more functional programming style, improving code readability and maintainability.

Uploaded by

Dhruv Dixit
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 19

Java 8 through Java 21: Key Language Feature

Updates
Java 8 marked a major modernization of the language, introducing functional-style features, and
subsequent releases (Java 9–21) have added further improvements. The following sections explain each
feature’s motivation, syntax (with examples), usage advice, and comparisons to prior approaches. Each
feature’s interview-relevant insights and design considerations are included. Code examples use Java syntax
highlighting for clarity.

Functional Interfaces
A functional interface is an interface with exactly one abstract method (a Single Abstract Method, or SAM).
Functional interfaces can include any number of default or static methods with implementations,
but only one abstract method. Common examples are Runnable , Callable , Comparator , and Java’s
built-in java.util.function.* interfaces.

This concept was introduced in Java 8 to support lambda expressions and method references. Functional
interfaces serve as target types for lambdas: a lambda provides an implementation of the interface’s single
method. Java provides the optional @FunctionalInterface annotation to document intent and let the
compiler enforce the single-abstract-method rule 1 2 . For example:

@FunctionalInterface
interface Calculator {
int compute(int a, int b);
default String info() { return "Calculates a and b"; }
}

Because Calculator has only one abstract method, you can write:

Calculator add = (x, y) -> x + y;


System.out.println(add.compute(2,3)); // prints 5

Here the lambda (x,y) -> x+y is assigned to a Calculator , providing its compute method.
Functional interfaces enable concise anonymous functions and avoid the verbosity of inner classes.

• Motivation: Functional interfaces make lambdas and method references possible, enabling
functional programming idioms. They also allow interfaces to evolve (with default methods) without
breaking existing implementations.

1
• Best Practice: Use the @FunctionalInterface annotation to signal intent and get compile-time
checking 3 . Name the interface clearly (e.g. Predicate , Function , or a domain-relevant verb
phrase). Avoid adding multiple abstract methods or conflicting default implementations.
• Use Case: Any time a single-operation callback or operation is needed (e.g. sorting, event handling,
callbacks). For instance, Comparator<T> is a functional interface used with lambdas to sort
collections.
• Prior Alternative: Before Java 8, single-method interfaces were often implemented via anonymous
inner classes, which were verbose. For example, prior to Java 8:

new Thread(new Runnable() {


@Override public void run() { System.out.println("Hello"); }
}).start();

Now we can use a lambda: new Thread(() -> System.out.println("Hello")).start();


4 .
• Interview Tip: Be prepared to define a functional interface and explain why
@FunctionalInterface is helpful. You might be asked to list common functional interfaces (like
Consumer , Function , Supplier , Predicate ) and how they relate to lambdas.

Lambda Expressions
Lambda expressions provide a concise syntax for implementing functional interfaces. Introduced in Java 8, a
lambda expression is essentially an anonymous function – a short block of code with parameters and a
result. Formally:

(parameters) -> { body }

For example, (x,y) -> x + y is a lambda that takes two ints and returns their sum. Lambdas do not
have names or belong to a class. They enable passing behavior (code) as data. Key points:

• Syntax: A lambda can have zero or more parameters. The body can be a single expression or a
block. E.g.:

() -> System.out.println("Hi");
(int x, String s) -> { return x + s.length(); }
list.forEach(item -> System.out.println(item)); // using in API

• Type: A lambda must match a functional interface’s method signature. The compiler infers types if
not given. For example, given interface Func { String apply(String s); } , you could
write Func f = s -> s.trim(); .
• Components: A lambda consists of an argument list, an arrow token ( -> ), and a body 5 .

2
Fig: A Java lambda has an argument list, an arrow ( -> ), and a body 5 .

• Example: Implementing a custom functional interface:

@FunctionalInterface
interface StringOp { int length(String s); }
StringOp getLength = str -> str.length();
System.out.println(getLength.length("hello")); // 5

• Advanced Use: Lambdas can capture (read-only) effectively-final local variables. They can throw
checked exceptions if the functional interface method declares them.
• Best Practices: Use lambdas to simplify code, especially in streams and callbacks. Avoid overly
complex lambdas – if logic is long, use a method or named class for clarity. Use method references
( Class::method ) when the lambda just calls an existing method (see next section).
• Prior Alternative: Before lambdas, one had to use anonymous classes, e.g.
new Func() { public int compute(int a, int b) { return a+b; } } . Lambdas greatly
reduce boilerplate.
• Performance/Design: Lambdas are syntactic sugar; at runtime they typically generate
invokedynamic calls to hidden classes or methods. There’s no significant performance penalty
compared to anonymous classes, and often they use less memory.
• Interview Tip: Know that lambdas require functional interfaces, and that variable capture is
effectively-final. Be able to convert a simple anonymous class to a lambda expression and vice versa.

Method References
A method reference is a shorthand notation for a lambda that calls an existing method. It uses :: to
refer to a method without invoking it. Method references also appeared in Java 8. They come in four forms
6 : 1. Static method: ClassName::staticMethod 2. Instance method of a particular object:
instance::instanceMethod 3. Instance method of an arbitrary object of a type:
ClassName::instanceMethod (used when signature matches (obj, args) ->
obj.instanceMethod(args) ) 4. Constructor reference: ClassName::new

For example:

List<String> names = List.of("Alice","Bob");


names.forEach(System.out::println); // 1. instance method of particular
object (System.out)
int maxLen = Collections.max(names,
Comparator.comparingInt(String::length)); // 3. Class::instanceMethod
Function<String, Integer> parse = Integer::parseInt; // 2. static method
Supplier<List<String>> listMaker = ArrayList::new; // 4. constructor

This is equivalent to lambdas like s -> System.out.println(s) or (a,b) ->


Integer.parseInt(a) but shorter.

3
• Advantages: Method references make code more concise and readable when simply calling an
existing method. They express intent clearly.
• Comparison: Without method ref, one would write list.forEach(s ->
System.out.println(s)); . Method ref System.out::println is cleaner 7 .
• Best Practices: Use method references when a lambda just delegates to a method. Otherwise, use
lambdas for inline logic. Avoid overusing static method refs if it hurts readability.
• Use Case: Common in stream operations (e.g. .map(Object::toString) or
.filter(String::isEmpty) ), and in sorting ( Comparator.comparing(Person::getAge) ).
• Interview Tip: Recognize and explain each kind of method reference. For example,
String::compareToIgnoreCase vs someList::addAll .

Stream API
The Java Stream API (introduced in Java 8’s java.util.stream package) provides a high-level, functional-
style way to process sequences of elements (usually from collections or arrays). A Stream is not a data
structure but a pipeline of aggregate operations. For example, to filter and sum weights of red widgets:

int totalWeight = widgets.stream()


.filter(w -> w.getColor()==RED)
.mapToInt(Widget::getWeight)
.sum();

Internally, streams support a fluent sequence of intermediate (e.g. filter , map ) and terminal (e.g.
forEach , collect , sum ) operations 8 . Operations are lazy: nothing happens until a terminal op
runs. Streams are designed for concise code and can improve readability. They also support easy
parallelization with parallelStream() 8 .

Fig: Java Streams process data through a pipeline of operations (source: GeeksforGeeks) 9 8 .

Streams enable tasks like filtering, mapping, and collecting without manual loops. For example:

List<String> cities = List.of("London","New York","Delhi");


List<String> caps = cities.stream()
.map(String::toUpperCase)
.filter(s -> s.startsWith("D"))
.collect(Collectors.toList());
System.out.println(caps); // [DELHI]

• Benefits: Streams can simplify code by avoiding explicit loops and mutable data structures. They are
internally optimized (e.g., fusion of operations) and can run in parallel. For example,
empList.parallelStream().filter(…)... utilizes multiple cores.

4
• Comparison: Prior code often used loops and conditionals. Streams let you write in a declarative
style (what to do, not how). For example, instead of a loop to sum values, use
list.stream().mapToInt(x->x).sum() .
• Best Practices: Use streams for clarity when processing collections. Remember that operations on
streams should be stateless and side-effect-free (except in forEach terminal if intentionally for I/
O). Be cautious with parallel streams on I/O or small data (overhead may outweigh benefits).
• Performance/Design: Stream pipelines are lazy; intermediate ops do not execute until a terminal op
is invoked. They can fuse operations to avoid extra iterations. The pipeline can potentially be
executed in parallel, but not all tasks are parallel-friendly.
• Interview Tip: Understand how a stream pipeline works (source → intermediate ops → terminal op)
8 , and differences between sequential and parallel streams. Be ready to explain

forEachOrdered vs forEach , and how streams differ from collections (for example, streams
cannot be reused after a terminal operation).

Default Methods in Interfaces


Java 8 allows interfaces to include default methods—methods with an implementation marked by the
default keyword. This lets library designers add new methods to interfaces without breaking existing
implementations 10 . For example:

interface Shape {
double area();
default String describe() { return "I am a shape"; }
}

Here, describe() has a body, so implementing classes need not override it (though they can). Default
methods provided backwards compatibility: if a new method is added to an interface like
List.sort(Comparator) in Java 8, existing List implementations automatically inherit the default
implementation 11 10 .

interface TestInterface {
void square(int a);
default void show() { System.out.println("Default Method Executed"); }
}
class TestClass implements TestInterface {
public void square(int a) { System.out.println(a*a); }
}

• Motivation: To evolve interfaces (especially core APIs like Collection ) without breaking existing
code. For instance, Java 8 added Collection.stream() as a default method, so all collections get
streams automatically.
• Prior Alternative: Before Java 8, interface changes required updating every implementing class.
Default methods eliminated that problem 10 .

5
• Best Practices: Use default methods sparingly and meaningfully. Avoid “multiple inheritance”
ambiguity: if a class implements two interfaces that define the same default, the class must override
it. Use InterfaceName.super.method() to disambiguate if needed.
• Real Use: In many APIs, default methods provide convenience or backward compatibility. For
example, Iterable.forEach (see next section), or Map.getOrDefault .
• Design Consideration: Default methods can complicate interface hierarchies. Only use them when a
reasonable default behavior exists. Remember, they are inherited by classes (unless overridden), but
cannot be abstract .
• Interview Tip: Be ready to explain how default methods help with binary compatibility 11 . You might
be asked to resolve a diamond problem (two defaults with same signature from different interfaces)
or to show a default method syntax.

Static Methods in Interfaces


Java 8 also allows static methods in interfaces. These are defined with the static keyword and have a
full implementation. For example:

interface Utils {
static String info() { return "Utility"; }
}

Static interface methods belong to the interface (not implementing classes) and cannot be overridden 12 .
They are useful for grouping related utility functions. For example, java.util.Map has static
Map.of(...) factory methods, and java.util.function.Predicate has static
Predicate.isEqual() .

• Syntax: interface I { static void helper() { ... } }


• Comparison: Without static interface methods, utility methods were in separate classes (like
Collections or Arrays ). Static interface methods allow keeping helper methods together with
the interface’s semantic group.
• Best Practices: Use them for helper/factory methods closely related to the interface. Always call via
the interface name: InterfaceName.method() . Because they can’t be inherited or overridden,
they are more like class static methods.
• Use Case: A common use is validation or conversion functions. For example:

interface Converter {
static Converter identity() { return s -> s; }
String convert(String s);
}

• Interview Tip: Know that static interface methods were introduced in Java 8. They differ from default
methods (which are inherited and instance-level). Static methods in interfaces must be called on the
interface, not on an implementing object.

6
Base64 Encoding and Decoding
Java 8 added the java.util.Base64 class to support Base64 operations natively 13 . This replaces older,
non-standard classes (e.g. sun.misc.BASE64Encoder ) and provides a standard API for encoding binary
data to Base64 and decoding it back. Usage is via Base64.Encoder and Base64.Decoder , typically
through static factory methods:

String original = "Hello";


String encoded =
Base64.getEncoder().encodeToString(original.getBytes(StandardCharsets.UTF_8));
byte[] decodedBytes = Base64.getDecoder().decode(encoded);
String decoded = new String(decodedBytes, StandardCharsets.UTF_8);

• Variants: There are basic, URL-safe, and MIME flavors. For example, Base64.getUrlEncoder()
produces a URL-safe Base64 string.
• Example:

String data = "Java8Data";


String b64 = Base64.getEncoder().encodeToString(data.getBytes());
System.out.println(b64);
System.out.println(new String(Base64.getDecoder().decode(b64)));

• Use Cases: Encoding binary data for text protocols (emails, JSON/XML, URLs) or for embedding in
HTML. It’s commonly asked how to base64-encode a string or byte array in Java (answer: use
java.util.Base64 ).
• Comparison: Prior to Java 8, developers often used external libraries like Apache Commons Codec or
Java’s javax.xml.bind.DatatypeConverter (which has a base64 method but is cumbersome).
Now use java.util.Base64 .
• Design Note: The API provides both encode (to byte[]) and encodeToString . The decoders
throw IllegalArgumentException on invalid input.
• Interview Tip: Remember the different variants ( getEncoder() , getUrlEncoder() , etc.) and
that this was introduced in Java 8. A common question is “How to Base64-encode in Java 8+?”
Answer: Base64.getEncoder() .

forEach Method
Java 8 introduced a default forEach(Consumer<? super T>) method in the Iterable interface, and
a terminal forEach on streams. This provides a concise way to process all elements of a collection or
stream. For example:

List<String> list = List.of("a","b","c");


list.forEach(System.out::println); // prints each element

list.stream()

7
.filter(s -> !s.isEmpty())
.forEach(System.out::println); // using stream's forEach

The Iterable.forEach method is effectively:

default void forEach(Consumer<? super T> action) {


for (T t : this) action.accept(t);
}

It’s a default interface method, so all collections have it 14 . For streams, forEach is a terminal operation
that loops over stream elements, invoking the given lambda for each element 15 .

• Example:

myList.stream().filter(x->x>0).forEach(System.out::print);

• Behavior: Using forEach on a parallel stream can process elements in parallel (in
nondeterministic order). Use forEachOrdered to preserve encounter order.
• Best Practices: Prefer forEach when you want to perform side-effects per element (logging,
collecting in a list via synchronized add, etc.). Otherwise, functional pipelines ending in collect
are more common. Note that you cannot “break” out of forEach ; for loops (or takeWhile on
streams) are needed for early exit.
• Comparison with loops: A classic for loop or iterator can be more flexible (can break or continue).
forEach often makes code shorter and focuses on the operation rather than iteration mechanics.
• Design Note: The forEach on streams consumes the stream (it’s terminal) 16 . After calling it, the
stream cannot be reused.
• Interview Tip: Understand differences between Iterable.forEach (default method in Iterable)
and Stream.forEach (terminal op). Common interview points: “Is forEach parallel-safe?” and
“Difference between forEach and forEachOrdered .”

Try-With-Resources
Originally introduced in Java 7 but still essential, try-with-resources automatically closes any
AutoCloseable (or Closeable ) resources, simplifying cleanup code. Its syntax is:

try (ResourceType res = new ResourceType(...)) {


// use res
}
// res is auto-closed here

For example:

8
try (BufferedReader br = new BufferedReader(new FileReader("input.txt"))) {
System.out.println(br.readLine());
} catch (IOException e) {
// handle error
}

Here br is closed automatically when the try block exits. This avoids the boilerplate of a finally
block. In Java 9+, you can even use an existing effectively final resource declared outside the try, by simply
listing it without redeclaring 17 :

BufferedReader br = new BufferedReader(new FileReader("in.txt"));


try (br) {
// use br
}

(Java 9 relaxation: if br is final or effectively final, the try(br) syntax closes it automatically 17 .)

• Benefits: Simplifies code and avoids resource leaks. Multiple resources can be declared (comma-
separated) in the try: try (A a = ...; B b = ...) { ... } .
• Comparison: Before try-with-resources, code had to close resources manually in a finally block,
which was verbose and error-prone.
• Performance: Negligible overhead; resource closing happens reliably. Multiple resources are closed
in reverse order of declaration.
• Design: Any object implementing AutoCloseable can be used. If exceptions are thrown during
close, they can be suppressed (available via Throwable.getSuppressed() ).
• Interview Tip: Know the Java 9 improvement: you no longer need to declare a new variable inside
the try if you already have a final/effectively-final resource 17 . Also, be clear that try-with-resources
ensures close() is called even if exceptions occur.

Type Annotations (JSR 308)


Java 8’s type annotations (JSR 308) allow annotations not just on declarations but on any use of types. For
example, you can now write:

public void setList(@NonNull List<String> list) { ... }


int @Range(0,100) [] scores = ...;

Here @NonNull and @Range could be type annotations on a parameter and array element type. This
enables powerful static checking (through frameworks) by annotating types throughout code 18 .

• Motivation: By applying annotations to types (with @Target(ElementType.TYPE_USE) ), tools


can enforce contracts (like null checks or taint analysis) anywhere a type is used. For example, the
Checker Framework uses @NonNull , @Nullable on types.
• Example:

9
import org.checkerframework.checker.nullness.qual.NonNull;
public @NonNull String sayHello(@NonNull String name) { ... }

• Use Cases: Static analysis (nullability checks, immutability), documentation of type usage. This does
not affect runtime behavior by default.
• Comparison: Prior to Java 8, annotations could only go on declarations (e.g. on a field or variable),
not directly on the type in an expression. Type annotations provide finer granularity.
• Frameworks: Java SE itself doesn’t enforce type annotations, but external tools (Checker Framework,
SpotBugs) do. For example, assigning null to an @NonNull variable will trigger a warning/error
under a checker 19 20 .
• Interview Tip: Know that type annotations expanded the places you can put @Override ,
@NonNull , etc., and that JSR 308 made types-first annotations (Java 8). You may be asked to give
an example of a type annotation (e.g. List<@NonNull String> ).

Repeating Annotations
Java 8 introduced repeating annotations. Now you can apply the same annotation multiple times to a
declaration without wrapping them in a container manually. An annotation type must be marked
@Repeatable , specifying its containing annotation. For example:

@Repeatable(Schedules.class)
@interface Schedule {
String dayOfMonth() default "first";
// ...
}
@interface Schedules { Schedule[] value(); }

@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour=23)
void doPeriodicCleanup() { ... }

Prior to Java 8, repeating required a manual container (like @Schedules({@Schedule(...),


@Schedule(...)}) ) 21 . Now Java allows the compiler to implicitly bundle repeated annotations into the
container.

• Benefits: Makes code more readable. Instead of wrapping with an array in a single annotation, you
can just list annotations one after another 21 22 .
• Usage: Just annotate a type multiple times, provided the annotation definition has @Repeatable .
• Example: Applying multiple validation constraints to a field:

@Pattern("[A-Z]+")
@Pattern("[0-9]+")
String mix; // hypothetical example

10
• Comparison: Before, you’d need a container annotation explicitly. Now Java treats multiple
annotations of the same type as an array-valued container behind the scenes.
• Interview Tip: Know @Repeatable ’s use and that it generates a hidden container annotation. For
instance, @Repeatable(Anno.Container.class) leads the compiler to use @Anno.Container
automatically when you repeat @Anno .

Java Module System (Project Jigsaw)


Java 9 introduced the Java Platform Module System (JPMS, a.k.a. Project Jigsaw) to modularize the JDK and
allow applications to be organized into modules with explicit dependencies. A module is defined by a
module-info.java descriptor. For example:

// module-info.java
module com.example.app {
requires java.sql;
requires com.example.lib;
exports com.example.app.api;
}

This says the module com.example.app depends on the java.sql and com.example.lib modules,
and it exports the com.example.app.api package (allowing other modules to use it). All other packages
remain encapsulated. The module system’s goals are dependency management, performance
improvements, smaller runtime images, better security, and strong encapsulation 23 . As one author notes,
“modular system…solves dependency management…improve performance…finer-grained encapsulation
and control” 23 .

• Features: JDK itself is modular; most java.* packages belong to named modules. You compile
and run with module paths (using javac --module-path and java --module ).
• Automatic Modules: Existing JARs on the module-path become automatic modules, bridging non-
modular and modular code.
• Encapsulation: Only explicitly exported packages are visible to other modules; non-exported
packages (even if public ) are hidden.
• Example: The java.sql module is defined as:

module java.sql {
requires transitive java.logging;
requires transitive java.xml;
exports java.sql;
exports javax.sql;
uses java.sql.Driver;
}

This declares its dependencies and exports 24 .


• Comparison: Before modules, Java had a flat classpath. Modules introduce a “module path” and
strong encapsulation. It’s often compared to OSGi but is built into the JDK.

11
• Best Practices: Use modules to split large apps into cohesive components. Specify exports only
for API packages, and requires for necessary modules. Use provides / uses for service
loading, if needed.
• Design Considerations: Some reflective libraries may need opens directives to work. Tools like
jdeps help analyze dependencies. The jlink tool can create custom runtime images containing
only needed modules.
• Interview Tip: You might be asked why modules were added. Emphasize dependency clarity and
modular JRE. Also know the basic syntax ( module , requires , exports , opens , uses ,
provides ) and difference between classpath and modulepath.

Diamond Operator with Anonymous Inner Classes


The diamond operator ( <> ) for generic instantiation was introduced in Java 7, but originally it didn’t work
with anonymous inner classes. Java 9 lifted this restriction: now you can use diamond when creating an
anonymous class instance. For example:

// Before Java 9, this was illegal:


Runnable r = new Callable<String>() { // anonymous class with generic
Callable<String>
public String call() { return "Hi"; }
};

// With diamond (Java 9+):


Callable<String> c = new Callable<>() {
public String call() { return "Hi"; }
};

The < > is inferred by the compiler even in the anonymous class case. As explained by GfG, “the diamond
operator could not be used with Anonymous inner classes in JDK 7. In JDK 9, it can be used with the
anonymous class as well to simplify code and improve readability” 25 .

• Benefit: Reduces verbosity. You no longer have to repeat generic types twice when extending or
implementing a generic class anonymously.
• Example:

List<String> list = new ArrayList<>() {{


add("one"); add("two");
}};

• Comparison: Previously, an anonymous class required explicit type parameters on the right side (no
diamond). Now new Type<>() { ... } is allowed, making code cleaner.
• Interview Tip: Mention that this change (JEP 213) came in Java 9. A question might ask what JEP
allowed diamond on anonymous classes. Remember: diamond on anonymous inner classes was
disallowed until Java 9.

12
Local Variable Type Inference ( var keyword)
Java 10 added var for local-variable type inference. You can write:

var list = new ArrayList<String>();

instead of ArrayList<String> list = ... . The compiler infers the type ( ArrayList<String>
here). This cuts down on repetitive type declarations (especially with generics). Key points:

• Usage: var can only be used for local variables with an initializer (so the type is obvious), or in the
header of a traditional for loop. It cannot be used for fields, method parameters, or return types.
• Example:

var stream = Files.lines(path); // inferred as Stream<String>


var map = new HashMap<String, Integer>(); // RHS generic decides the type

• Limitations: You cannot write var x; (no initializer) 26 , or var x = null; (cannot infer from
null) 27 , and not in method signatures 28 .
• Benefits: Reduces boilerplate ( Map<String,List<T>> often repeated). Can make code cleaner
when type is obvious. However, use judiciously: overusing var can hurt readability if the type isn’t
clear from context.
• Comparison: Before var , explicit type declarations were required. Now, the type is still static and
checked at compile time – there is no dynamic typing.
• Best Practices: Reserve var for local variables where the type is evident from the initializer. It’s not
meant to hide types; it’s a convenience. (In interviews, avoid var in examples where the type isn’t
obvious.)
• Interview Tip: Know that var is syntactic sugar (no effect on runtime) and Java remains statically
typed. In Java 11+ you can even use var in lambda parameter declarations, but only when all
parameters use var .

Switch Expressions (with yield )


Java 12–14 revamped switch into a switch expression, which can return a value and uses a more concise
syntax. The key features (finalized in Java 14) are:

• Arrow labels: Instead of case X: ... break; , you can write case X -> expr; . The code to
the right of -> executes with no fall-through. For example:

int numLetters = switch(day) {


case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;

13
default -> throw new IllegalStateException("Invalid day: " + day);
};

Here switch itself is an expression producing an int . As Oracle docs explain, “When the runtime
matches any label to the left of the arrow, it runs the code to the right of the arrow and does not fall
through” 29 .

• yield keyword: If using the old-style case L: syntax with a block, you must use yield to
return a value. For example:

String result = switch(value) {


case 1: { yield "one"; }
case 2: yield "two";
default: yield "other";
};

yield specifies the value of that branch. It “takes one argument, the value that the case label
produces in a switch expression” 30 .

• Comparison Table:

Feature Classic switch (pre-Java 12) Switch Expression (Java 14+)

Syntax case X: ...; break; case X -> ...; (no break needed)

Fall-through Allowed (must break ) Not allowed (no fall-through)

Returns Value No (switch is a statement) Yes (switch is an expression)

yield Keyword N/A Used to return a value from a block

Comma labels Each case one label You can list multiple case X, Y -> together

This summarizes differences: switch expressions eliminate fall-through bugs, make switches expressions
(not only statements), and allow grouping cases with commas.

• Best Practices: Use the new -> syntax when possible for brevity. Use default -> to cover other
cases. Note that switch expressions must cover all cases (either via default or exhaustive
enums), otherwise it’s a compile error.
• Performance: Equivalent to the old switch in compiled code; mostly a syntax/clarity change.
However, using expressions can encourage more use of immutable patterns.
• Interview Tip: Explain how switch(expr) now yields a value, and what yield does. An
interviewer might ask you to rewrite a traditional switch using the new expression form, or vice
versa. Also note that you can use string switch expressions as well.

14
Text Blocks
Java 15 standardized text blocks, which are multi-line string literals enclosed in triple quotes ( """ ). They
simplify writing multi-line text (like HTML, JSON, SQL) by preserving line breaks and avoiding cumbersome
escapes. For example:

String html = """


<html>
<body>
<p>Hello, world!</p>
</body>
</html>
""";

This yields a string with embedded newlines and indentation. Prior to text blocks, one had to concatenate
strings or use \n manually, which was error-prone 31 .

• Syntax: A text block starts with """ , then a newline, then content, and then an ending """ .
Leading whitespace is stripped based on the minimal common margin.
• Motivation: Embedding multi-line code snippets was cumbersome; text blocks “eliminate most of
these obstructions” 31 and let you write the text nearly as-is. They improve readability of embedded
text.
• Example:

String json = """


{
"name": "Alice",
"age": 30
}
""";

• Comparison: Before, one might write String json = "{\n\"name\":\"Alice\",\n\"age\":


30\n}"; , which is hard to read.
• Use Cases: Embedding SQL queries, HTML templates, JSON/XML snippets, regex, etc. Any time you
had a multi-line string literal.
• Design Notes: Text blocks automatically include line breaks (no need for \n ), and you can use
triple quotes inside by escaping the final quote (or by separating).
• Interview Tip: Mention that text blocks were a preview in Java 13–14 and finalized in Java 15 32 .A
typical question: show how to write a multi-line string in Java 15+.

Records
Java 16 (preview in 14) introduced record classes, a concise way to declare immutable data carriers. A
record automatically generates boilerplate like private final fields, constructor, accessors, equals() ,
hashCode() , and toString() . For example:

15
record Point(int x, int y) { }

This single line is equivalent to a class with final int x, y; , a constructor, int x() {return x;} ,
int y() {return y;} , and standard equals , hashCode , toString implementations 33 . As
Oracle explains:

“A record declaration specifies in a header a description of its contents; the appropriate


accessors, constructor, equals , hashCode , and toString methods are created
automatically. A record's fields are final because the class is intended to serve as a simple
'data carrier'.” 33

• Usage: Records are great for simple DTOs or value objects. They are implicitly final (cannot be
extended) and extend java.lang.Record . Records can implement interfaces, but all fields are
final.
• Example:

record Person(String name, int age) { }


Person p = new Person("Bob", 25);
System.out.println(p.name()); // "Bob"

• Best Practices: Use records for plain data holders. Don’t abuse them when behavior is needed.
Records automatically enforce immutability of fields and a useful toString .
• Comparison: Before records, one wrote full classes or used tools like Lombok to reduce boilerplate.
Records are built-in, type-safe, and avoid extra dependencies.
• Design Notes: You can add methods or override the default ones if needed. You can also define a
compact constructor to enforce invariants.
• Interview Tip: Expect questions like “What are records good for?” or “How does a record differ from
a normal class?” You should mention immutable data, auto-generated methods, and lack of
inheritance.

Sealed Classes
Java 17 (preview in 15) added sealed classes and interfaces. A sealed class explicitly lists the classes that
are allowed to extend it, using permits . For example:

public abstract sealed class Shape permits Circle, Square { }


public final class Circle extends Shape { ... }
public final class Square extends Shape { ... }

This means only Circle and Square may extend Shape . Sealed classes improve control over
inheritance hierarchies. As one article notes: “Sealed classes and interfaces provide improved control over

16
the inheritance hierarchy” 34 . You might use this when you want a fixed set of subclasses (like an algebraic
data type).

• Syntax: Mark a class/interface with sealed and include a permits clause listing permitted
subclasses. Subclasses must themselves be final , sealed , or non-sealed .
• Example:

public sealed interface Operation permits Add, Subtract, Multiply {}


public final class Add implements Operation { ... }
public final class Subtract implements Operation { ... }
public non-sealed class Multiply implements Operation { ... } // allows
further extension

• Advantages: Provides exhaustive case coverage (useful with switch expressions), and stronger
encapsulation (others cannot extend your type). It can also help the compiler (pattern matching can
be exhaustive).
• Comparison: Before sealed classes, any public class could be subclassed. Enums were the old
way to restrict to known values; sealed classes generalize this to classes with possibly different
structures.
• Design Notes: Sealed is a compile-time constraint. Reflection can still instantiate classes if they’re
accessible, and the JVM does not enforce additional runtime restrictions beyond normal access rules.
• Interview Tip: Emphasize “sealed controls which types can implement/extend” 35 . You might be
asked to compare sealed vs enum vs normal class, or to write a sealed class hierarchy.

Summary Tables

Interface Methods (Java 8):


| Method Type | Syntax | Body Allowed? | Overrideable by Impl? | Introduced |
|-------------|-------------------------|---------------|-----------------------|------------| | Abstract | void f(); | No | Yes |
Java 1.0 | | Default | default void f(){...} | Yes | Optional (can override) | Java 8 | | Static |
static void f(){...} | Yes | No (cannot override) | Java 8 |

Switch Statement vs Switch Expression:


| Feature | Classic switch (pre-Java 12) | Switch Expression (Java 14+) |
|------------------|---------------------------------------|--------------------------------------------| | Syntax | case X: ...;
break; | case X -> ...; (no break ) | | Fall-through | Allowed (need explicit break ) | Not
allowed (no implicit fall-through) | | Returns Value | No (statement only) | Yes (expression returns value) |
| Multiple Cases | One case per label | case A, B -> (comma-separated) | | Yield Statement | N/A |
Use yield value; to return from a block |

By understanding these features and how they improve on older patterns, developers can write cleaner,
more robust Java code. Each of the above topics is a common interview area, so ensure you can define
them, give examples, and discuss why and how they are used.

17
Sources: Authoritative Java tutorials and documentation 1 2 8 15 10 36 13 14 37 17 18 21 23
38 25 39 29 31 33 34 . Each code example and claim is based on these references and standard Java

behavior.

1 2 3 4 Java Functional Interfaces | GeeksforGeeks


https://www.geeksforgeeks.org/java-functional-interfaces/

5 Java Lambda Expressions | GeeksforGeeks


https://www.geeksforgeeks.org/lambda-expressions-java-8/

6 7 Method References in Java 8


https://beginnersbook.com/2017/10/method-references-in-java-8/

8 Stream (Java Platform SE 8 )


https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html

9 Java 8 Stream Tutorial | GeeksforGeeks


https://www.geeksforgeeks.org/java-8-stream-tutorial/

10 Default Methods In Java 8 | GeeksforGeeks


https://www.geeksforgeeks.org/default-methods-java/

11 Default Methods (The Java™ Tutorials > Learning the Java Language > Interfaces and Inheritance)
https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html

12 36 Static method in Interface in Java | GeeksforGeeks


https://www.geeksforgeeks.org/static-method-in-interface-in-java/

13 Base64 (Java Platform SE 8 )


https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html

14 Iterable forEach() Method in Java | GeeksforGeeks


https://www.geeksforgeeks.org/iterable-foreach-method-in-java-with-examples/

15 16 A Guide to Java Streams in Java 8 - Stackify


https://stackify.com/streams-guide-java-8/

17 Java SE 9 : Improved try-with-resources statements | GeeksforGeeks


https://www.geeksforgeeks.org/java-se-9-improved-try-resources-statements/

18 19 20 JSR 308 Explained: Java Type Annotations


https://www.oracle.com/technical-resources/articles/java/ma14-architect-annotations.html

21 22 Understanding Java’s Repeatable Annotations – Developer’s Coffee


https://www.developerscoffee.com/blog/java-8-repeatable-custom-annotations/

23 Understanding the Java Platform Module System (Project Jigsaw) - Alibaba Cloud Community
https://www.alibabacloud.com/blog/understanding-the-java-platform-module-system-project-jigsaw_601621

24 38 Introduction to Modules in Java - Dev.java


https://dev.java/learn/modules/intro/

25 Diamond operator for Anonymous Inner Class with Examples in Java | GeeksforGeeks
https://www.geeksforgeeks.org/diamond-operator-for-anonymous-inner-class-with-examples-in-java/

18
26 27 28 39 Local Variable Type Inference or LVTI in Java 10 | GeeksforGeeks
https://www.geeksforgeeks.org/local-variable-type-inference-or-lvti-in-java-10/

29 30 Switch Expressions
https://docs.oracle.com/en/java/javase/17/language/switch-expressions-and-statements.html

31 32 Text Blocks in Java 15 | GeeksforGeeks


https://www.geeksforgeeks.org/text-blocks-in-java-15/

33 Record Classes
https://docs.oracle.com/en/java/javase/17/language/records.html

34 35 Sealed Class In Java With Examples


https://javatechonline.com/sealed-class-in-java/

37 The try-with-resources Statement (The Java™ Tutorials > Essential Java Classes > Exceptions)
https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

19

You might also like