0% found this document useful (0 votes)
28 views

Unit 3 Java-1

Uploaded by

Akhand Pratap
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
28 views

Unit 3 Java-1

Uploaded by

Akhand Pratap
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 33

UNIT-III OOPJ

1.Interface in Java
Interface
An interface in Java is a blueprint of a class. It has static constants and abstract methods.
The interface in Java is a mechanism to achieve abstraction. There can be only abstract
methods in the Java interface, not method body. It is used to achieve abstraction and multiple
inheritance in Java.
In other words, you can say that interfaces can have abstract methods and variables. It cannot
have a method body.
 Java Interface also represents the IS-A relationship.
 It cannot be instantiated just like the abstract class.
 Since Java 8, we can have default and static methods in an interface.
 Since Java 9, we can have private methods in an interface.
Why use Java interface?
There are mainly three reasons to use interface. They are given below.
 It is used to achieve abstraction.
 By interface, we can support the functionality of multiple inheritance.
 It can be used to achieve loose coupling.

How to declare an interface?


An interface is declared by using the interface keyword. It provides total abstraction; means
all the methods in an interface are declared with the empty body, and all the fields are public,
static and final by default. A class that implements an interface must implement all the
methods declared in the interface.
Syntax:
interface <interface_name> {

// declare constant fields


// declare methods that abstract
// by default.
}

Java 8 Interface Improvement


Since Java 8, the interface can have default and static methods which is discussed later.
Internal addition by the compiler
The Java compiler adds public and abstract keywords before the interface method. Moreover,
it adds public, static and final keywords before data members.
In other words, Interface fields are public, static and final by default, and the methods are
public and abstract.

interface in java
The relationship between classes and interfaces
As shown in the figure given below, a class extends another class, an interface extends
another interface, but a class implements an interface.

The relationship between class and interface

Java Interface Example

In this example, the Printable interface has only one method, and its implementation is
provided in the A6 class.

interface printable{
void print();
}
class A6 implements printable{
public void print(){System.out.println("Hello");}

public static void main(String args[]){


A6 obj = new A6();
obj.print();
}
}

Java Interface Example: Drawable


In this example, the Drawable interface has only one method. Its implementation is provided
by Rectangle and Circle classes. In a real scenario, an interface is defined by someone else,
but its implementation is provided by different implementation providers. Moreover, it is
used by someone else. The implementation part is hidden by the user who uses the interface.

File: TestInterface1.java

//Interface declaration: by first user


interface Drawable{
void draw();
}
//Implementation: by second user
class Rectangle implements Drawable{
public void draw(){System.out.println("drawing rectangle");}
}
class Circle implements Drawable{
public void draw(){System.out.println("drawing circle");}
}
//Using interface: by third user
class TestInterface1{
public static void main(String args[]){
Drawable d=new Circle();//In real scenario, object is provided by method e.g. getDrawable()
d.draw();
}}

Output:

drawing circle

Java 8 Static Method in Interface

Since Java 8, we can have static method in interface. Let's see an example:

File: TestInterfaceStatic.java

interface Drawable{
void draw();
static int cube(int x){return x*x*x;}
}
class Rectangle implements Drawable{
public void draw(){System.out.println("drawing rectangle");}
}

class TestInterfaceStatic{
public static void main(String args[]){
Drawable d=new Rectangle();
d.draw();
System.out.println(Drawable.cube(3));
}}

Output:
drawing rectangle
27

.Difference between abstract class and interface

Abstract class and interface both are used to achieve abstraction where we can declare the
abstract methods. Abstract class and interface both can't be instantiated.
Example of abstract class and interface in Java

Let's see a simple example where we are using interface and abstract class both.

//Creating interface that has 4 methods


interface A{
void a();//bydefault, public and abstract
void b();
void c();
void d();
}

//Creating abstract class that provides the implementation of one method of A interface
abstract class B implements A{
public void c(){System.out.println("I am C");}
}

//Creating subclass of abstract class, now we need to provide the implementation of rest of
the methods
class M extends B{
public void a(){System.out.println("I am a");}
public void b(){System.out.println("I am b");}
public void d(){System.out.println("I am d");}
}

//Creating a test class that calls the methods of A interface


class Test5{
public static void main(String args[]){
A a=new M();
a.a();
a.b();
a.c();
a.d();
}}

Output:

I am a
I am b
I am c
I am d

1. Functional Interfaces in Java

A functional interface is an interface that contains only one abstract method. They can have
only one functionality to display. From Java 8 onwards, lambda expressions can be used to
represent the instance of a functional interface. A functional interface can have any number of
default methods. Runnable, ActionListener, and Comparable are some of the examples of
functional interfaces.

Functional Interface is additionally recognized as Single Abstract Method Interfaces. In short,


they are also known as SAM interfaces. Functional interfaces in Java are the new feature that
provides users with the approach of fundamental programming.

Functional interfaces are included in Java SE 8 with Lambda expressions and Method
references in order to make code more readable, clean, and straightforward. Functional
interfaces are interfaces that ensure that they include precisely only one abstract method.
Functional interfaces are used and executed by representing the interface with an annotation
called @FunctionalInterface. As described earlier, functional interfaces can contain only one
abstract method. However, they can include any quantity of default and static methods.

In Functional interfaces, there is no need to use the abstract keyword as it is optional to use
the abstract keyword because, by default, the method defined inside the interface is abstract
only. We can also call Lambda expressions as the instance of functional interface.

Example 2
@FunctionalInterface
interface sayable{
void say(String msg);
}
public class FIE implements sayable{
public void say(String msg){
System.out.println(msg);
}
public static void main(String[] args) {
FIE fie = new FIE();
fie.say("Hello there");
}
}
Output: Hello there

Example 3
@FunctionalInterface
interface sayable{
void say(String msg); // abstract method
// It can contain any number of Object class methods.
int hashCode();
String toString();
boolean equals(Object obj);
}
public class FunctionalInterfaceExample2 implements sayable{
public void say(String msg){
System.out.println(msg);
}
public static void main(String[] args) {
FunctionalInterfaceExample2 fie = new FunctionalInterfaceExample2();
fie.say("Hello there");
}
}

Some Built-in Java Functional Interfaces


Since Java SE 1.8 onwards, there are many interfaces that are converted into functional
interfaces. All these interfaces are annotated with @FunctionalInterface. These interfaces are
as follows –

Runnable –> This interface only contains the run() method.


Comparable –> This interface only contains the compareTo() method.
ActionListener –> This interface only contains the actionPerformed() method.
Callable –> This interface only contains the call() method.

Java SE 8 included four main kinds of functional interfaces which can be applied in multiple
situations as mentioned below:
Consumer
Predicate
Function
Supplier

2.Lambda Expression in Java


In Java, Lambda expressions basically express instances of functional interfaces (An interface
with a single abstract method is called a functional interface). Lambda Expressions in Java
are the same as lambda functions which are the short block of code that accepts input as
parameters and returns a resultant value. Lambda Expressions are recently included in Java
SE 8.

Functionalities of Lambda Expression in Java


Lambda Expressions implement the only abstract function and therefore implement
functional interfaces lambda expressions are added in Java 8 and provide the below
functionalities.

Enable to treat functionality as a method argument, or code as data.


A function that can be created without belonging to any class.
A lambda expression can be passed around as if it was an object and executed on demand.

Java Lambda Expression Example

// Java program to demonstrate lambda expressions


// to implement a user defined functional interface.

// A sample functional interface (An interface with


// single abstract method
interface FuncInterface
{
// An abstract function
void abstractFun(int x);

// A non-abstract (or default) function


default void normalFun()
{
System.out.println("Hello");
}
}

class Test
{
public static void main(String args[])
{
// lambda expression to implement above
// functional interface. This interface
// by default implements abstractFun()
FuncInterface fobj = (int x)->System.out.println(2*x);

// This calls above lambda expression and prints 10.


fobj.abstractFun(5);
}
}

Output
10

Lambda Expression Syntax


lambda operator -> body
Lambda Expression Parameters
There are three Lambda Expression Parameters are mentioned below:

Zero Parameter
Single Parameter
Multiple Parameters

1. Lambda Expression with Zero parameter

() -> System.out.println("Zero parameter lambda");

2. Lambda Expression with Single parameter

(p) -> System.out.println("One parameter: " + p);

It is not mandatory to use parentheses if the type of that variable can be inferred from the
context

// A Java program to demonstrate simple lambda expressions


import java.util.ArrayList;
class Test {
public static void main(String args[])
{
// Creating an ArrayList with elements
// {1, 2, 3, 4}
ArrayList<Integer> arrL = new ArrayList<Integer>();
arrL.add(1);
arrL.add(2);
arrL.add(3);
arrL.add(4);

// Using lambda expression to print all elements


// of arrL
arrL.forEach(n -> System.out.println(n));

// Using lambda expression to print even elements


// of arrL
arrL.forEach(n -> {
if (n % 2 == 0)
System.out.println(n);
});
}
}

Output : 1,,2 ,3 , 4 2 ,4

3. Lambda Expression with Multiple parameters


(p1, p2) -> System.out.println("Multiple parameters: " + p1 + ", " + p2);
A Java program to demonstrate the working of a lambda expression with two arguments.

// Java program to demonstrate working of lambda expressions


public class Test {
// operation is implemented using lambda expressions
interface FuncInter1 {
int operation(int a, int b);
}

// sayMessage() is implemented using lambda expressions


// above
interface FuncInter2 {
void sayMessage(String message);
}

// Performs FuncInter1's operation on 'a' and 'b'


private int operate(int a, int b, FuncInter1 fobj)
{
return fobj.operation(a, b);
}

public static void main(String args[])


{
// lambda expression for addition for two parameter data type for x and y is optional.
// This expression implements 'FuncInter1' interface
FuncInter1 add = (int x, int y) -> x + y;

// lambda expression multiplication for two parameters This expression also implements
// 'FuncInter1' interface
FuncInter1 multiply = (int x, int y) -> x * y;

// Creating an object of Test to call operate using


// different implementations using lambda
// Expressions
Test tobj = new Test();

// Add two numbers using lambda expression


System.out.println("Addition is + tobj.operate(6, 3, add));

// Multiply two numbers using lambda expression


System.out.println("Multiplication is "+ tobj.operate(6, 3, multiply));

// lambda expression for single parameter


// This expression implements 'FuncInter2' interface
FuncInter2 fobj = message -> System.out.println("Hello " + message);
fobj.sayMessage("Geek");
}
}

Output
Addition is 9
Multiplication is 18
Hello Geek

Note: Lambda expressions are just like functions and they accept parameters just like
functions.
3.Java Method References

Java provides a new feature called method reference in Java 8. Method reference is used to
refer method of functional interface. It is compact and easy form of lambda expression. Each
time when you are using lambda expression to just referring a method, you can replace your
lambda expression with method reference. In this tutorial, we are explaining method
reference concept in detail.

Types of Method References


There are following types of method references in java:
Reference to a static method.
Reference to an instance method.
Reference to a constructor.
1) Reference to a Static Method
You can refer to static method defined in the class. Following is the syntax and example
which describe the process of referring static method in Java.

Syntax
ContainingClass::staticMethodName

Example 1
In the following example, we have defined a functional interface and referring a static
method to it's functional method say().

interface Sayable{
void say();
}
public class MethodReference {
public static void saySomething(){
System.out.println("Hello, this is static method.");
}
public static void main(String[] args) {
// Referring static method
Sayable sayable = MethodReference::saySomething;
// Calling interface method
sayable.say();
}
}
Output:
Hello, this is static method.

Example 2
You can also override static methods by referring methods. In the following example, we
have defined and overloaded three add methods.

import java.util.function.BiFunction;
class Arithmetic{
public static int add(int a, int b){
return a+b;
}
public static float add(int a, float b){
return a+b;
}
public static float add(float a, float b){
return a+b;
}
}
public class MethodReference4 {
public static void main(String[] args) {
BiFunction<Integer, Integer, Integer>adder1 = Arithmetic::add;
BiFunction<Integer, Float, Float>adder2 = Arithmetic::add;
BiFunction<Float, Float, Float>adder3 = Arithmetic::add;
int result1 = adder1.apply(10, 20);
float result2 = adder2.apply(10, 20.0f);
float result3 = adder3.apply(10.0f, 20.0f);
System.out.println(result1);
System.out.println(result2);
System.out.println(result3);
}
}
Output:

30
30.0
30.0

2) Reference to an Instance Method

Like static methods, you can refer instance methods also. In the following example, we are
describing the process of referring the instance method.

Syntax
containingObject::instanceMethodName

Example 1
In the following example, we are referring non-static methods. You can refer methods by
class object and anonymous object.

interface Sayable{
void say();
}
public class InstanceMethodReference {
public void saySomething(){
System.out.println("Hello, this is non-static method.");
}
public static void main(String[] args) {
InstanceMethodReference methodReference = new InstanceMethodReference(); //
Creating object
// Referring non-static method using reference
Sayable sayable = methodReference::saySomething;
// Calling interface method
sayable.say();
// Referring non-static method using anonymous object
Sayable sayable2 = new InstanceMethodReference()::saySomething; // You can use
anonymous object also
// Calling interface method
sayable2.say();
}
}

Output:
Hello, this is non-static method.
Hello, this is non-static method.

3) Reference to a Constructor
You can refer a constructor by using the new keyword. Here, we are referring constructor
with the help of functional interface.

Syntax

ClassName::new

Example

interface Messageable{
Message getMessage(String msg);
}
class Message{
Message(String msg){
System.out.print(msg);
}
}
public class ConstructorReference {
public static void main(String[] args) {
Messageable hello = Message::new;
hello.getMessage("Hello");
}
}

Output:

Hello

4.Stream In Java
Introduced in Java 8, Stream API is used to process collections of objects. A stream in Java is
a sequence of objects that supports various methods which can be pipelined to produce the
desired result.

Use of Stream in Java


There uses of Stream in Java are mentioned below:

Stream API is a way to express and process collections of objects.


Enable us to perform operations like filtering, mapping,reducing and sorting.
How to Create Java Stream?
Java Stream Creation is one of the most basic steps before considering the functionalities of
the Java Stream. Below is the syntax given on how to declare Java Stream.

Syntax
Stream<T> stream;
Here T is either a class, object, or data type depending upon the declaration.

Java Stream Features


The features of Java stream are mentioned below:

A stream is not a data structure instead it takes input from the Collections, Arrays or I/O
channels.
Streams don’t change the original data structure, they only provide the result as per the
pipelined methods.
Each intermediate operation is lazily executed and returns a stream as a result, hence various
intermediate operations can be pipelined. Terminal operations mark the end of the stream and
return the result.
Different Operations On Streams
There are two types of Operations in Streams:
Intermediate Operations
Terminate Operations

Intermediate Operations

Intermediate Operations are the types of operations in which multiple methods are
chained in a row.

Characteristics of Intermediate Operations


Methods are chained together.
Intermediate operations transform a stream into another stream.
It enables the concept of filtering where one method filters data and passes it to another
method after processing.
Benefit of Java Stream
There are some benefits because of which we use Stream in Java as mentioned below:

No Storage
Pipeline of Functions
Laziness
Can be infinite
Can be parallelized
Can be created from collections, arrays, Files Lines, Methods in Stream, IntStream etc.

Important Intermediate Operations


There are a few Intermediate Operations mentioned below:
1. map()
The map method is used to return a stream consisting of the results of applying the given
function to the elements of this stream.

List number = Arrays.asList(2,3,4,5);


List square = number.stream().map(x->x*x).collect(Collectors.toList());
2. filter()
The filter method is used to select elements as per the Predicate passed as an argument.

List names = Arrays.asList("Reflection","Collection","Stream");


List result = names.stream().filter(s->s.startsWith("S")).collect(Collectors.toList());
3. sorted()
The sorted method is used to sort the stream.

List names = Arrays.asList("Reflection","Collection","Stream");


List result = names.stream().sorted().collect(Collectors.toList());
Terminal Operations
Terminal Operations are the type of Operations that return the result. These Operations are
not processed further just return a final result value.

Important Terminal Operations


There are a few Terminal Operations mentioned below:

1. collect()
The collect method is used to return the result of the intermediate operations performed on
the stream.

List number = Arrays.asList(2,3,4,5,3);


Set square = number.stream().map(x->x*x).collect(Collectors.toSet());
2. forEach()
The forEach method is used to iterate through every element of the stream.

List number = Arrays.asList(2,3,4,5);


number.stream().map(x->x*x).forEach(y->System.out.println(y));
3. reduce()
The reduce method is used to reduce the elements of a stream to a single value. The reduce
method takes a BinaryOperator as a parameter.

List number = Arrays.asList(2,3,4,5);


int even = number.stream().filter(x->x%2==0).reduce(0,(ans,i)-> ans+i);
Here ans variable is assigned 0 as the initial value and i is added to it.

Note: Intermediate Operations are running based on the concept of Lazy Evaluation, which
ensures that every method returns a fixed value(Terminal operation) before moving to the
next method.

Example of Java Stream


Java

// Java program to demonstrate


// the use of stream in java
import java.util.*;
import java.util.stream.*;
class Demo {
public static void main(String args[])
{
// create a list of integers
List<Integer> number = Arrays.asList(2, 3, 4, 5);

// demonstration of map method


List<Integer> square
= number.stream()
.map(x -> x * x)
.collect(Collectors.toList());

// create a list of String


List<String> names = Arrays.asList(
"Reflection", "Collection", "Stream");

// demonstration of filter method


List<String> result
= names.stream()
.filter(s -> s.startsWith("S"))
.collect(Collectors.toList());

System.out.println(result);

// demonstration of sorted method


List<String> show
= names.stream()
.sorted()
.collect(Collectors.toList());

System.out.println(show);

// create a list of integers


List<Integer> numbers
= Arrays.asList(2, 3, 4, 5, 2);

// collect method returns a set


Set<Integer> squareSet
= numbers.stream()
.map(x -> x * x)
.collect(Collectors.toSet());

System.out.println(squareSet);

// demonstration of forEach method


number.stream()
.map(x -> x * x)
.forEach(y -> System.out.println(y));

// demonstration of reduce method


int even
= number.stream()
.filter(x -> x % 2 == 0)
.reduce(0, (ans, i) -> ans + i);

System.out.println(even);
}
}
Output

[4, 9, 16, 25]


[Stream]
[Collection, Reflection, Stream]
[16, 4, 9, 25]
4
9
16
25
6

5. Basic Type Base64 Encoding and Decoding in Java

In computer programming, Base64 is a group of binary-to-text encoding schemes that


transforms binary data into a sequence of printable characters, limited to a set of 64 unique
characters. More specifically, the source binary data is taken 6 bits at a time, then this group of
6 bits is mapped to one of 64 unique characters.
As with all binary-to-text encoding schemes, Base64 is designed to carry data stored in binary
formats across channels that only reliably support text content. Base64 is particularly prevalent
on the World Wide Web[1] where one of its uses is the ability to embed image files or other
binary assets inside textual assets such as HTML and CSS files.[2]
Base64 is also widely used for sending e-mail attachments, because SMTP – in its original
form – was designed to transport 7-bit ASCII characters only. Encoding an attachment as
Base64 before sending, and then decoding when received, assures older SMTP servers will not
interfere with the attachment.

Base 64 is an encoding scheme that converts binary data into text format so that encoded
textual data can be easily transported over network un-corrupted and without any data
loss.(Base 64 format reference).
The Basic encoding means no line feeds are added to the output and the output is mapped to a
set of characters in A-Za-z0-9+/ character set and the decoder rejects any character outside of
this set.

Encode simple String into Basic Base 64 format

String BasicBase64format= Base64.getEncoder().encodeToString(“actualString”.getBytes());

Explanation: In above code we called Base64.Encoder using getEncoder() and then get the
encoded string by passing the byte value of actualString in encodeToString() method as
parameter.

Decode Basic Base 64 format to String

byte[] actualByte= Base64.getDecoder().decode(encodedString);


String actualString= new String(actualByte);
Explanation: In above code we called Base64.Decoder using getDecoder() and then decoded
the string passed in decode() method as parameter then convert return value to string.

Below programs illustrate the Encoding and Decoding in Java:

Program 1:Encode simple String into Basic Base 64 format


// Java program to demonstrate
// Encoding simple String into Basic Base 64 format

import java.util.*;
public class GFG {
public static void main(String[] args)
{

// create a sample String to encode


String sample = "India Team will win the Cup";

// print actual String


System.out.println("Sample String:\n" + sample);

// Encode into Base64 format


String BasicBase64format = Base64.getEncoder().encodeToString(sample.getBytes());

// print encoded String


System.out.println("Encoded String:\n" + BasicBase64format);
}
}
Output:
Sample String: India Team will win the Cup
Encoded String: SW5kaWEgVGVhbSB3aWxsIHdpbiB0aGUgQ3Vw

Program 2: Decode Basic Base 64 format to String


// Java program to demonstrate
// Decoding Basic Base 64 format to String

import java.util.*;
public class GFG {
public static void main(String[] args)
{
// create an encoded String to decode
String encode = "SW5kaWEgVGVhbSB3aWxsIHdpbiB0aGUgQ3Vw";
// print encoded String
System.out.println("Encoded String:\n+ encoded);
// decode into String from encoded format
byte[] actualByte = Base64.getDecoder() .decode(encoded);

String actualString = new String(actualByte);


// print actual String
System.out.println("actual String:\n"
+ actualString);
}
}
Output:
Encoded String: SW5kaWEgVGVhbSB3aWxsIHdpbiB0aGUgQ3Vw
actual String: India Team will win the Cup

6. Java forEach loop

Java provides a new method forEach() to iterate the elements. It is defined in Iterable and
Stream interface. It is a default method defined in the Iterable interface. Collection classes
which extends Iterable interface can use forEach loop to iterate elements.
This method takes a single parameter which is a functional interface. So, you can pass
lambda expression as an argument.

forEach() Signature in Iterable Interface


default void forEach(Consumer<super T>action)

Java 8 forEach() example 1

import java.util.ArrayList;
import java.util.List;
public class ForEachExample {
public static void main(String[] args) {
List<String> gamesList = new ArrayList<String>();
gamesList.add("Football");
gamesList.add("Cricket");
gamesList.add("Chess");
gamesList.add("Hocky");
System.out.println("------------Iterating by passing lambda expression--------------");
gamesList.forEach(games -> System.out.println(games));
} }
Output:
------------Iterating by passing lambda expression--------------
Football Cricket Chess Hocky

Java 8 forEach() example 2

import java.util.ArrayList;
import java.util.List;
public class ForEachExample {
public static void main(String[] args) {
List<String> gamesList = new ArrayList<String>();
gamesList.add("Football");
gamesList.add("Cricket");
gamesList.add("Chess");
gamesList.add("Hocky");
System.out.println("------------Iterating by passing method reference---------------");
gamesList.forEach(System.out::println);
} }
Output:
------------Iterating by passing method reference---------------
Football Cricket Chess Hocky

7.Try-with-resources Feature in Java


In Java, the Try-with-resources statement is a try statement that declares one or more
resources in it. A resource is an object that must be closed once your program is done using
it. For example, a File resource or a Socket connection resource. The try-with-resources
statement ensures that each resource is closed at the end of the statement execution. If we
don’t close the resources, it may constitute a resource leak and also the program could
exhaust the resources available to it.

You can pass any object as a resource that implements java.lang.AutoCloseable, which
includes all objects which implement java.io.Closeable.

By this, now we don’t need to add an extra finally block for just passing the closing
statements of the resources. The resources will be closed as soon as the try-catch block is
executed.

Syntax: Try-with-resources

try(declare resources here) {


// use resources
}
catch(FileNotFoundException e) {
// exception handling
}

Exceptions:

When it comes to exceptions, there is a difference in try-catch-finally block and try-with-


resources block. If an exception is thrown in both try block and finally block, the method
returns the exception thrown in finally block.
For try-with-resources, if an exception is thrown in a try block and in a try-with-resources
statement, then the method returns the exception thrown in the try block. The exceptions
thrown by try-with-resources are suppressed, i.e. we can say that try-with-resources block
throws suppressed exceptions.

Now, let us discuss both the possible scenarios which are demonstrated below as an example
as follows:
Case 1: Single resource
Case 2: Multiple resources

Example 1: try-with-resources having a single resource

// Java Program for try-with-resources


// having single resource

// Importing all input output classes


import java.io.*;

// Class
class GFG {

// Main driver method


public static void main(String[] args)
{
// Try block to check for exceptions
try (

// Creating an object of FileOutputStream


// to write stream or raw data

// Adding resource
FileOutputStream fos = new FileOutputStream("gfgtextfile.txt")) {

// Custom string input


String text = "Hello World. This is my java program";

// Converting string to bytes


byte arr[] = text.getBytes();

// Text written in the file


fos.write(arr);
}

// Catch block to handle exceptions


catch (Exception e) {

// Display message for the occurred exception


System.out.println(e);
}

// Display message for successful execution of


// program
System.out.println(
"Resource are closed and message has been written into the gfgtextfile.txt");
}
}
Output:

Resource are closed and message has been written into the gfgtextfile.txt

8.Java Type and Repeating Annotations

Java Type Annotations


Java 8 has included two new features repeating and type annotations in its prior annotations
topic. In early Java versions, you can apply annotations only to declarations. After releasing
of Java SE 8 , annotations can be applied to any type use. It means that annotations can be
used anywhere you use a type. For example, if you want to avoid NullPointerException in
your code, you can declare a string variable like this:

@NonNull String str;


Following are the examples of type annotations:

@NonNull List<String>
List<@NonNull String> str
Arrays<@NonNegative Integer> sort
@Encrypted File file
@Open Connection connection
void divideInteger(int a, int b) throws @ZeroDivisor ArithmeticException
Note - Java created type annotations to support improved analysis of Java programs. It
supports way of ensuring stronger type checking.

Java Repeating Annotations


In Java 8 release, Java allows you to repeating annotations in your source code. It is helpful
when you want to reuse annotation for the same class. You can repeat an annotation anywhere
that you would use a standard annotation.

For compatibility reasons, repeating annotations are stored in a container annotation that is
automatically generated by the Java compiler. In order for the compiler to do this, two
declarations are required in your code.
1. Declare a repeatable annotation type
2. Declare the containing annotation type

1) Declare a repeatable annotation type


Declaring of repeatable annotation type must be marked with the @Repeatable meta-
annotation. In the following example, we have defined a custom @Game repeatable
annotation type.

@Repeatable(Games.class)
@interfaceGame{
String name();
String day();
}
The value of the @Repeatable meta-annotation, in parentheses, is the type of the container
annotation that the Java compiler generates to store repeating annotations. In the following
example, the containing annotation type is Games. So, repeating @Game annotations is
stored in an @Games annotation.

2) Declare the containing annotation type


Containing annotation type must have a value element with an array type. The component
type of the array type must be the repeatable annotation type. In the following example, we
are declaring Games containing annotation type:

@interfaceGames{
Game[] value();
}
Note - Compiler will throw a compile-time error, if you apply the same annotation to a
declaration without first declaring it as repeatable.

Java Repeating Annotations Example


// Importing required packages for repeating annotation

import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
// Declaring repeatable annotation type
@Repeatable(Games.class)
@interfaceGame{
String name();
String day();
}
// Declaring container for repeatable annotation type
@Retention(RetentionPolicy.RUNTIME)
@interfaceGames{
Game[] value();
}
// Repeating annotation
@Game(name = "Cricket", day = "Sunday")
@Game(name = "Hockey", day = "Friday")
@Game(name = "Football", day = "Saturday")
public class RepeatingAnnotationsExample {
public static void main(String[] args) {
// Getting annotation by type into an array
Game[] game =
RepeatingAnnotationsExample.class.getAnnotationsByType(Game.class);
for (Gamegame2 : game) { // Iterating values
System.out.println(game2.name()+" on "+game2.day());
}
}
}
OUTPUT:

Cricket on Sunday
Hockey on Friday
Football on Saturday

9.Java 9 Module System

Java Module System is a major change in Java 9 version. Java added this feature to collect
Java packages and code into a single unit called module.

In earlier versions of Java, there was no concept of module to create modular Java
applications, that why size of application increased and difficult to move around. Even JDK
itself was too heavy in size, in Java 8, rt.jar file size is around 64MB.

To deal with situation, Java 9 restructured JDK into set of modules so that we can use only
required module for our project.

Apart from JDK, Java also allows us to create our own modules so that we can develop
module based application.
The module system includes various tools and options that are given below.
o Includes various options to the Java tools javac, jlink and java where we can specify
module paths that locates to the location of module.
o Modular JAR file is introduced. This JAR contains module-info.class file in its root
folder.
o JMOD format is introduced, which is a packaging format similar to JAR except it can
include native code and configuration files.
o The JDK and JRE both are reconstructed to accommodate modules. It improves
performance, security and maintainability.
o Java defines a new URI scheme for naming modules, classes and resources.

Java 9 Modularized JDK

Module is a collection of Java programs or softwares. To describe a module, a Java


file module-info.java is required. This file also known as module descriptor and defines the
following

o Module name
o What does it export
o What does it require

Module Name

It is a name of module and should follow the reverse-domain-pattern. Like we name packages,
e.g. com.javatpoint.

How to create Java module

Creating Java module required the following steps.

o Create a directory structure


o Create a module declarator
o Java source code

Create a Directory Structure

To create module, it is recommended to follow given directory structure, it is same as reverse-


domain-pattern, we do to create packages / project-structure in Java.

Note: The name of the directory containing a module's sources should be equal to the name of
the module, e.g. com.javatpoint.
Create a file module-info.java, inside this file, declare a module by using module identifier
and provide module name same as the directory name that contains it. In our case, our directory
name is com.javatpoint.

ADVERTISEMENT

1. module com.javatpoint{
2.
3. }

Leave module body empty, if it does not has any module dependency. Save this file
inside src/com.javatpoint with module-info.java name.

Java Source Code

Now, create a Java file to compile and execute module. In our example, we have
a Hello.java file that contains the following code.

1. class Hello{
2. public static void main(String[] args){
3. System.out.println("Hello from the Java module");
4. }
5. }

Save this file inside src/com.javatpoint/com/javatpoint/ with Hello.java name.

Compile Java Module

To compile the module use the following command.

1. javac -d mods --module-source-path src/ --module com.javatpoint

After compiling, it will create a new directory that contains the following structure.

Now, we have a compiled module that can be just run.


Run Module

To run the compiled module, use the following command.

1. java --module-path mods/ --module com.javatpoint/com.javatpoint.Hello

Output:

Hello from the Java module

Well, we have successfully created, compiled and executed Java module.

Look inside compiled Module Descriptor

To see the compiled module descriptor use the following command.

1. javap mods/com.javatpoint/module-info.class

This command will show the following code to the console.

1. Compiled from "module-info.java"


2. module com.javatpoint {
3. requires java.base;
4. }

See, we created an empty module but it contains a java.base module. Why? Because all Java
modules are linked to java.base module and it is default module.

10.Diamond syntax with inner anonymous class

Diamond Operator: Diamond operator was introduced in Java 7 as a new feature.The


main purpose of the diamond operator is to simplify the use of generics when creating an
object. It avoids unchecked warnings in a program and makes the program more readable.
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 improves
readability. Before JDK 7, we have to create an object with Generic type on both side of the
expression like:

// Here we mentioned the generic type


// on both side of expression while creating object
List<String> geeks = new ArrayList<String>();
When Diamond operator was introduced in Java 7, we can create the object without
mentioning generic type on right side of expression like:

List<String> geeks = new ArrayList<>();

Problem with Diamond Operator in JDK 7?

With the help of Diamond operator, we can create an object without mentioning the generic
type on the right hand side of the expression. But the problem is it will only work with
normal classes. Suppose you want to use the diamond operator for anonymous inner class
then compiler will throw error message like below:

// Program to illustrate the problem


// while linking diamond operator
// with an anonymous inner class

abstract class Geeksforgeeks<T> {


abstract T add(T num1, T num2);
}

public class Geeks {


public static void main(String[] args)
{
Geeksforgeeks<Integer> obj = new Geeksforgeeks<>() {
Integer add(Integer n1, Integer n2)
{
return (n1 + n2);
}
};
Integer result = obj.add(10, 20);
System.out.println("Addition of two numbers: " + result);
}
}

Output:
prog.java:9: error: cannot infer type arguments for Geeksforgeeks
Geeksforgeeks obj = new Geeksforgeeks () {
^
reason: cannot use '' with anonymous inner classes
where T is a type-variable:
T extends Object declared in class Geeksforgeeks
1 error
Java developer extended the feature of the diamond operator in JDK 9 by allowing the
diamond operator to be used with anonymous inner classes too. If we run the above code
with JDK 9, then code will run fine and we will generate the below output.
Output:
Addition of two numbers: 30

11.Enhancements for Switch Statement in Java

Java 12 improved the traditional switch statement and made it more useful. Java 13 further
introduced new features. Before going into the details of new features, let’s have a look at
the drawbacks faced by the traditional Switch statement.

Problems in Traditional Switch

1. Default fall through due to missing break:


The default fall-through behavior is error-prone. Let’s understand it with an example.
switch (itemCode) {
case 001 :
System.out.println("It's a laptop!");
break;
case 002 :
System.out.println("It's a desktop!");
break;
case 003 :
System.out.println("It's a mobile phone!");
break;
default :
System.out.println("Unknown device!");
}

The above code works by matching the corresponding case and executing the particular code
block. As long as you provide the necessary break statements, it works fine.

But what happens if we forget any of the required break statements:


switch (itemCode) {
case 001 :
System.out.println("It's a laptop!");
// missed out break here
case 002 :
System.out.println("It's a desktop!");

}
Here, if we pass 001, the first case matches, and the code block executes. But due to missing
break, execution falls through and continues for case 002. We get the following wrong
output:
It's a laptop!
It's a desktop!
Clearly, this is not the intended output. It is a result of accidentally missing out break
statements.
2. Multiple values per case not supported:
There may be situations where similar processing is required for multiple case values. But
the traditional switch makes to follow the fall through behaviors.
case 001:
case 002:
case 003:
System.out.println("It's an electronic gadget!");
Much improved switch accepts multiple values per case.

12.Upgraded Switch in Java 13

Enhancements to switch statements were introduced by Java 12 and then further modified by
Java 13. Let’s dive into the important features of this improved version of the Switch
statement.
1. Supports multiple values per case:
With multiple values being specified per case, it simplifies the code structure and eliminates
the need for using fall through.
The values need to be separated by commas and break should follow the case block.
switch (itemCode) {
case 001, 002, 003 :
System.out.println("It's an electronic gadget!");
break;

case 004, 005:


System.out.println("It's a mechanical device!");
}
2. yield is used to return a value:
A new keyword yield has been introduced. It returns values from a switch branch only.
We don’t need a break after yield as it automatically terminates the switch expression.
int val = switch (code) {
case "x", "y" :
yield 1;
case "z", "w" :
yield 2;
};
3. Switch can be used as an expression:
The switch can now be used as an expression. This means the switch can now return
values based on our input. There is a slight change in switch syntax to accommodate this
change. A switch block needs to be delimited by a semicolon. The yield keyword is used to
return values. No break required with the yield statement.
Let’s have a look at a code snippet to understand these changes better.
String text = switch (itemCode) {
case 001 :
yield "It's a laptop!";
case 002 :
yield "It's a desktop!";
case 003 :
yield "It's a mobile phone!";
default :
throw new IllegalArgumentException(itemCode + "is an unknown device!");
}; // switch block ended by semicolon
4.

13.Yield Keyword

In Java, the yield keyword was introduced as part of the enhancement of the switch
expression in Java 12 (as a preview feature) and then fully integrated in Java 14. It allows for
a value to be returned from a switch expression, making the switch statement more flexible
and expressive.
Understanding yield in Switch Expressions

Prior to Java 12, switch statements were primarily used for control flow, but they couldn't
directly return values. With the introduction of switch expressions, you can now use switch
statements to produce values.

Example with yield

Here's an example that demonstrates the use of yield in a switch expression:

public class YieldExample {


public static void main(String[] args) {
int day = 3;
String dayType = switch (day) {
case 1, 2, 3, 4, 5 -> "Weekday";
case 6, 7 -> "Weekend";
default -> throw new IllegalArgumentException("Invalid day: " + day);
};

System.out.println("Day type: " + dayType);


}
}

In the example above, the switch expression assigns a value to the variable dayType. It uses
the arrow (->) syntax for cases and does not require the yield keyword because it returns a
simple value.

However, when you have more complex logic within a case, you can use yield to return a
value from that case:

public class YieldExample {


public static void main(String[] args) {
int day = 3;
String dayType = switch (day) {
case 1, 2, 3, 4, 5 -> {
System.out.println("It's a weekday");
yield "Weekday";
}
case 6, 7 -> {
System.out.println("It's the weekend");
yield "Weekend";
}
default -> throw new IllegalArgumentException("Invalid day: " + day);
};

System.out.println("Day type: " + dayType);


}
}

Explanation
1. Switch Expression: The switch statement in this example is used as an expression
that returns a value.
2. Arrow Syntax: The -> arrow syntax simplifies returning a single value.
3. Yield Statement: When more complex logic is required inside a case, the yield
statement is used to return a value from the block. This is particularly useful when the
case block contains multiple statements.

Benefits of yield in Switch Expressions

 Clarity and Conciseness: The code is clearer and more concise compared to
traditional switch statements.
 Expressions Over Statements: Allows switch to be used as an expression, making it
more powerful and flexible.
 Less Error-Prone: Reduces common errors associated with traditional switch
statements, such as fall-through.

14.Text Blocks in Java 15




In this article, we are going to discuss the Java 15 text blocks feature to declare multi-line
strings most efficiently. We all know that how we can
declare multi-line strings and that too quite easily with the help of concatenation, string’s join
method, StringBuilder append method, etc. Now the question will arise in our mind that if
everything is this simple then what is the need for a new feature i.e. text blocks to declare
multi-line strings. So let’s first try to understand the problem and then we will understand the
need for a text block.
In earlier releases of the JDK, embedding multi-line code snippets required a tangled mess of
explicit line terminators, string concatenations, and delimiters. Text blocks eliminate most of
these obstructions, allowing you to embed code snippets and text sequences more or less as-
is.
A text block is an alternative form of Java string representation that can be used anywhere a
traditional double-quoted string literal can be used. Text blocks begin with a “”” (3 double-
quote marks) observed through non-obligatory whitespaces and a newline. For example:
 Java

// Using a literal string


String text1 = "Geeks For Geeks";

// Using a text block


String text2 = """
Geeks For Geeks""";

Note: Since Java 15, text blocks are available as a standard feature. With Java 13 and 14, we
needed to enable it as a preview feature.
The object created from text blocks is java.lang.String with the same properties as a regular
string enclosed in double quotes. This includes the presentation of objects and the interning.
Continue with text1 and text2 from the previous examples,
 Java
// Both text1 and text2 are strings of equal value
text1.equals(text2) // true

// Both text1 and text2 intern to the same string


text1 == text2 // true

Text blocks may be utilized in the region of a string literal to enhance the clarity and
readability of the code. This typically takes place whilst a string literal is used to symbolize a
multi-line string. In this example there’s a substantial muddle from citation marks, newline
escapes, and concatenation operators:
 Java

// ORIGINAL
String message = "A-143, 9th Floor, Sovereign Corporate Tower,\n" +
"Sector-136, Noida,\n" +
"Uttar Pradesh - 201305";

// BETTER : Using text blocks


// gets rid of lots of the clutter
String message = """
A-143, 9th Floor, Sovereign Corporate Tower,
Sector-136, Noida,
Uttar Pradesh - 201305""";

15.Sealed Classes

Sealed classes and interfaces were introduced in Java 15 as a preview feature and became a
standard feature in Java 17. Sealed classes and interfaces allow you to restrict which other
classes or interfaces can extend or implement them. This helps to model your domain more
precisely and enhances maintainability and security by controlling the inheritance hierarchy.

Key Features of Sealed Classes

1. Restricting Inheritance: Sealed classes enable you to define a fixed set of


subclasses, ensuring that no other classes can extend your sealed class without your
explicit permission.
2. Exhaustiveness in Switch Expressions: When used with pattern matching and
switch expressions, sealed classes help the compiler ensure all possible cases are
covered.

Syntax

Sealed classes and interfaces are defined using the sealed keyword followed by the permits
clause, which lists the permitted subclasses.

Example of Sealed Class

Here's an example demonstrating how to define and use a sealed class:


public sealed class Shape permits Circle, Rectangle, Square {
// common methods and fields for shapes
}

public final class Circle extends Shape {


private final double radius;

public Circle(double radius) {


this.radius = radius;
}

public double getRadius() {


return radius;
}

// other methods specific to Circle


}

public final class Rectangle extends Shape {


private final double width;
private final double height;

public Rectangle(double width, double height) {


this.width = width;
this.height = height;
}

public double getWidth() {


return width;
}

public double getHeight() {


return height;
}

// other methods specific to Rectangle


}

public final class Square extends Shape {


private final double side;

public Square(double side) {


this.side = side;
}

public double getSide() {


return side;
}

// other methods specific to Square


}

Explanation
1. Sealed Class: The Shape class is declared as sealed and specifies the subclasses
Circle, Rectangle, and Square using the permits clause.
2. Final Subclasses: The permitted subclasses are declared as final, meaning they
cannot be extended further. Alternatively, subclasses can be non-sealed, allowing
further subclassing, or sealed themselves.

Permitted Subclass Modifiers

 final: The subclass cannot be extended further.


 sealed: The subclass can be sealed, allowing specific further subclasses.
 non-sealed: The subclass removes the sealing restriction, allowing any class to extend
it.

Benefits of Sealed Classes

1. Control Over Inheritance: Ensures that the class hierarchy remains controlled and
predictable.
2. Exhaustiveness Checking: Helps the compiler perform exhaustive checking, which
is useful for pattern matching and switch expressions.
3. Enhanced Design: Provides a clear and well-defined class hierarchy, improving the
design and maintenance of APIs and libraries.

Use Cases for Sealed Classes

 Defining Fixed Sets of Related Classes: When you have a fixed set of related classes
and want to ensure no other classes can extend your base class.
 Enum-Like Structures: When you need more complex behaviour than enums but
still want to restrict the set of possible types.
 Exhaustive Pattern Matching: In combination with pattern matching and switch
expressions, sealed classes allow for exhaustive checks at compile time.

Conclusion

Sealed classes in Java provide a powerful way to control the inheritance hierarchy of your
classes and interfaces. They allow you to define a restricted set of subclasses, ensuring better
design, enhanced security, and more predictable code behaviour. By leveraging sealed
classes, you can create more robust and maintainable applications.

You might also like