Detailed Briefing: Java Unit 3 - New
Features and Enhancements
This document provides a comprehensive overview of the new features
and enhancements in Java, primarily focusing on additions from Java 8
onwards. The source, an educational video, details these features with
definitions, explanations, examples, and practical applications.
1. Introduction to New Features in Java
The video begins by defining "features" as the inclusion or display of
something new and important within an existing entity. Java has
continuously added new features with its updates, particularly in versions
Java 5, 6, 7, and 8. These enhancements aim to improve performance,
code conciseness, and support new programming paradigms.
Key Features Mentioned:
Functional Interface
Lambda Expression
For-Each Method
Switch Expression
Sealed Classes
Autoboxing
Generics
Varargs
Java Annotations
Method References
Stream API
Try-with-Resources
Diamond Syntax
Local Variable Type Inference
Yield Keyword
Text Blocks
Java Records
Java Module System
Base64 Encoding/Decoding
The main focus of this unit is on the newer additions, predominantly from
Java 8, 9, 10, 13, 14, and 15.
2. Java 8 Functional Interface
Introduced in Java 8, a Functional Interface is an interface that contains
exactly one abstract method. It can, however, have any number of
default and static methods. It can also declare methods of the Object
class.
Key Concepts:
Single Abstract Method Interface (SAM Interface): This is
another name for a functional interface, highlighting its core
characteristic.
Purpose: It is primarily used to achieve a functional programming
approach in Java.
Annotation: The @FunctionalInterface annotation (e.g.,
@FunctionalInterface public interface MyInterface { ... }) is optional
but highly recommended. It restricts the interface to ensure it
remains a functional interface, throwing a compile-time error if more
than one abstract method is added.
Predefined Functional Interfaces: Java provides several
predefined functional interfaces in the java.util.function package,
including:
Consumer: Takes an argument but returns nothing (consumes).
Function: Takes an argument and returns a result.
Predicate: Takes an argument and returns a boolean value
(true/false).
Supplier: Takes no arguments but returns a result (supplies).
Custom Functional Interfaces: Developers can also define their
own custom functional interfaces.
Example (Valid Functional Interface):
@FunctionalInterface
interface MyInterface {
public void sayRitvik(); // Single abstract method
default void doSomething() { // Default method
System.out.println("Doing something");
static void anotherStaticMethod() { // Static method
System.out.println("Another static method");
3. Java Lambda Expression
Also introduced in Java 8, Lambda Expressions are a new and important
feature that provides a clear and concise way to represent an
interface method using an expression. They are particularly useful
with collection libraries for operations like iteration, filtering, and data
extraction.
Key Concepts:
Anonymous Function: A lambda expression is essentially an
anonymous function – a function without a name, return type, or
access modifier.
Implementation of Functional Interfaces: Lambda expressions
are primarily used to provide the implementation for functional
interfaces (which have only one abstract method).
Conciseness: They significantly reduce the amount of boilerplate
code, making the code shorter and more readable.
Benefits:Less coding (shorthand method).
Enables functional programming.
Makes code more readable, maintainable, and concise.
Supports parallel processing.
Reduces JAR file size.
Components/Syntax: A lambda expression has three main
components:
1. Argument List: Enclosed in parentheses (). Can be empty, have
one parameter, or multiple parameters.
2. Arrow Token: -> connects the argument list to the body.
3. Body: Contains the expression or statement(s) to be executed.
Syntax Examples:
No Parameters: () -> { System.out.println("No parameters"); }
One Parameter: (param) -> { System.out.println(param); } or
param -> { System.out.println(param); } (parentheses optional for
single parameter)
Multiple Parameters: (param1, param2) ->
{ System.out.println(param1 + param2); }
Example (Using Lambda Expression with Functional Interface):
@FunctionalInterface
interface Drawable {
public void draw(int width);
public class LambdaExample {
public static void main(String[] args) {
int width = 10;
// Lambda expression to implement the draw method
Drawable d = (w) -> {
System.out.println("Drawing " + w);
};
d.draw(width); // Output: Drawing 10
4. Java Method References
Method References, introduced in Java 8, provide an even more compact
and easier way to refer to methods of functional interfaces, often
serving as an alternative to lambda expressions when a lambda simply
calls an existing method.
Key Concepts:
Concise Lambda Replacement: When a lambda expression
merely invokes an existing method, a method reference can be used
to make the code even shorter and more readable.
Purpose: To refer to a method of a functional interface.
1. Types of Method References:Reference to a Static Method:
Refers to a static method of a class.
Syntax: ContainingClass::staticMethodName
1. Reference to an Instance Method: Refers to an instance method
of an object.
Syntax: containingObject::instanceMethodName
1. Reference to a Constructor: Refers to a constructor.
Syntax: ClassName::new
Example (Reference to a Static Method):
class MyClass {
public static void myStaticMethod() {
System.out.println("Static method called.");
interface MyInterface {
void display();
public class StaticMethodRefExample {
public static void main(String[] args) {
MyInterface mi = MyClass::myStaticMethod; // Method reference
mi.display(); // Output: Static method called.
5. Stream API
The Stream API, another significant addition in Java 8, is used to process
objects from a collection in a functional style. A "stream" is a sequence
of elements that supports various operations.
Key Concepts:
Collection vs. Stream: A collection (e.g., List, Set) holds data,
while a stream processes data from a source (like a collection or an
array).
Converting Collection to Stream: A collection can be converted
to a stream using the .stream() method.
1. Flow of Operations:Source: (e.g., Collection, Array, I/O Channel)
2. Stream Instance Creation: collection.stream()
3. Intermediate Operations: Transform the stream into another
stream (e.g., filter, map, distinct, sorted, limit).
4. Terminal Operations: Produce a result and terminate the stream
(e.g., forEach, collect, reduce, count, max, min).
5. Result: (e.g., aggregated result, new collection)
Immutability: Stream operations produce new streams or results
without modifying the original data source.
Pipeline: The sequence of intermediate and terminal operations on
a stream is often referred to as a "pipeline."
Advantages:Enhances functional programming in Java.
Supports powerful data processing operations (filtering, sorting,
mapping).
Enables efficient parallel processing.
Makes code more readable and maintainable.
Common Operations:Filter: Selects elements that satisfy a given
Predicate.
Map: Transforms each element using a Function to produce a new
stream.
Sorted: Sorts elements in natural order or using a custom
Comparator.
Distinct: Returns a stream with unique elements.
Limit: Truncates the stream to a maximum size.
forEach: Performs an action on each element (terminal operation).
Collect: Gathers elements into a Collection.
Count: Returns the number of elements.
Min/Max: Finds the minimum/maximum element.
Example (Stream Operations):
List<String> names = Arrays.asList("Ritvik", "Aryan", "Mohit", "Sourabh",
"Swapnil");
// Filtering and printing names starting with 'S'
names.stream()
.filter(name -> name.startsWith("S"))
.forEach(System.out::println);
// Output:
// Sourabh
// Swapnil
// Sorting names alphabetically
names.stream()
.sorted()
.forEach(System.out::println);
// Output:
// Aryan
// Mohit
// Ritvik
// Sourabh
// Swapnil
6. Default and Static Methods in Interfaces
Introduced in Java 8, these allow methods with implementations to be
defined inside interfaces. This helps in evolving interfaces without
breaking existing implementations.
Key Concepts:
Default Methods:Defined inside an interface using the default
keyword.
Provide a default implementation for methods, allowing existing
classes implementing the interface to inherit the new method
without requiring immediate modification.
Can be overridden by implementing classes.
"Non-abstract" methods within an interface.
Static Methods:Defined inside an interface using the static
keyword.
Belong to the interface itself, not to any specific instance of a class
implementing the interface.
Cannot be overridden by implementing classes.
Used to define utility methods related to the interface.
Purpose: To add new functionalities to existing interfaces without
affecting classes that already implement them. This solves the
"diamond problem" related to multiple inheritance by providing a
concrete implementation.
Example:
interface MyInterface {
void abstractMethod(); // Abstract method (no implementation)
default void defaultMethod() { // Default method
System.out.println("This is a default method.");
static void staticMethod() { // Static method
System.out.println("This is a static method.");
class MyClass implements MyInterface {
@Override
public void abstractMethod() {
System.out.println("Implementing abstract method.");
public class InterfaceMethodsExample {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.abstractMethod();
obj.defaultMethod(); // Calls default method
MyInterface.staticMethod(); // Calls static method directly from interface
7. Base64 Encoding and Decoding
Java 8 introduced built-in support for Base64 encoding and decoding,
standardizing the process of converting binary data into text format
and vice-versa. This ensures data integrity during network transmission.
Key Concepts:
Purpose: To encode binary data (0s and 1s) into a textual format
(A-Z, a-z, 0-9, +, /) that can be easily transported over networks
without corruption or data loss. Decoding reverses this process.
Pre-Java 8: Prior to Java 8, Base64 support often relied on non-
public (and limited) classes or external libraries.
java.util.Base64 Package: Java 8 introduced the public
java.util.Base64 class, simplifying Base64 operations.
1. Types of Encoders/Decoders:Basic Encoding/Decoding:
Standard Base64 as defined by RFC 4648 and RFC 2045. It does not
add line separators.
2. URL and Filename Safe Encoding/Decoding: A variant for URL
and filename safe Base64 encoding (replaces + with - and / with _).
3. MIME Encoding/Decoding: Adheres to RFC 2045, often used in
email. It ensures that encoded output lines are no more than 76
characters long and adds line separators.
Methods: The Base64 class provides getEncoder() and
getDecoder() methods to obtain Base64.Encoder and
Base64.Decoder instances respectively.
Example (Basic Encoding/Decoding):
import java.util.Base64;
public class Base64Example {
public static void main(String[] args) {
String originalInput = "Hello, World!";
// Encode
String encodedString =
Base64.getEncoder().encodeToString(originalInput.getBytes());
System.out.println("Encoded: " + encodedString); // Output:
SGVsbG8sIFdvcmxkIQ==
// Decode
byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
String decodedString = new String(decodedBytes);
System.out.println("Decoded: " + decodedString); // Output: Hello, World!
8. For-Each Method
The For-Each method is a utility function introduced in Java 8 that allows
iteration over collections (Lists, Sets, Maps) and streams in a more
concise way, often using lambda expressions or method references.
Key Concepts:
Utility Function: A function that performs a common, often
reusable operation.
Iteration: Used to perform a given action on each element of a
collection or stream.
Applicability:Iterable Interface: Available to List, Set, and other
collections (except Map).
Map Interface: Has its own forEach method.
Stream Interface: Offers forEach and forEachOrdered methods.
Parameter: Takes a single parameter, which is a functional
interface (typically a Consumer), allowing lambda expressions to be
passed as arguments.
Benefits: Reduces the boilerplate code of traditional for loops,
making iteration more readable and functional.
Example (For-Each with List and Lambda):
import java.util.ArrayList;
import java.util.List;
public class ForEachExample {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
// Using forEach with a lambda expression
names.forEach(name -> System.out.println("Hello, " + name));
// Output:
// Hello, Alice
// Hello, Bob
// Hello, Charlie
9. Try-With-Resources
Introduced in Java 7, Try-With-Resources is a statement that ensures that
each resource opened in the try block is automatically closed at
the end of the block, whether normally or abnormally.
Key Concepts:
Problem Solved (Pre-Java 7): Before Java 7, developers had to
explicitly close resources (like InputStream, OutputStream, database
connections) in a finally block to prevent resource leaks. This
increased code complexity and length.
Automatic Resource Management (ARM): Try-With-Resources
automates this closing process.
AutoCloseable Interface: Any object that implements the
java.lang.AutoCloseable interface (or java.io.Closeable, which
extends AutoCloseable) can be used in a Try-With-Resources
statement.
Syntax: Resources are declared within the parentheses after the try
keyword.
Syntax Example:
try (Resource resource1 = new Resource(); Resource resource2 = new
Resource()) {
// Use resource1 and resource2
} catch (Exception e) {
// Handle exceptions
// resource1 and resource2 are automatically closed here
Benefits:
Reduces boilerplate code by eliminating the need for an explicit
finally block for closing resources.
Improves code readability and conciseness.
Prevents common resource leak bugs.
Supports multiple resources separated by semicolons.
10. Java Annotations
Annotations are a form of metadata that can be added to Java source
code. They provide supplementary information about the program but do
not directly affect its execution. They are processed by compilers and
JVMs.
Key Concepts:
Metadata: Data about data (e.g., details of a photo like time, date,
size). Annotations provide metadata about classes, interfaces,
methods, fields, etc.
Introduced: JDK 5.
Syntax: Start with @ symbol (e.g., @Override).
Purpose:Provide information to the compiler (e.g., @Override helps
detect errors if a method is not actually overriding).
Generate code (e.g., for frameworks like Spring or Hibernate).
Runtime processing (can be inspected at runtime using reflection).
Difference from Comments: Unlike comments, which are ignored
by the compiler, annotations are read and processed by the
compiler and JVM.
Optional: They are not mandatory for code execution.
1. Types of Annotations (General):Marker Annotations: No
elements (e.g., @Override).
2. Single-Value Annotations: One element (e.g.,
@SuppressWarnings("unchecked")).
3. Full Annotations: Multiple elements (e.g., @Author(name="John
Doe", date="2023-01-01")).
Standard Built-in Annotations:@Override: Indicates that a
method overrides a superclass method.
@Deprecated: Marks an element as deprecated, indicating it should
no longer be used.
@SuppressWarnings: Suppresses compiler warnings.
Meta-Annotations (Annotations that annotate other
annotations):@Target: Specifies where an annotation can be
applied (e.g., method, class).
@Retention: Specifies how long an annotation is retained (source,
class, runtime).
@Inherited: Indicates that an annotation can be inherited by
subclasses.
@Documented: Includes the annotation in Javadoc.
11. Type Annotations and Repeating Annotations
These are advanced uses of annotations, primarily introduced in Java 8.
11.1 Type Annotations:
Introduced: Java 8.
Purpose: Can be applied to any place where a type is used, not just
declarations. This includes:
Return types of methods.
Type arguments (e.g., List<@NonNull String>).
new expressions.
instanceof checks.
Type casts.
Benefit: Enables richer static analysis and allows frameworks to
validate types at various points in the code.
Example:
public class TypeAnnotationExample {
public @NonNull String getName() { // Annotation on return type
return "John Doe";
public void processList(List<@NotEmpty String> items) { // Annotation on
type argument
// ...
11.2 Repeating Annotations:
Introduced: Java 8.
Purpose: Allows applying the same annotation multiple times to a
single declaration.
Mechanism: Requires a "container annotation" to hold the multiple
instances of the repeatable annotation. The repeatable annotation
itself must be annotated with @Repeatable.
Example:
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Schedules.class)
@interface Schedule {
String day();
@Retention(RetentionPolicy.RUNTIME)
@interface Schedules {
Schedule[] value();
public class RepeatingAnnotationExample {
@Schedule(day = "Monday")
@Schedule(day = "Wednesday")
public void doWork() {
System.out.println("Working on scheduled days.");
public static void main(String[] args) {
// Can retrieve multiple Schedule annotations at runtime
Schedule[] schedules =
RepeatingAnnotationExample.class.getAnnotation(Schedules.class).value(
);
for (Schedule s : schedules) {
System.out.println("Scheduled for: " + s.day());
12. Java Module System (Project Jigsaw)
A major change introduced in Java 9, the Module System (Project Jigsaw)
restructures the JDK into a set of modules and allows developers to create
their own.
Key Concepts:
Problem Solved (Pre-Java 9): Before Java 9, the JDK was a
monolithic structure, making applications heavy and difficult to
deploy on small devices. The entire rt.jar (runtime library) was
included, even if only a small part was needed.
Definition: A module is a named, self-describing collection of
related packages, types (classes, interfaces), and resources.
Purpose: To make Java more scalable, secure, and maintainable by
allowing developers to bundle only the necessary parts of the Java
platform and their application code.
Modular JARs: Java 9 introduced modular JAR files, which include a
module-info.class file in their root folder. This file is called the
Module Descriptor.
Module Descriptor (module-info.java):Describes the module's
name.
Specifies which packages it exports (makes available to other
modules).
Specifies which other modules it requires (depends on).
Example:module com.javamodule {
exports com.javamodule.greetings; // Exports this package
requires java.base; // Requires the base Java module
}
Benefits:Reliable Configuration: Strong encapsulation ensures
that modules only expose what they intend to.
Strong Encapsulation: Code outside a module cannot access non-
exported packages within it.
Scalability: Allows for smaller runtime images and applications.
Maintainability: Clear dependencies make applications easier to
understand and maintain.
Improved Security: Reduces the attack surface by only including
necessary modules.
1. Creating a Module:Create Directory Structure: Follows a
reverse-domain pattern (e.g., com/javamodule).
2. Create Module Descriptor: module-info.java file.
3. Write Java Source Code.
4. Compile: Use javac --module-source-path <path> -d <output-dir>
<module-info-file> <java-files>.
5. Run: Use java --module-path <path> --module
<module-name>/<main-class>.
java.base Module: The fundamental module in JDK 9+, containing
core Java APIs, and is implicitly required by all other modules.
13. Diamond Syntax Enhancement
The "Diamond Operator" (<>) was introduced in Java 7 to reduce
verbosity when creating instances of generic classes. Its
enhancement in Java 9 extended its applicability.
Key Concepts:
Introduced: Java 7.
Purpose: To avoid redundant type specification when instantiating
a generic class.
Pre-Java 7: Developers had to specify the generic type arguments
on both the left and right sides of the assignment.
List<String> list = new ArrayList<String>();
Java 7 Improvement: The diamond operator allowed omitting the
type arguments on the right-hand side, as the compiler could infer
them.
List<String> list = new ArrayList<>();
Java 7 Limitation: The diamond operator could not be used with
anonymous inner classes in Java 7, leading to a compilation error.
Java 9 Enhancement: Java 9 improved the diamond operator to
allow its use with anonymous inner classes. This further
reduces boilerplate code for such scenarios.
Example (Java 9 Diamond with Anonymous Inner Class):
import java.util.ArrayList;
import java.util.List;
public class DiamondOperatorEnhancement {
public static void main(String[] args) {
// Java 7 would throw a compilation error here for anonymous inner class
List<Integer> numbers = new ArrayList<Integer>() { // Anonymous inner
class
add(10);
add(20);
};
// In Java 9+, this is allowed:
List<Integer> numbers9 = new ArrayList<>() { // Diamond operator with
anonymous inner class
add(30);
add(40);
};
System.out.println(numbers);
System.out.println(numbers9);
}
14. Local Variable Type Inference (LVTI - var keyword)
Introduced in Java 10, Local Variable Type Inference allows developers to
skip the explicit type declaration for local variables, letting the
compiler infer the type automatically.
Key Concepts:
Introduced: Java 10.
Keyword: var
Purpose: To reduce boilerplate code and improve readability by
allowing the compiler to deduce the type of local variables from
their initializers.
Compiler's Role: The Java compiler (JDK 10+) figures out the data
type of the variable at compile time based on the value assigned to
it.
Applicability (Use Cases):Local variables with initializers (e.g., var
name = "John";).
Index variables in traditional for loops (e.g., for (var i = 0; i < 10; i+
+)).
Iteration variables in enhanced for loops (e.g., for (var item : list)).
Variables declared in try-with-resources statements.
Restrictions (Where var cannot be used):Class fields (instance
or static variables).
Method parameters.
Method return types.
Uninitialized local variables (e.g., var x;).
Variables initialized to null (e.g., var x = null;).
In lambda expression parameters.
For multiple variable declarations on a single line.
Example (Using var):
import java.util.ArrayList;
import java.util.List;
public class VarKeywordExample {
public static void main(String[] args) {
// Before Java 10
String name = "Alice";
List<String> messages = new ArrayList<>();
// With 'var' (Java 10+)
var newName = "Bob"; // newName is inferred as String
var newMessages = new ArrayList<String>(); // newMessages is inferred
as ArrayList<String>
System.out.println(newName);
newMessages.add("Hello");
System.out.println(newMessages);
15. Switch Expressions
Introduced as a preview feature in Java 12, finalized in Java 14, Switch
Expressions are an enhancement to the traditional switch statement,
allowing it to return a value and offering a more concise syntax.
Key Concepts:
Problem Solved (Traditional switch statement):Fall-through:
Requires break statements to prevent execution of subsequent
cases.
Verbosity: Can be quite long with many cases and break
statements.
Auxiliary Variable: Often requires a separate variable to store the
result.
Expression vs. Statement: Unlike switch statements which
perform an action, switch expressions produce a result.
Arrow Syntax (->): New syntax using case L -> result; which
implicitly handles break.
Return Value: The switch expression can directly assign a value to
a variable.
yield Keyword (Java 13/14): Used within a switch expression to
explicitly yield a value from a case block, especially when a case
block contains multiple statements.
Example (Switch Expression):
public class SwitchExpressionExample {
public static void main(String[] args) {
String day = "MONDAY";
// Traditional Switch Statement
int numLettersOld;
switch (day) {
case "MONDAY":
case "FRIDAY":
case "SUNDAY":
numLettersOld = 6;
break;
case "TUESDAY":
numLettersOld = 7;
break;
case "THURSDAY":
case "SATURDAY":
numLettersOld = 8;
break;
case "WEDNESDAY":
numLettersOld = 9;
break;
default:
numLettersOld = -1;
System.out.println("Old Switch: " + numLettersOld);
// Switch Expression (Java 14+)
int numLettersNew = switch (day) {
case "MONDAY", "FRIDAY", "SUNDAY" -> 6;
case "TUESDAY" -> 7;
case "THURSDAY", "SATURDAY" -> 8;
case "WEDNESDAY" -> 9;
default -> -1;
};
System.out.println("New Switch: " + numLettersNew);
16. Yield Keyword
Introduced as a preview feature in Java 13 and standardized in Java 14,
the yield keyword is used within switch expressions to produce a value
from a case block.
Key Concepts:
Introduced: Java 13 (preview), Java 14 (standard).
Purpose: To explicitly return a value from a case label within a
switch expression block, especially when a case block requires
multiple statements to compute the result. It also acts as a
terminator for the case block.
Difference from return:return exits the entire method or
constructor.
yield exits only the switch expression, transferring control to the
statement immediately following the switch expression.
Context: Exclusively used within switch expressions.
Example (Yield in Switch Expression):
public class YieldKeywordExample {
public static void main(String[] args) {
String day = "SATURDAY";
String typeOfDay = switch (day) {
case "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY" -> {
System.out.println("It's a weekday!");
yield "Weekday"; // Yields value from a block
case "SATURDAY", "SUNDAY" -> {
System.out.println("It's the weekend!");
yield "Weekend"; // Yields value from a block
default -> "Invalid Day";
};
System.out.println("Type of day: " + typeOfDay);
17. Java Text Blocks
Introduced as a preview feature in Java 13, finalized in Java 15, Text
Blocks provide a convenient way to define multi-line string literals in
Java.
Key Concepts:
Introduced: Java 13 (preview), Java 15 (standard).
Definition: A multi-line string literal.
Syntax: Starts with three double-quote characters (""") followed by
optional whitespace and a newline, and ends with three double-
quote characters.
Purpose:Readability: Improves the readability of multi-line
strings, especially for code snippets in other languages (HTML, JSON,
SQL, XML).
Avoids Escape Sequences: Eliminates the need for most escape
sequences (\n, \", \\) for newlines and quotes within the block.
Developer Control: Gives developers control over the formatting
of the string.
Features:Automatic handling of newlines and indentation.
Support for concatenation with other string literals.
Can contain almost any characters without needing to be escaped.
Benefit: Makes it significantly easier to embed blocks of code or
text from other languages directly into Java code.
Example:
public class TextBlockExample {
public static void main(String[] args) {
// Without Text Blocks (traditional string literal)
String htmlOld = "<html>\n" +
" <body>\n" +
" <p>Hello, world!</p>\n" +
" </body>\n" +
"</html>";
System.out.println("Old HTML:\n" + htmlOld);
// With Text Blocks (Java 15+)
String htmlNew = """
<html>
<body>
<p>Hello, world!</p>
</body>
</html>
""";
System.out.println("New HTML:\n" + htmlNew);
String json = """
"name": "Alice",
"age": 30
""";
System.out.println("JSON:\n" + json);
18. Java Records
Introduced as a preview feature in Java 14, standardized in Java 16, Java
Records are a special kind of Java class designed to concisely define
immutable data carriers.
Key Concepts:
Introduced: Java 14 (preview), Java 16 (standard).
Purpose: To reduce the boilerplate code typically required for data
classes (constructors, getters, equals(), hashCode(), toString()
methods).
Immutability: Record instances are inherently immutable; once
created, their state cannot be changed.
Declaration: Declared using the record keyword.
Automatic Generation: The Java compiler automatically
generates:
A canonical constructor (matching the component list).
Accessor methods (getters) for each component (e.g., brand()
instead of getBrand()).
equals(), hashCode(), and toString() methods.
Restrictions:Records are implicitly final and cannot be extended.
Cannot declare instance fields other than those declared in the
header.
Cannot be abstract.
Benefits:Conciseness: Significantly reduces code size for data-
only classes.
Readability: Easier to understand the purpose of a record.
Immutability: Promotes safe data handling.
Boilerplate Reduction: Eliminates repetitive code.
Example:
public record Vehicle(String brand, String licensePlate) {
// Records automatically generate constructor, getters, equals(),
hashCode(), toString()
// You can add compact constructor or other methods if needed
public class RecordExample {
public static void main(String[] args) {
Vehicle myVehicle = new Vehicle("Swift", "UP32AB0001");
System.out.println("Brand: " + myVehicle.brand()); // Accessor method
System.out.println("License Plate: " + myVehicle.licensePlate()); //
Accessor method
System.out.println("Vehicle Info: " + myVehicle); // Auto-generated
toString()
// Output:
// Brand: Swift
// License Plate: UP32AB0001
// Vehicle Info: Vehicle[brand=Swift, licensePlate=UP32AB0001]
19. Sealed Classes
Introduced as a preview feature in Java 15, finalized in Java 17, Sealed
Classes and Interfaces allow developers to restrict which other classes
or interfaces can extend or implement them.
Key Concepts:
Introduced: Java 15 (preview), Java 17 (standard).
Purpose: To provide more control over inheritance, allowing a
class/interface to specify exactly which types are permitted to be its
direct subclasses/implementations.
Keywords:sealed: Declares a class or interface as sealed.
permits: Specifies the allowed direct subclasses/implementations.
Rule for Permitted Subclasses: A permitted subclass must
explicitly be declared as final, sealed, or non-sealed.
final: Cannot be extended further.
sealed: Can be extended, but only by its own permitted subclasses.
non-sealed: Can be extended by any class (breaks the sealed
hierarchy at that point).
Benefits:Enhanced Security: Provides more control over type
hierarchies.
Domain Modeling: Useful for modeling well-defined sets of types.
Improved Compile-time Checks: Allows the compiler to perform
exhaustive switch checks with sealed hierarchies.
Use Cases: Often used with Pattern Matching for instanceof (an
upcoming feature not covered in detail here but mentioned as a
future enhancement).
Example:
public sealed class Shape permits Circle, Square, Triangle {
// Common behavior or abstract methods for Shape
public final class Circle extends Shape {
// Circle specific implementation
}
public non-sealed class Square extends Shape {
// Square specific implementation, can be extended by any class
public sealed class Triangle extends Shape permits EquilateralTriangle {
// Triangle specific implementation
public final class EquilateralTriangle extends Triangle {
// EquilateralTriangle specific implementation
This briefing document covers the core concepts and important details of
the new Java features discussed in the provided source. Each section aims
to capture the essence of the topic, its purpose, syntax, and benefits,
along with relevant examples.
convert_to_textConvert to source
Quiz: Short Answer Questions
Answer each question in 2-3
sentences.
1. What is a Functional Interface in Java 8?
2. Explain the primary purpose of a Lambda Expression in Java.
3. How do Method References relate to Lambda Expressions,
and what is their main advantage?
4. Describe the two main types of operations in Java Stream
API and their fundamental difference.
5. What is the core problem that Java's try-with-resources
statement aims to solve, as compared to earlier Java
versions?
6. Define Java Annotation and provide an example of its use.
7. What is the primary benefit of the Java Module System
introduced in Java 9?
8. How did the Diamond Operator enhance code in Java 7, and
what further improvement came in Java 9 regarding
Anonymous Inner Classes?
9. Explain the concept of "Type Inference" in the context of
Java's Local Variable Type Inference (LVTI).
10. What is the key advantage of using the modern Switch
Expression (Java 14) compared to the traditional Switch
Statement?
Answer Key
1. A Functional Interface in Java 8 is an interface that contains exactly
one abstract method. It can have any number of default or static
methods, but the single abstract method is its defining
characteristic, making it suitable for functional programming.
2. The primary purpose of a Lambda Expression is to provide a concise
and clear way to represent an anonymous function, especially for
implementing functional interfaces. It reduces boilerplate code by
allowing direct implementation of the interface's abstract method
without defining a full class.
3. Method References are a compact and easy way to replicate the
functionality of Lambda Expressions, particularly when a lambda
simply refers to an existing method. They provide a shorthand for
lambda expressions that perform a single method call, improving
code readability.
4. The two main types of operations in Java Stream API are
Intermediate Operations and Terminal Operations. Intermediate
operations transform one stream into another (e.g., filter, map),
while terminal operations produce a result or a side-effect and
terminate the stream (e.g., count, forEach).
5. Java's try-with-resources statement aims to solve the problem of
ensuring that resources (like file streams) are properly closed, even
if exceptions occur. Before Java 7, programmers had to explicitly
close resources in a finally block, increasing code complexity and
potential for errors if not handled correctly.
6. Java Annotation is a form of metadata that provides additional
information about a program to the compiler or JVM. It acts like a
tag, offering supplementary details about classes, interfaces,
methods, or fields without directly affecting code execution. An
example is @Override used to indicate a method overrides a
superclass method.
7. The primary benefit of the Java Module System (Java 9) is to enable
developers to create modular applications by structuring the JDK
and user code into distinct modules. This allows for smaller
application sizes by including only required modules, improving
performance, security, and maintainability.
8. The Diamond Operator (Java 7) made it easier to employ generics
by eliminating the need to explicitly mention the type on the right-
hand side of a declaration, reducing redundancy. In Java 9, this was
further enhanced to allow the use of the diamond operator with
anonymous inner classes, which was not permitted in Java 7.
9. Type Inference refers to the automatic detection of a variable's data
type, generally performed at compile time. In Local Variable Type
Inference (LVTI) introduced in Java 10, developers can use the var
keyword instead of explicitly declaring the local variable's type, and
the compiler automatically infers the type.
10. The key advantage of using the modern Switch Expression
(Java 14) is its conciseness and expressiveness compared to the
traditional Switch Statement. It allows multiple case labels with a
single result, uses an arrow (->) instead of a colon and break
statements, and can directly return a value, leading to more
readable and less error-prone code.
Essay Format Questions
1. Discuss the evolution of functional programming support in Java,
focusing on how Functional Interfaces, Lambda Expressions, and
Method References contribute to achieving a functional
programming approach. Provide examples of how these features
work together.
2. Explain the concept of Stream API in Java 8, detailing its purpose,
architecture (source, intermediate operations, terminal operations),
and key advantages for data processing. Compare and contrast at
least three intermediate and two terminal operations with
illustrative examples.
3. Analyze the advancements in resource management and code
readability brought by try-with-resources (Java 7) and Local Variable
Type Inference (Java 10). Describe the problems each feature solves
and provide clear examples of their syntax and benefits.
4. Examine the concept of "modularity" in Java, specifically focusing on
the Java Module System introduced in Java 9. Discuss its motivation,
key components (e.g., module-info.java), and the benefits it offers
for large-scale application development. Outline the steps involved
in creating a Java 9 module.
5. Compare and contrast the traditional Switch Statement with the
modern Switch Expression (Java 14), highlighting the specific
syntactic and semantic improvements of the latter. Explain how the
yield keyword enhances the functionality of Switch Expressions,
providing examples to illustrate the differences.
Glossary of Key Terms
Functional Interface: An interface that contains exactly one
abstract method. It can have any number of default or static
methods.
Lambda Expression: A concise way to represent an anonymous
function. It's often used to implement functional interfaces.
Method Reference: A compact syntax for a lambda expression
that refers to an existing method.
Stream API: A new API introduced in Java 8 to process collections
of objects in a functional style. It supports various operations like
filtering, mapping, and reducing.
Intermediate Operations (Stream API): Operations that
transform a stream into another stream (e.g., filter(), map(),
sorted()).
Terminal Operations (Stream API): Operations that produce a
result or a side-effect and terminate the stream (e.g., forEach(),
count(), collect()).
try-with-resources: A statement in Java 7 that ensures resources
opened in the try block are automatically closed when the block
exits, whether normally or due to an exception.
AutoCloseable: An interface that indicates a resource can be
automatically closed by the try-with-resources statement.
Java Annotation: A form of metadata that provides additional
information about a program to the compiler or JVM. It starts with @.
Type Annotation: An annotation that can be applied to any place
where a type is used (e.g., method return types, type arguments).
Repeating Annotation: An annotation that can be applied more
than once to a single declaration or type use.
Java Module System (Jigsaw): Introduced in Java 9, it allows
developers to break down large applications into smaller,
manageable, and self-contained units called modules.
module-info.java: The module descriptor file that defines a
module, including its name, exported packages, and required
modules.
Anonymous Inner Class: An inner class that is declared and
instantiated in a single expression and does not have a name.
Diamond Operator (<>): Introduced in Java 7, it allows for type
inference when instantiating a generic class, reducing boilerplate
code.
Local Variable Type Inference (LVTI): A feature in Java 10 that
allows the use of the var keyword to declare local variables, letting
the compiler infer the variable's type based on the initializer.
Switch Expression: An enhancement to the switch statement
introduced in Java 14, allowing switch to be used as an expression
that yields a value, reducing verbosity.
yield keyword: Introduced in Java 13, it is used within switch
expressions to yield a value for a case label, providing a clearer way
to return a value and terminate the switch block.
Default Method (Interface): A method defined inside an interface
with the default keyword. It provides a default implementation for
that method, allowing new functionality to be added to interfaces
without breaking existing implementations.
Static Method (Interface): A method defined inside an interface
with the static keyword. These methods belong to the interface itself
and are called directly on the interface name.
Java Base64 Encoding/Decoding: A utility in Java for encoding
binary data into a text format (Base64) and decoding it back,
primarily for safe transmission over networks.
forEach Method: A utility function used to iterate over collections
(like lists, sets, maps) and streams, performing a given action on
each element.
Java Text Blocks: Introduced as a standard feature in Java 15, they
are multi-line string literals that avoid the need for most escape
sequences and allow for easy embedding of multi-line text (like
HTML, JSON, SQL) directly into Java code.
Java Records: A special kind of Java class introduced in Java 16,
designed for concisely defining immutable data carriers. They
automatically generate methods like equals(), hashCode(),
toString(), and accessor methods.
Sealed Classes: Introduced as a preview feature in Java 15 and
standardized in Java 17, they allow a class or interface to control
which other classes or interfaces can extend or implement it,
providing more control over inheritance.
permits keyword: Used with sealed classes or interfaces to
explicitly list the direct subclasses or implementing classes that are
allowed.
Preview Feature: A feature introduced in Java that is not yet
permanent and might change or be removed in future releases. It's
available for developers to try out and provide feedback.
Standard Feature: A feature that has been fully integrated into
Java and is considered stable and permanent.