Post

Java Handbook

Java Handbook

Java is a high-level, class-based, object-oriented programming language. It is a compiled language.

Java is consider as fully object oriented. It seems controversial topic because there are diversified thoughts on this like Java supports Primitive data type hence it’s not considered as fully object oriented language.

Java program is WORA (Write once run anywhere).

How Java works?

JavaIntro-modified

When Java program compiles by javac compiler the Java code converted into byte code that is then understood by the JVM and then byte code is converted into machine code.

JDKJREJVM

JVM (Java Virtual Machine)

Java Virtual Machine is platform dependent that means it needs to be install on every machine required to run Java Program. while Java Program is Java Independent that means Java Program developed on one machine can execute on all the Machines which has JVM Install.

JVM is used to convert byte code to machine code.

JVM consist of JIT (Just-In-Time) which is used to improve the performance by compiling the byte code to machine code.

JRE (Java Runtime Environment)

Java Runtime Environment consists of collection of library and other components for Java Program.

It provide wide range of library to reuse the code already written.

JDK (Java Development Kit)

When we download the JDK it gives JRE & JVM. It include couple of components like compiler, a debugger and other tools to develop Java Program.


Java is strongly type language that means we need to specify the type of variables while declaring in Java Code.

Variables in Java

Primitive Types : Primitive data types specify the size and type of variable values. They are the building blocks of data manipulation and cannot be further divided into simpler data types.

Non-Primitive Types : Non-primitive data types or reference data types refer to instances or objects.

Primitive TypesNon-Primitive Types
IntegerArray
FloatClass
CharacterInterfaces
BooleanStrings
 Enums

Integers

IntegersSize
int4 bytes
long8 bytes
short2 byte
byte1 byte

Floats

FloatsSize
float4 bytes
double8 bytes

Others

OthersSize
characters2 bytes
boolean1 bit

Literals in Java

Literals are specific representation of the values in Java. Such as…

1
2
int money = 10_00_000; // refers to 10M or 1000000
boolean value = 1; // error

Type Casting and Conversion

Type Casting is the process of converting type of the from one data type to another data type.

Types of Type Casting in Java

Widening Type Casting: It refers to converting lower data type to higher data types. It is also refers as Implicit Type Casting.

1
2
short smallValue = 10;
int largeValue = smallValue;

In above example short is getting type casted in int, because int is higher data type (4 bytes) and short is lower data type (2 bytes).


Narrowing Type Casting : It refers to converting higher data types to lower data types. It is also refers as Explicit Type Casting.

1
2
int largeValue = 257;
byte smallValue = (byte) largeValue; // not a good idea

It’s never recommended to use Narrowing Type Casting because it may leads to data loss. Suppose in above example largeValue has value 99000 then it’s anyway not possible to fit in smallValue then we’ll lose the value.

smallValue will have value = 257 (largeValue) % 256 (Range of byte) = 1


Type Promotion

1
2
3
byte a = 10;
byte b = 30;
int output = a * b; // answer of a x b exceed range of the byte.

Because result of the expression a * b exceeds the range of byte the type is promoted as int.

Conditional Statements and Looping

if, else & else if

if, else & else if is used to run the code based on some condition.

1
2
3
4
5
6
7
8
int age = 19;
if (age >= 18) {
    System.out.println("You can Drive!");
} else if (age < 18) {
    System.out.println("You can't Drive!");
} else {
    System.out.println("Invalid Age!");
}

if first condition matches then It’ll print the message "You can Drive!". else second condition matches then It’ll print the message "You can't Drive!". If none of above condition matches It’ll print the message "Invalid Age!".

While, Do-While, For & ForEach Loop

Loops are used to run code iteratively. You’re required to provide base condition that define when to exits the loop. Otherwise there will be infinite looping and eventually leads to segmentation fault.

While Loop

1
2
3
4
5
int age = 0;
while(age != 18) {
    age++;
}
System.out.println("Your age is 18!");

Do-While Loop

1
2
3
4
5
int age = 0;
do {
    age++;
} while(age != 18);
System.out.println("Your age is 18!");

No matter what, Do-While Loop at least execute once. because it first run the code once and then check condition and run the code again until condition is unsatisfied.

For Loop

1
2
3
4
5
int age = 0;
for(int i = 0; i < 18; i++) {
    age++;
}
System.out.println("Your age is 18!");

Syntax for For loop consist of three parts, initialization; condition; increment/decrement.

ForEach Loop

1
2
3
4
5
int [] array = { 10, 20, 30, 40, 50 };

for(int value : array ) {
   System.out.println(value);
}

ForEach loop generally used with array or list. It will pickup elements from array one by one util it reaches the end of the loop.

Classes and Objects

Class is blueprint of objects. Class contains member variables & methods. We tends to wrap the entity with their attributes and behavior together in form of class.

e.g. Car which has attributes as gear, steering, engine, tires etc. and Behavior as moving steering left and right, changing gear, rotating wheels etc.

1
2
3
4
5
6
7
8
9
10
11
12
/* Calculator.java */

class Calculator {
    // Instance Variables
    int number1;
    int number2;

    // method
    public int add() {
        return a + b;
    }
}
1
2
3
4
5
6
7
8
/* Main.java */
public static void main(String [] args){
    Calculator cal = new Calculator();
    cal.number1 = 10;
    cal.number2 = 20;
    int result = cal.add();
    System.out.println(result);
}

We created object cal of class Calculator and assign values to the member variables and run the method add of the class Calculator.

How classes & objects works internally.

JavaInternal-modified

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Calc {
    int num = 5;
    public int add(int n1, int n2){
        return n1 + n2;
    }
}

class Main {
    public static void main(String[] args) {
        int data = 10;
        Calc obj = new Calc();
        int r1 = obj.add(3, 2);
        System.out.println(r1);
    }
}

All the Objects and Collection will be stored in Heap Memory, Other than that will be stored in Stack Memory.

When Object is created it makes entry in Stack with address of object available in Heap Memory.

Method Overloading

It refers to the term which says “Same name different behavior”. We can define multiple method with same name but It mush have different number of arguments.

1
2
3
4
5
6
7
8
9
class Calc {
    public int add(int a, int b) {
        return a + b;
    }

    public int add(int a, int b, int c) {
        return a + b + c;
    }
}

Whenever we pass 2 arguments, It will automatically call fist method. and When we pass 3 arguments, It will call second method.

It determines which one to call only with the parameters.

Constructor

It is a special method called when Object it created. It must have the name exactly same as Class Name. We can overload the constructor too (Based on Parameters) just like Method Overloading.

1
2
3
4
5
6
7
8
9
10
11
12
13
//default constructor
public class Human{
    public Human() {
        /* ...body... */
    }
}

// parameterized constructor
public class Human {
    public Human(int totalHands, int totalLegs){
        /* ...body... */
    }
}

Generally, We see this keyword in the constructor that eventually means the current class. eg. this.totalHands then It’s pointing to current class member variable.

Array

There are different types of arrays.

  • Single Dimensional Array
  • Multi Dimensional Array
  • Jagged Array

Single Dimensional Array

1
2
3
4
5
6
7
int num[] = {2, 3, 4, 5};
int [] num = new int[5];

// foreach loop
for(int item: num){
    /* ...operation... */
}

Multi Dimensional Array

1
2
3
4
5
6
7
8
// row = 3, column = 4
int num[][] = new num[3][4];

for(int n[]: num) {
    for(int m: n) {
        /* ...body... */
    }
}

Jagged Array

Jagged array where in each row columns are not fixed.

1
2
3
4
int num[][] = new int [3][];
num[0] = int [4];
num[1] = int [3];
num[2] = int [2];

String

The Behavior of String is not normal compare to other programming languages.

There are 2 types of Strings in Java.

  • Mutable String (Can Change)
  • Immutable String (Can’t Change)

Immutable String

1
2
String name = new String("Om Jogani");
System.out.println(name);

It creates object name in Heap Memory. Above String is not changeable but If you try to change the String, There won’t be any problem.

It gives and illusion of changing the String but Behind the Scene It pulls out String data and assign to new String and return new String instead of changing the existing String.

Now, Question is What are the alternative of Immutable String?

String Buffer

1
StringBuffer sb = new StringBuffer();

Default sb Size = 16 bytes
If we assign sb = “hello” then size of sb would be 16 + 5 = 21 bytes.

There are many methods available on StringBuffer like append, insert, ensureCapacity etc.

ensureCapacity is used to set initial capacity.

StringBuilder

1
StringBuilder sb = new StringBuilder();

StringBuilder is same as StringBuffer but StringBuilder is thread safe while StringBuffer is not thread safe.

Static

We can use static keyword with variables, functions and as a Block.

If We use static variables then It will be alive until execution of the Program ends that means We can have common variables across multiple objects.

If We use static methods then It’s possible to call the method without Object.

Static methods can be called using ClassName only. eg. ClassName.method();

Static Block only called once despite creating 100s of Objects. Static Block is generally used to assign the value of the static variable.

It runs before constructor because It gets executed When Class is getting loaded.

JVM has Class Loader which loads the class first and then Object gets initiated.

Load Class

1
Class.forname("ClassName");

It won’t create Object of Class rather It only loads the class. While Static Block runs when class load It will execute the Static Block reside in in that Class.

Static Methods only accepts Static variables. It doesn’t accept non-static variables.

main method is static so that It doesn’t required to have object to call it.

Example of Static Method

1
2
3
public static void greetings() {
    System.out.println("Hello World");
}

Object Oriented Programming Concepts

Encapsulations

As real Capsule has outer cover and inside that It has Medicine that is not exposed directly.Based on the same concept, We want some variables to not accessed or modify directly by anyone.

So, We provide getters & setters for that and make that variable private. It also helps to perform some extra steps in setter method.

Inheritance

Inheritance is used to derive the properties of one class to the another class. It provides code reusability.

We use extends keyword to extend the functionality of current class with inherited class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Vehicle {
    public void move() {
        /* ...body... */
    }

    public void details(String model) {
        /* ...body... */
    }
}

class Bike extends Vehicle {
    public void bikeDetails(String model) {
        details(model);
    }
}

There are several types of Inheritance available in Java.

inheritance1

Polymorphism

There are 2 Types of Polymorphism.

  • Compiler Time Polymorphism
  • Run Time Polymorphism

In Compiler Time Polymorphism, Behavior decided at compile time. eg. Method Overloading. In Run Time Polymorphism, Behavior decided at run time. eg. Method Overriding.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Vehicle {
    public void move() {
        System.out.println("Move");
    }
}

class Bike extends Vehicle{
    public void move() {
        System.out.println("Bike move");
    }s
}
class Main {
    public static void main(String[] args) {
        Bike bike = new Vehicle();
        bike.move(); // call `move` of Vehicle Class
        bike = new Bike();
        bike.move(); // call `move` of Bike Class
    }
}

We can also reassign the object as shown above and it’s also called dynamic method dispatch.

Anonymous Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Vehicle {
    public void move() {
        System.out.println("Move");
    }
}

class Bike {
    public void details() {
        System.out.println("Bike Details");
    }
}
class Main {
    public static void main(String[] args) {
        Bike bike = new Bike();
        bike.details();
    }
}

Above code can be converted to Anonymous Class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Vehicle {
    public void move() {
        System.out.println("Move");
    }
}

class Main {
    public static void main(String[] args) {
        Bike bike = new Bike() {
            public void details() {
                System.out.println("Bike Details");
            }
        }
    }
}

Anonymous Object

1
new Calc();

It only called constructor of the class Calc. It cannot be reuse.

1
new Calc().show();

Method Overriding

While we have Single Inheritance we can override the method in the sub class Which is extended from super class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Vehicle {
    public void move() {
        System.out.println("Move");
    }
}

class Bike {
    public void move() {
        System.out.println("Override Method");
    }
}
class Main {
    public static void main(String[] args) {
        Bike bike = new Bike();
        bike.move();
    }
}

Packages

Combine Multiple Classes to Package.

  • tools
    • Calc
    • AdvanceCalc
  • database
    • Connections
    • Operations

Here tools & database are packages & Calc, AdvanceCalc, Connection, Operations are Classes.

  • tools
    • ui
      • login
      • home

Here tools is a package, ui is sub-package & login & home are Classes.

1
import tools.*;

We can import everything from tools package with above lines.

Access Modifier Table

 PrivateProtectedPublicDefault
Same ClassYESYESYESYES
Same Package Sub-ClassNOYESYESYES
Same Package Non-SubClassNOYESYESYES
Different Package Sub-ClassNOYESYESNO
Different Package Non-SubClassNONOYESNO

Final Keyword

Final keyword can be used with Variables, Functions as well as Classes.

If We use final with Variables then that Variable will become constant.

1
final double PI = 3.14;

If We use final with Methods then It will prevent method overriding.

1
2
3
public final void display() {
    System.out.println("I can't Override!");
}

If We use final with Classes then It will prevent Inheritance.

1
2
3
final class MyClass {
    /* ...body... */
}

If we try to print the Object it will print ClassName@HashCode. Because the default implementation of the toString() method consist of ClassName@HashCode.

It is possible to customize the default behavior of toString() by overriding toString() method.

We have equal method to Override and have checks based on values. By default it compare with HashCode.

UpCasting & DownCasting

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Vehicle {
    public void move() {
        System.out.println("Move");
    }
}

class Bike {
    public void details() {
        System.out.println("Bike Details");
    }
}
class Main {
    public static void main(String[] args) {
        Vehicle vehicle = (Vehicle) new Bike(); // UpCasting
        Bike bike = (Bike) vehicle; // DownCasting
    }
}

Abstract

Abstract Method is used to just declare the method. Abstract method is only used in abstract class. We can’t create object of abstract class.

1
2
3
4
5
6
7
8
9
abstract class Vehicle {
    public abstract void show();
}

class Car extends Vehicle {
    public void show() {
        System.out.println("Show Method");
    }
}

Inner Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Transport {
    int num;
    public void show() {
        System.out.println("Transport Show");
    }

    // inner class
    class Vehicle {
        public void details() {
            System.out.println("Details");
        }
    }
}

class Main {
    public static void main(String[] args) {
        Transport trans = new Transport();
        trans.show();
        Transport.Vehicle vehicle = trans.new Vehicle();
        vehicle.details();
    }
}

Interfaces

All the method in interface are Public & Abstract.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface Pokemon {
    String name = "Pikachu";
    void getAbilities();
    void getHeight();
    void getWeight();
}

class PokeService implements Pokemon {
    /* implements all the methods of Pokemon*/
}

interface PokeDetails extends Pokemon {
    void details();
}

Every Variable in Interface is Final & Static.

1
2
3
4
5
6
7
8
interface Pokemon {
    void getAbilities();

    // default implementation: No need to implement
    default void getDetails() {
        System.out.println("Pokemon Details");
    }
}

It’s possible to provide default implementation of method inside interface so that It’s not necessary to implement that method in implemented class.

Enums

Enums are used to provide set of static values in Java. Enums also contain methods, constructors and variables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY
}
public class EnumTest {
    Day day;

    public EnumTest(Day day) {
        this.day = day;
    }

    public void tellItLikeItIs() {
        switch (day) {
            case MONDAY:
                System.out.println("Mondays are bad.");
                break;

            case FRIDAY:
                System.out.println("Fridays are better.");
                break;

            case SATURDAY: case SUNDAY:
                System.out.println("Weekends are best.");
                break;

            default:
                System.out.println("Midweek days are so-so.");
                break;
        }
    }

    public static void main(String[] args) {
        EnumTest firstDay = new EnumTest(Day.MONDAY);
        firstDay.tellItLikeItIs();
    }
}

Annotation

It is always good idea to use Annotation because…

  • Code will be clean & readable
  • Prevent Temporary Typos
  • Some Errors can be catch at compiler time. (eg. @FunctionalInterface will give compile time error in case we have more than 1 method in interface.)

e.g.

  • @Override
  • @Test
  • @FunctionalInterface

Functional Interface

A interface which has exactly one method is called functional interface. It is annotated with @FunctionalInterface. We can use lambda function if We’ve used Functional Interface.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@FunctionalInterface
interface PokemonOperation {
    public List<Pokemon> getAllPokemons();
}

public class Main {
    // with anonymous class & without lambda expression
    PokemonOperation po = new PokemonOperation(){
        public List<Pokemon> getAllPokemons() {
            return pokemons;
        }
    }


    // with anonymous class & lambda expression
    PokemonOperation op = () -> {
        return pokemons;
    }
}

Types of Interfaces

  • Normal Interface: Basic Interface consist of two or more interfaces.

  • FunctionalInterface: Allow only one method.

  • Marker Interface: It is a blank interface, used for serializations.

Exception

There are 2 types of Errors in Java.

  1. Compile Time Errors
  2. Run Time Errors

It’s not possible to handle the errors like IO Errors etc.

There are 2 Types of Exceptions

  1. Checked Exceptions: It is one of the type of Exception where It’s mandatory to handle. eg. SQLException.

  2. Unchecked Exceptions: It is one of those Exception which let you make your own choice weather to handle the Exception or not.

Exception Handling

1
2
3
4
5
try {
    /* ...Critical Statements...*/
} catch (Exception exception) {
    /* ...Exception Handler Code... */
}

It can have multiple catch after try block such that we can catch multiple Exceptions. If We want to handle Specific Exception, It can be done by replacing Exception with Specific Exception or Custom Exception. eg. ArithmeticException

It’s always good idea to have Exception as last catch Block, because in any case if Exception is not handled with Specific Exception then It would be handle by Exception which is Super class of all the Exception.

Object => Throwable => Error, Exceptions.

Throw

It is used to throw Exception manually.

1
2
3
4
5
6
7
8
9
10
try {
    int a = 10;
    int b = 20;
    if(b == 0) {
        throw new ArithmeticException("Can't Divide by Zero");
    }
    int c = a / b;
} catch (Exception e) {
    System.out.println("Exception Thrown!");
}

Custom Exception

All the Exceptions are thrown with It’s super class called Exception. So, in order to have our custom exception we are required to extends Exception or RuntimeException Class.

1
2
3
4
5
class MyException extends RuntimeException {
    public MyException(String message) {
        super(message);
    }
}

Now we can throw our Custom Exception as throw new MyException("Invalid Stuff");

Throws

Throws is used to let parent method handle the Exception.

1
2
3
void show() throws Exception {
    // Hey Parent, Handle my Exception
}

Any Exception thrown in above method will be handle by It’s Parent Method. It’s possible to handle chain of throws and the root method will handle the Exception.

Try with Resources

Sometimes We need to close the resource we are working with. eg. SQL Connection, FileDescriptor etc.

We can make use of finally block which will always execute no matter Exception occurs or not. but There is a better way.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Using Finally Block
try {
    // Open SQL Connection
} catch(SQLException e) {
    // Handle Exception while opening connection
} finally {
    // Close SQL Connection
}

// Using try() Syntax
try(/*Open SQL Connection*/) {

} catch() {

}

If We use try() syntax then we don’t need to close the connection manually. It will get close automatically.

Threads

A Thread is a lightweight process. There are couple of ways to create threads in Java.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A extends Thread {
    public void run() {

    }
}
class B extends Thread {
    public void run() {

    }
}

class Main {
    public static void main(String []args) {
        A obj1 = new A();
        obj1.start();

        B obj2 = new B();
        obj2.start();
    }
}

Thread also has some priority which is between 1 to 10. The Default Priority of Thread is 5. It’s changeable too.

1
obj1.setPriority(9);

For setting Maximum Priority we can use Thread.MAX_PRIORITY and for minimum priority Thread.MIN_PRIORITY.

We can also make the thread sleep with Thread.sleep(100); The Thread will sleep for 100 Milliseconds.

Thread using Runnable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class A implements Runnable {
    public void run() {

    }
}

class B implements Runnable {
    public void run() {

    }
}

class Main {
    public static void main(String []args) {
        A obj1 = new A();
        B obj2 = new B();

        Thread t1 = new Thread(obj1);
        Thread t2 = new Thread(obj2);
        t1.start();
        t2.start();
    }
}

Race Condition

While working with Thread There might be condition where multiple threads are in race to access the resource and eventually leads to infinity waiting that is called Race Condition.

We might loose the data while working with same resources with multiple threads.

We use synchronized keyword to Race Condition this problem.

1
2
3
4
5
6
class Counter {
    private int count = 0;
    public synchronized void increment() {
        count++;
    }
}

Collections

Collections in java provides architecture to store and work with different types of objects. It provides many interfaces like ArrayList, List, Stack, Queue, Priority Queue, Tree, HashSet, Set, Map etc.

  • List: ArrayList, LinkedList
  • Queue: DeQueue
  • Set: HashSet, LinkedHashSet
  • Map: HashMap

List Example

1
2
Collection<Integer> nums = new ArrayList<Integer>();
List<Integer> numList= new ArrayList<Integer>();

Set Example

1
2
3
4
5
6
7
Set<Integer> numSet = new HashSet<Integer>();
// for sorted list
Set<Integer> numSetSorted = new TreeSet<Integer>();

while(numSet.hasNext()){
    System.out.println(numSet.next());
}

Map Example

1
2
3
4
5
6
7
8
9
10
Map<String, Integer> data = new HashMap<String, Integer>();
data.put("Om", 20);
System.out.println(data.get("Om")); // 20
System.out.println(data.keySet()); // [Om, ...]

// for sorted Data
List<Integer> numList = ArrayList<Integer>();
Collection.sort(numList);
// OR
Collection.sort(numList, com); // custom compare function

Example of custom Comparator Function

1
2
3
4
5
6
7
8
9
Comparator<Integer> com = new Comparator<Integer>() {
    public int compare (Integer i , Integer j) {
        if (i % 10 > j % 10) {
            return true;
        } else {
            return false;
        }
    }
}

Stream API

While using Collection sometimes we have to perform some Operations on our Collection. We can use loops to iterate the Collection and perform the actions on the Collection, but It makes code unreadable and messy, for better readability and easy to work on we can apply Stream on the Collections.

We can add a layer as filters, map or reduce and add some terminal operations at the end to access the applied result.

To Print the ArrayList

1
2
3
4
5
List<Integer> num = new ArrayList<Integer>();
num.add(5);
num.add(3);
num.add(6);
num.forEach(n -> System.out.println(n));

Consumers

It is used to accept the result and it can call when we run accept method of the consumer.

1
2
3
4
5
6
7
Consumer<List<Integer>> listNumber = listOfInt -> {
    for(Integer i: listOfInt) {
        System.out.println(i);
    }
}

listNumber.accept(Array.asList(1,2,3,4));

andThen() of Consumer

1
2
3
4
5
6
7
8
9
10
11
12
13
Consumer<List<Integer>> listNumber1 = listOfInt -> {
    for(Integer i: listOfInt) {
        System.out.println(i);
    }
}

Consumer<List<Integer>> listNumber2 = listOfInt -> {
    for(Integer i: listOfInt) {
        System.out.println(i + 1);
    }
}

listNumber1.andThen(listNumber2).accept(Array.asList(1,2,3,4));

First it will run listNumber1 then It runs listNumber2 with given List.

Stream can only be used once. Once you use it, It will get expired but It’s possible to convert existing stream to the new Stream, as like wise we can perform multiple nested operations.

1
2
3
4
List<Integer> numList = new ArrayList<Integer>();
Stream<Integer> s1 = numList.stream();
Stream<Integer> s2 = s1.filter(n -> n % 10);
// can't use s1 any more

Once s1 is used, we can’t use s1 any more but we can create another stream out of it which is s2 and can s2 further.

Alternatively we can use . operator to perform multiple operation within one stream.

1
2
3
4
int result = nums.stream()
    .filter(n -> n % 2 == 0)
    .map(n -> n * 2)
    .toList();

Records

When we have all the class member variables as final then we can define record instead of class.

Generally, We use records to hold the data. eg. Request & Response Holder, as well as Database Response holder etc.

1
2
3
4
5
6
7
8
9
10
11
12
13
public record Person(String name, int age) {
    public Person {
        if (name == "") {
            System.out.println("Name is Empty");
        }
    }
}

public class Main {
    public static void main(String []args) {
        Person person = new Person("Om", 20);
    }
}

Sealed Classes

Sealed Class is used to restrict the access of the class. We specify the classes to which We want to give access to our class.

1
2
3
public sealed class PremiumVehicle permits Audi, BMW, RR {
    /* ...body... */
}

The class PremiumVehicle is only accessible by Audi, BMW, RR. None other than those class can access the PremiumVehicle.

This post is licensed under CC BY 4.0 by the author.