Java Backend Complete

Variable

It is a named memory location to store value.

Types of Variables:

  1. Local Variables:
    • Declared inside a method, constructor, or block.
    • Cannot be used outside the body where they are declared
    • Must be initialized before use.
    • Cannot be declared static.
    void method() {
        int localVar = 10; // Local variable
        System.out.println(localVar);
    }
  2. Instance Variables:
    • Declared outside any method, inside a class.
    • Represent the state of an object , each object has its own copy
    • Memory is allocated when an object is created and deallocated when it is destroyed.
    • Default values are provided (e.g., 0 for int, null for objects), if they are not initialized
    class MyClass {
        int instanceVar; // Instance variable
    }
  3. Static Variables (Class Variables):
    • Declared with the static keyword.
    • Shared among all instances of a class.
    • Memory is allocated when the class is loaded only once ,no matter how many instances are created.
    • Static varaiable are initaialized when class is loaded and destroyed when class is unloaded.
    • Static variable are accessed with classname.variable name when they are accessed in different class.
    class MyClass {
        static int staticVar = 5; // Static variable
    }

Indentfier :

1. Java Identifier Rules

Java is a statically-typed language, and its identifiers must follow these rules:
Rules:

  1. Allowed Characters:
    • Identifiers can include letters (A-Z, a-z), digits (0-9), the underscore (_), and the dollar sign ($).
    • They cannot contain spaces or special characters (e.g., @, #, %).
  2. Cannot Start with a Digit:
    • Identifiers must begin with a letter, _, or $.
    int _count = 0;  // Valid
    int $money = 100; // Valid
    int 1stNumber = 10; // Invalid
  3. No Reserved Keywords:
    • Keywords like class, if, else, int, etc., cannot be used as identifiers.
  4. Case Sensitivity:
    • Identifiers are case-sensitive.
    • myVar and myvar are distinct identifiers.
  5. No Length Limit:
    • There is no strict limit on the length of identifiers, but long names are discouraged for readability.
    int myVariable = 10; // Valid
    int MyVariable = 20; // Different from above

Data Types in Java

Java is a statically-typed language, which means you must specify the data type of a variable when you declare it. Java's data types are divided into two categories: Primitive and Non-Primitive (or Reference types).

1. Primitive Data Types

Java provides 8 built-in primitive data types:

Data Type Size Default Value Description Example
byte 1 byte 0 Stores small integer values (-128 to 127). byte b = 100;
short 2 bytes 0 Stores medium integer values (-32,768 to 32,767). short s = 30000;
int 4 bytes 0 Stores large integer values (-2Β³ΒΉ to 2Β³ΒΉ-1). int i = 100000;
long 8 bytes 0L Stores very large integer values (-2⁢³ to 2⁢³-1). long l = 10000000000L;
float 4 bytes 0.0f Stores single-precision decimal values. float f = 5.75f;
double 8 bytes 0.0d Stores double-precision decimal values. double d = 19.99;
char 2 bytes '\u0000' Stores a single character. char c = 'A';
boolean 1 bit false Stores true or false values. boolean b = true;
Characteristics of Primitives:

Non-Primitive (Reference) Data Types:

Non-primitive types are objects that store references to data and include:

Data Type Description Example
String Sequence of characters. String str = "Hello";
Arrays Collection of similar types. int[] arr = {1, 2, 3};
Classes User-defined data type for creating objects. class MyClass {}
Interfaces Abstract data type for defining methods. interface MyInterface {}
Enums Represents a fixed set of constants. enum Color { RED, GREEN, BLUE }
Characteristics:

Class point
{
Int x;
Int y;
} members of the class
Class test
{
Public static void main (String args[])
{
Point p=new Point() ; we are creating a variable of non primitive using new keyword
p.x=10; or use setter method
p.y=20; or use setter method
}


Memory Management: Stack vs. Heap

To understand how primitives and non-primitives are stored and accessed, let’s explore stack and heap memory.

Stack Memory in Java: Explanation

What Stack Memory Stores?

Key Characteristics of Stack Memory:

Heap Memory

Memory Allocation for Primitive vs. Non-Primitive

Aspect Primitive Non-Primitive
Memory Location Stored in the stack. Reference in stack, object in heap.
Data Type Holds actual data values. Holds references to data objects.
Garbage Collection Not applicable. Managed by the garbage collector.

Example in Action

public class MemoryExample {
    public static void main(String[] args) {
        int primitive = 10; // Stored in stack
        String nonPrimitive = "Java"; // Reference in stack, "Java" object in heap

        Person person = new Person(); // 'person' reference in stack, object in heap
        person.setName("Alice");      // Name field stored in heap
    }
}

class Person {
    private String name; // Non-primitive, stored in heap

    public void setName(String name) {
        this.name = name;
    }
}

Key Points to Remember

  1. Primitive types are simpler and more lightweight; they reside in the stack.
  2. Non-primitive types are versatile and allow more complex data structures but are less efficient in terms of memory usage.
  3. The stack is for short-term storage (local variables, references), and the heap is for long-term storage (objects).
  4. Efficient memory management is crucial to avoid issues like stack overflow or heap memory leaks.

Implicit and Explicit Type Conversion in Java

Type conversion in Java is the process of converting a value from one data type to another. It can occur automatically (implicit) or through manual intervention (explicit).

1. Implicit Type Conversion (Widening)

Definition:

Rules:

  1. Compatible Data Types: The source and target data types must be compatible (e.g., numeric to numeric).
  2. No Data Loss: The target type has a larger range and precision, ensuring no loss of data.

Example:

public class ImplicitConversion {
    public static void main(String[] args) {
        int intValue = 42;
        long longValue = intValue;  // Implicit conversion from int to long
        double doubleValue = longValue; // Implicit conversion from long to double
        
        System.out.println("Integer value: " + intValue);
        System.out.println("Long value: " + longValue);
        System.out.println("Double value: " + doubleValue);
    }
}

Key Scenarios:

2. Explicit Type Conversion (Narrowing)

Definition:

Rules:

  1. Potential Data Loss: The target type may lose precision or truncate values.
  2. Cast Required: You must explicitly specify the target type using a cast operator (type).

Example:

public class ExplicitConversion {
    public static void main(String[] args) {
        double doubleValue = 42.9;
        int intValue = (int) doubleValue;  // Explicit conversion from double to int

        System.out.println("Double value: " + doubleValue);
        System.out.println("Integer value: " + intValue);
    }
}

Key Scenarios:

The reason float is considered "bigger" than long

The reason float is considered "bigger" than long in certain contexts in Java (like type conversion) is not because it can store more precision but due to its range and representation.

Differences Between float and long

Long data type variable data are stored as we enter and if the capacity increases we get overflow. But in float, the data is stored in the form of exponents meaning 1333 is stored as 13.3 * 102, also because it is stored as exponent it rounds up the data and does not store as full.

  1. Range of Values
    • long:
      • Used for storing large whole numbers (integers) without a fractional part.
      • Range: -263 to 263 - 1 (approximately -9.2 quintillion to 9.2 quintillion).
      • Uses 64 bits to store the number directly.
    • float:
      • Used for floating-point numbers, which include fractions and decimals.
      • Range: Approximately Β±3.4 Γ— 1038 (much larger than the range of long).
      • Uses 32 bits, but its range is achieved through the scientific notation (exponential) format, not precision.

Comparison Table

Feature long float
Data Type Integer Floating-point (decimal numbers)
Size (bits) 64 32
Range Β±9.2 quintillion Β±3.4 Γ— 1038
Precision Exact for integers up to 19 digits Approx. 6-7 decimal digits
Usage For large whole numbers For scientific computations

Comments in Java, Python, JavaScript, and TypeScript

Comments are used to add explanations or notes within the code, which are ignored by the compiler/interpreter during execution. They help make the code more understandable for humans.

Summary:

Language Single-line comment Multi-line comment Documentation comment
Java // /* ... */ /** ... */
Python # ''' ... ''' or """ ... """ N/A (used for docstrings)
JavaScript // /* ... */ /** ... */
TypeScript // /* ... */ /** ... */

Operators in Java

Types of Operators:

  1. Arithmetic Operators (+, -, *, /, %)
  2. Relational (Comparison) Operators (>, >=, <, <=, !=)
  3. Logical Operators (&&, ||, !)
  4. Bitwise Operators
  5. Assignment Operators (=, +=, -=, *=, /=)
  6. Unary Operators
  7. Ternary Operator
  8. Shift Operators

Examples:

Arithmetic Operators

int a = 10, b = 3;
System.out.println(a + b);  // 13
System.out.println(a - b);  // 7
System.out.println(a * b);  // 30
System.out.println(a / b);  // 3
System.out.println(a % b);  // 1

Relational Operators

System.out.println(a > b);   // true
System.out.println(a < b);   // false
System.out.println(a == b);  // false
System.out.println(a != b);  // true

Logical Operators

boolean x = true, y = false;
System.out.println(x && y);  // false
System.out.println(x || y);  // true
System.out.println(!x);      // false

Bitwise Operators

System.out.println(5 & 3);  // 1 (AND)
System.out.println(5 | 3);  // 7 (OR)
System.out.println(5 ^ 3);  // 6 (XOR) assume it as subtracting
1101 – 1001 = 0100 (xor)

Assignment Operators

Note: if two operators have the same associativity, then it is right-to-left. Example: {a={b=c}}

int c = 5;
c += 3;  // c = c + 3
System.out.println(c);  // 8

Ternary Operator

int max = (a > b) ? a : b;
System.out.println(max);  // 10

Shift Operators

System.out.println(5 << 1);  // 10 (Left Shift)
System.out.println(5 >> 1);  // 2 (Right Shift)

Input

1) BufferedReader

The BufferedReader class is used to read text from an input stream efficiently. It buffers the characters to provide faster performance, particularly for reading large chunks of data at once.

Key Features

Constructors

Common Methods

Method Description
String readLine() Reads a line of text. Returns null at the end of the stream.
int read() Reads a single character. Returns -1 if the end of the stream is reached.
int read(char[] cbuf) Reads characters into an array.
boolean ready() Checks if the stream is ready to be read.
void close() Closes the reader and releases system resources.
long skip(long n) Skips the specified number of characters.

Usage Example

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderExample {
    public static void main(String[] args) {
        try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line); // Print each line of the file
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Example 2

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Input {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        // System.in β†’ reads raw bytes.
        // InputStreamReader β†’ converts bytes into characters.
        // BufferedReader β†’ buffers input for efficiency and provides methods like .readLine().

        System.out.println("enter string ");
        String s = br.readLine();
        System.out.println(s);
        int i = Integer.parseInt(br.readLine());
        float f = Float.parseFloat(s);
        double d = Double.parseDouble(s);
        String z = String.valueOf(i);
    }
}

Advantages of BufferedReader


2) Scanner

The Scanner class is used to parse primitive types and strings using regular expressions. It is more versatile for parsing and user input.

Key Features

Constructors

Note: If we use nextInt() after nextDouble() and next(), it does not read the \n when we press enter after inserting the value. So what happens is, after using next() and nextDouble(), if nextLine() is used immediately, it will not read the value the user inserts; instead, it reads only the \n which was created when we pressed enter. Hence, we need to use nextLine() with an empty string as soon as we use next() and nextDouble().

Common Methods

Method Description
String nextLine() Reads an entire line of input.
String next() Reads a single word only from the input.
int nextInt() Parses the next token as an int.
double nextDouble() Parses the next token as a double.
boolean hasNext() Checks if there is another token in the input.
boolean hasNextInt() Checks if the next token can be parsed as an int.
void close() Closes the scanner.
String findInLine(String regex) Finds a string matching the regular expression in the current line.

Usage Example

import java.util.Scanner;

public class ScannerExample {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("Enter your name: ");
        String name = scanner.nextLine();

        System.out.print("Enter your age: ");
        int age = scanner.nextInt();

        System.out.println("Name: " + name);
        System.out.println("Age: " + age);

        scanner.close();
    }
}

Advantages of Scanner


Comparison Between BufferedReader and Scanner

Aspect BufferedReader Scanner
Package java.io java.util
Speed Faster for reading large text data. Slower due to built-in parsing capabilities.
Parsing Does not parse data; requires manual parsing. Supports parsing of primitive types and tokens directly.
Buffering Buffers input for efficient reading. No explicit buffering.
Input Sources Works with Reader objects. Works with InputStream, File, and String.
Use Case Best for reading large text files or streams efficiently. Best for user input and small-scale parsing tasks.
Error Handling Throws IOException. Throws InputMismatchException for invalid inputs.

When to Use What?


Output

System.out.print() / println():

It has method overriding, meaning based on the type, it automatically changes; it converts the output to a string before printing.

System.out.format() / printf:

It has a placeholder defined with % and a variable with a value.

1) Placeholders

Example: System.out.format("%h", "abc");, System.out.format("%b", 5 > 3); β†’ Output: true

2) Flags

Java Control Flow Statements

Conditional Statements

Conditional statements execute different code blocks based on whether a condition is true or false.

1. if Statement

Executes a block of code if the condition evaluates to true.

if (condition) {
    // Code to execute if condition is true
}

Example:

int a = 10;
if (a > 5) {
    System.out.println("a is greater than 5");
}

2. if-else Statement

Executes one block of code if the condition is true and another block if it's false.

if (condition) {
    // Code to execute if condition is true
} else {
    // Code to execute if condition is false
}

Example:

int a = 10;
if (a < 5) {
    System.out.println("a is less than 5");
} else {
    System.out.println("a is greater than or equal to 5");
}

3. if-else if-else Ladder

Tests multiple conditions. The first condition that evaluates to true is executed.

if (condition1) {
    // Code for condition1
} else if (condition2) {
    // Code for condition2
} else {
    // Code if all conditions are false
}

Example:

int a = 10;
if (a < 5) {
    System.out.println("a is less than 5");
} else if (a == 10) {
    System.out.println("a is equal to 10");
} else {
    System.out.println("a is greater than 10");
}

4. switch Statement

Tests a variable against a list of values (cases) and executes the corresponding block.

switch (variable) {
    case value1:
        // Code for value1
        break;
    case value2:
        // Code for value2
        break;
    default:
        // Code if no cases match
}

Example:

int day = 2;
switch (day) {
    case 1:
        System.out.println("Monday");
        break;
    case 2:
        System.out.println("Tuesday");
        break;
    default:
        System.out.println("Other Day");
}

Loops

Loops allow you to execute a block of code multiple times.

1. for Loop

Executes a block of code a fixed number of times.

for (initialization; condition; update) {
    // Code to repeat
}

Example:

for (int i = 1; i <= 5; i++) {
    System.out.println("Count: " + i);
}

2. while Loop

Executes a block of code as long as the condition is true.

while (condition) {
    // Code to repeat
}

Example:

int i = 1;
while (i <= 5) {
    System.out.println("Count: " + i);
    i++;
}

3. do-while Loop

Executes the block of code at least once, then repeats as long as the condition is true.

do {
    // Code to execute
} while (condition);

Example:

int i = 1;
do {
    System.out.println("Count: " + i);
    i++;
} while (i <= 5);

4. Enhanced for Loop (For-Each)

Iterates over arrays or collections.

for (type element : collection) {
    // Code to execute
}

Example:

int[] numbers = {1, 2, 3, 4, 5};
for (int num : numbers) {
    System.out.println(num);
}

Branching Statements

1. break

Exits the loop or switch immediately.

for (int i = 1; i <= 5; i++) {
    if (i == 3) {
        break; // Exit loop
    }
    System.out.println("Count: " + i);
}

Output:

Count: 1
Count: 2

2. continue

Skips the current iteration and moves to the next.

for (int i = 1; i <= 5; i++) {
    if (i == 3) {
        continue; // Skip iteration
    }
    System.out.println("Count: " + i);
}

Output:

Count: 1
Count: 2
Count: 4
Count: 5

Key Differences Between return and break

Aspect return break
Scope Ends the method/function execution. Ends only the loop or switch-case.
Usage in Loops Stops the loop and method immediately. Stops the loop but continues the method.
Typical Use Case When you need to send a value or exit the method early. When you want to exit a loop prematurely but keep running the method.

Object-Oriented Programming (OOP)

Object-Oriented Programming (OOP) is a programming paradigm based on the concept of objects, which are instances of classes. It focuses on organizing code into reusable and interconnected components that represent real-world entities and their behaviors.

Key Concepts of OOP

1. Classes and Objects

Think of a class as a blueprint or a recipe. For example, a blueprint of a house shows you how the house should be built, but it’s not the actual house itself. Similarly, a class is a set of instructions that define what an object will look like (its properties) and what it can do (its behaviors).

In programming, it defines:

2. Encapsulation

3. Inheritance

4. Polymorphism

5. Abstraction

Benefits of OOP

Object Creation

Objects are created outside the class, usually in some other class. The variable referring to an object is called a reference variable.

Encapsulation

Encapsulation is one of the fundamental principles of Object-Oriented Programming (OOP). It refers to hiding the internal details of an object and exposing only what is necessary through a controlled interface. We hide variables and methods by declaring them private and access them through getters and setters methods.

Example:

public class Encapsulation
{
    private int id;

    public int getId()
    {
        return id;
    }

    public void setId(int id)
    {
        this.id = id;
    }
}
    

Access Modifiers in Detail

1. public - Accessible from Anywhere

A public method, variable, or class can be accessed from anywhere, even from a different package.

Example: Public Class and Method

In MainClass.java (Package: mypackage1)
package mypackage1;

public class PublicExample {
    public void display() {
        System.out.println("This is a public method.");
    }
}
    
In AnotherClass.java (Different Package: mypackage2)
package mypackage2;

import mypackage1.PublicExample; // Importing the class

public class TestPublic {
    public static void main(String[] args) {
        PublicExample obj = new PublicExample();
        obj.display(); // βœ… Accessible
    }
}
    

Output:

This is a public method.
    

βœ” Public methods and classes are accessible everywhere, even in different packages.

2. private - Accessible Only Within the Declared Class

A private method or variable cannot be accessed outside the class.

Example: Private Variable and Method

class PrivateExample {
    private int secretNumber = 42; // Private variable

    private void showSecret() { // Private method
        System.out.println("Secret number is: " + secretNumber);
    }

    public void accessPrivateMethod() { // Public method to access private method
        showSecret();
    }
}

public class TestPrivate {
    public static void main(String[] args) {
        PrivateExample obj = new PrivateExample();

        // System.out.println(obj.secretNumber); // ❌ ERROR: private variable cannot be accessed
        // obj.showSecret(); // ❌ ERROR: private method cannot be accessed

        obj.accessPrivateMethod(); // βœ… Indirect access via public method
    }
}
    

Output:

Secret number is: 42
    

βœ” Private members can only be accessed within the same class.

βœ” Use public methods (getters/setters) to access private variables.

3. default (No Modifier) - Accessible Within the Same Package

If no access modifier is specified, the member is considered default (package-private), meaning it can be accessed only within the same package.

Example: Default Modifier

In DefaultExample.java (Package: mypackage1)
package mypackage1;

class DefaultExample {
    void display() { // No access modifier = default
        System.out.println("This is a default method.");
    }
}
    
In TestDefault.java (Same Package: mypackage1)
package mypackage1;

public class TestDefault {
    public static void main(String[] args) {
        DefaultExample obj = new DefaultExample();
        obj.display(); // βœ… Accessible
    }
}
    
In AnotherPackageTest.java (Different Package: mypackage2)
package mypackage2;

import mypackage1.DefaultExample; // ❌ ERROR: Class has default access

public class AnotherPackageTest {
    public static void main(String[] args) {
        DefaultExample obj = new DefaultExample(); // ❌ ERROR: Cannot access default class from another package
    }
}
    

Output (from TestDefault.java in the same package):

This is a default method.
    

❌ Error if accessed from another package:

DefaultExample is not public in mypackage1; cannot be accessed from outside package
    

βœ” Default methods are accessible only within the same package.

4. protected - Accessible in Same Package & in Subclasses (Even in Different Packages)

A protected method or variable can be accessed:

  1. Within the same package (like default modifier).
  2. From a subclass, even if the subclass is in a different package (only through inheritance).

Example: Protected Modifier

In ParentClass.java (Package: mypackage1)
package mypackage1;

public class ParentClass {
    protected void showMessage() {
        System.out.println("This is a protected method.");
    }
}
    
In ChildClass.java (Different Package: mypackage2)
package mypackage2;

import mypackage1.ParentClass;

public class ChildClass extends ParentClass { // Inheriting ParentClass
    public static void main(String[] args) {
        ChildClass obj = new ChildClass();
        obj.showMessage(); // βœ… Accessible because of inheritance
    }
}
    
In TestProtected.java (Different Package: mypackage2, Without Inheritance)
package mypackage2;

import mypackage1.ParentClass;

public class TestProtected {
    public static void main(String[] args) {
        ParentClass obj = new ParentClass();
        // obj.showMessage(); // ❌ ERROR: Cannot access protected method without inheritance
    }
}
    

Output from ChildClass.java:

This is a protected method.
    

❌ Error in TestProtected.java (without inheritance):

showMessage() has protected access in mypackage1.ParentClass
    

βœ” Protected members are accessible in the same package and in subclasses (even in different packages).

βœ” To access a protected method from a different package, the class must inherit it.

Comparison with Other Access Specifiers

Access Level Within Class Within Package Subclass (Other Package) Other Classes
private Yes No No No
default Yes Yes No No
protected Yes Yes Yes (via inheritance) No
public Yes Yes Yes Yes

Inheritance in Java

Introduction

Inheritance is one of the key principles of Object-Oriented Programming (OOP) in Java. It allows a class (called a child class or subclass) to acquire the properties (fields) and behaviors (methods) of another class (called a parent class or superclass) which are public and protected no matter which package they belong to, default can be accessed only within the same package. This promotes code reuse, modularity, and scalability in application development. A child class can override any methods of the parent except the private using @Override annotation.

Inheritance is-a Relationship

Inheritance represents an "is-a" relationship, meaning:

Syntax

class ParentClass {
    // Fields and methods of the parent class
}

class ChildClass extends ParentClass {
    // Fields and methods of the child class
}

Example

// Parent Class
class Animal {
    String name;

    public void eat() {
        System.out.println(name + " is eating.");
    }
}

// Child Class
class Dog extends Animal {
    public void bark() {
        System.out.println(name + " is barking.");
    }
}

// Main Class
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.name = "Buddy";
        dog.eat();  // Inherited from Animal class
        dog.bark(); // Defined in Dog class
    }
}

The super Keyword

The keyword super refers to the immediate parent or its ancestors.

Note: super can only be used inside a method, constructor, and super cannot be used in a static context and not for field initialization outside.

Its used to call variable, methods, and constructor of its parent class.

Usage of super in Java

  1. Access Parent Class Variables

    If a subclass has a variable with the same name as in the parent class, super helps distinguish between them.

    class Parent {
        String message = "Hello from Parent";
    }
    
    class Child extends Parent {
        String message = "Hello from Child";
    
        void display() {
            System.out.println(message);      // Refers to Child's message
            System.out.println(super.message); // Refers to Parent's message
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Child obj = new Child();
            obj.display();
        }
    }

    Output:

    Hello from Child
    Hello from Parent
  2. Call Parent Class Methods

    If a subclass overrides a method, super can call the parent class's version of that method.

    class Parent {
        void show() {
            System.out.println("Parent's show() method");
        }
    }
    
    class Child extends Parent {
        void show() {
            System.out.println("Child's show() method");
            super.show(); // Calls Parent's method
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Child obj = new Child();
            obj.show();
        }
    }

    Output:

    Child's show() method
    Parent's show() method
  3. Call Parent Class Constructor

    The super() call invokes the immediate parent class constructor. It must be the first statement inside a subclass constructor.

    class Parent {
        Parent() {
            System.out.println("Parent Constructor");
        }
    }
    
    class Child extends Parent {
        Child() {
            super(); // Calls Parent constructor
            System.out.println("Child Constructor");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Child obj = new Child();
        }
    }

    Output:

    Parent Constructor
    Child Constructor

Key Points:

Additional Functionalities of super

  1. Calling Parent Class’s Parameterized Constructor:

    If the parent class has a parameterized constructor, the child class must explicitly call it using super(arguments). If super() is not provided, Java will attempt to call the default constructor of the parent.

    class Parent {
        Parent(String msg) {
            System.out.println("Parent Constructor: " + msg);
        }
    }
    
    class Child extends Parent {
        Child() {
            super("Hello from Child"); // Calls Parent's parameterized constructor
            System.out.println("Child Constructor");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Child obj = new Child();
        }
    }

    Output:

    Parent Constructor: Hello from Child
    Child Constructor

    Important: If the parent class only has a parameterized constructor, you must call it explicitly using super(arguments), otherwise, compilation will fail.

  2. super Cannot Be Used in a Static Context:

    Since super refers to an instance of the parent class, it cannot be used in a static method or block.

    class Parent {
        void instanceMethod() {
            System.out.println("Instance Method in Parent");
        }
    }
    
    class Child extends Parent {
        static void staticMethod() {
            // super.instanceMethod(); // ❌ Compilation error
            System.out.println("Static Method in Child");
        }
    }

    Error: super cannot be referenced from a static context.

  3. Using super in Method Overloading:

    If a parent class has multiple overloaded methods, super can be used to choose which version of the method should be called.

    class Parent {
        void display() {
            System.out.println("Parent display() method");
        }
    
        void display(String msg) {
            System.out.println("Parent display() with message: " + msg);
        }
    }
    
    class Child extends Parent {
        void display() {
            super.display(); // Calls Parent's display()
            super.display("Hello from Child"); // Calls overloaded display()
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Child obj = new Child();
            obj.display();
        }
    }

    Output:

    Parent display() method
    Parent display() with message: Hello from Child
  4. Using super with instanceof:

    Even though super itself cannot be used with instanceof, the parent class reference can be used to check inheritance.

    class Parent {}
    
    class Child extends Parent {}
    
    public class Main {
        public static void main(String[] args) {
            Child obj = new Child();
            System.out.println(obj instanceof Parent); // true
        }
    }

    Output:

    true

    This confirms that obj is an instance of Parent.

  5. super and Multiple Levels of Inheritance:

    If there are multiple levels of inheritance, super only refers to the immediate parent.

    class GrandParent {
        void greet() {
            System.out.println("Hello from GrandParent");
        }
    }
    
    class Parent extends GrandParent {
        void greet() {
            System.out.println("Hello from Parent");
        }
    }
    
    class Child extends Parent {
        void greet() {
            super.greet(); // Calls Parent's greet()
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Child obj = new Child();
            obj.greet();
        }
    }

    Output:

    Hello from Parent

    Note: If you want to call the GrandParent's method, you must do super.super.greet(); but Java does not allow this. Instead, you need to modify the Parent to call super.greet().

Example:

package inheritance;

public class Bike {
    int name;
    int speed;
    
    public Bike(int name, int speed) {
        this.name = name;
        this.speed = speed;
    }
    
    public void addspeed(int speed) {
        this.speed = speed;
    }
}

package inheritance;

public class Motorchildbike extends Bike {
    String eng;
    
    public Motorchildbike(int name, int speed, String engine) {
        super(name, speed); // Calling parent constructor
        eng = engine;
    }
    
    public String toString() {
        return "name: " + name + " speed: " + speed + " engine: " + eng;
    }
}

package inheritance;

public class Main {
    public static void main(String[] args) {
        Motorchildbike mcb = new Motorchildbike(1, 23, "v2");
        System.out.println(mcb.toString());
    }
}

Note:

Using the parent constructor to pass data to the child constructor using super.

Rule: If we are using the constructor of the parent in the constructor of the child, then super must be the first statement.

Types of Inheritance in Java

  1. Single Inheritance

    A subclass inherits from a single parent class.

    class Parent {
        void display() {
            System.out.println("Parent method");
        }
    }
    
    class Child extends Parent {
        void show() {
            System.out.println("Child method");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Child obj = new Child();
            obj.display(); // Inherited from Parent
            obj.show();    // Defined in Child
        }
    }
  2. Multilevel Inheritance

    A subclass inherits from another subclass, creating a chain of inheritance.

    class Grandparent {
        void showGrandparent() {
            System.out.println("Grandparent method");
        }
    }
    
    class Parent extends Grandparent {
        void showParent() {
            System.out.println("Parent method");
        }
    }
    
    class Child extends Parent {
        void showChild() {
            System.out.println("Child method");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Child obj = new Child();
            obj.showGrandparent(); // Inherited from Grandparent
            obj.showParent();      // Inherited from Parent
            obj.showChild();       // Defined in Child
        }
    }
  3. Hierarchical Inheritance

    Multiple subclasses inherit from a single parent class.

    class Animal {
        void eat() {
            System.out.println("Eating");
        }
    }
    
    class Dog extends Animal {
        void bark() {
            System.out.println("Barking");
        }
    }
    
    class Cat extends Animal {
        void meow() {
            System.out.println("Meowing");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Dog dog = new Dog();
            dog.eat();
            dog.bark();
    
            Cat cat = new Cat();
            cat.eat();
            cat.meow();
        }
    }
  4. Multiple Inheritance (via Interfaces)

    Java does not support multiple inheritance with classes to avoid ambiguity, but it can be achieved using interfaces.

    interface A {
        void methodA();
    }
    
    interface B {
        void methodB();
    }
    
    class C implements A, B {
        public void methodA() {
            System.out.println("Method A");
        }
    
        public void methodB() {
            System.out.println("Method B");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            C obj = new C();
            obj.methodA();
            obj.methodB();
        }
    }

Method Overriding in Inheritance

Method overriding allows a subclass to provide a specific implementation of a method that is already defined in the parent class. The overridden method must have the same name, return type, and parameters.

class Animal {
    void sound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.sound(); // Output: Dog barks
    }
}

The super Keyword

The super keyword is used to access members of the parent class or its ancestors (whichever is found first), including:

  1. Accessing parent class methods: super.methodName()
  2. Accessing parent class fields: super.fieldName
  3. Calling the parent class constructor: super(parameters)
class Car {
    public Car(String color, int speed) {
        this.color = color;
        this.speed = speed;
    }
}

class Ferrari extends Car {
    int gear;
    public Ferrari(String color, int speed, int gear) {
        super(color, speed); // Calling parent constructor
        this.gear = gear;
    }
}

Note: We can call any method that is not private using super except for methods in the Object class. Except for the Object class, every class will have only one parent class, and in case the class does not have any parent class, it is a child of the Object class.

Example:

class Parent {
    String name = "Parent";

    void display() {
        System.out.println("Parent method");
    }
}

class Child extends Parent {
    String name = "Child";

    void display() {
        super.display(); // Calls parent method
        System.out.println("Child method");
    }

    void showName() {
        System.out.println("Name: " + super.name); // Access parent field
    }
}

public class Main {
    public static void main(String[] args) {
        Child child = new Child();
        child.display();
        child.showName();
    }
}

Output:

Parent method
Child method
Name: Parent

The final Keyword and Inheritance

  1. final Classes: Prevent a class from being inherited.
    final class Parent { }
    class Child extends Parent { } // ERROR: Cannot inherit a final class
  2. final Methods: Prevent a method from being overridden in a subclass.
    class Parent {
        final void display() {
            System.out.println("Final method");
        }
    }
    class Child extends Parent {
        void display() { } // ERROR: Cannot override a final method
    }

Advantages of Inheritance

  1. Code Reusability: Shared code in the parent class can be reused in child classes.
  2. Extensibility: Allows easy extension of functionality.
  3. Polymorphism: Supports dynamic method dispatch through overridden methods.

Disadvantages of Inheritance

  1. Tight Coupling: Subclasses are tightly coupled with the parent class, making changes in the parent affect the subclasses.
  2. Limited Scope for Changes: Extending a class with deep inheritance hierarchies can become rigid and harder to modify.
  3. Increased Complexity: Misuse of inheritance can lead to complex and hard-to-maintain code.

Best Practices

  1. Use inheritance only when there is a clear is-a relationship (e.g., Dog is a type of Animal).
  2. Avoid deep inheritance hierarchies; prefer composition over inheritance where possible.
  3. Use the super keyword judiciously to avoid confusion between parent and child class members.

Note: A subclass inherits all public and proctected members even if they are in different package.

Composition

Composition in Even Simpler Terms with an Easy Example

Composition is a "has-a" relation, meaning that a class has instances of other classes (i.e., objects of other classes) as its members. For example:

Note: In composition, you do not need to inherit from another class to use its methods. Instead, you create an instance of the other class inside your class and call its methods.

Imagine a Toy Car

A toy car is made of different parts like:

Now, the toy car doesn't inherit these parts. Instead, it has these parts. This means:

Each part works independently. For example, the wheels know how to roll, and the engine knows how to make noise.

In Java Programming

When you use composition, you say:

Example with Code

1. Part 1: Define the Engine

This class defines the behavior of the engine.

class Engine {
    void start() {
        System.out.println("Engine is starting...");
    }

    void stop() {
        System.out.println("Engine is stopping...");
    }
}

2. Part 2: Define the Car

The car has an engine as its part.

class Car {
    private Engine engine; // The car "has-a" engine

    public Car() {
        this.engine = new Engine(); // Create an engine for the car
    }

    void startCar() {
        System.out.println("Car is starting...");
        engine.start(); // Use the engine
    }

    void stopCar() {
        System.out.println("Car is stopping...");
        engine.stop(); // Use the engine
    }
}

3. Part 3: Run the Program

Now you use the car and let the engine do its job.

public class Main {
    public static void main(String[] args) {
        Car car = new Car(); // Create a car
        car.startCar();      // Start the car
        car.stopCar();       // Stop the car
    }
}

What Happens in the Code?

Why is Composition Useful?

  1. Independent Parts: If you want to change the Engine class (e.g., make it an electric engine), you can replace it without touching the Car class.
  2. Reuse Code: If you create another class (like a Truck), it can also use the same Engine class.
  3. Easy Maintenance: Each part is separate, so fixing one part doesn't affect others.

Simpler analogy:

Think of composition as building a house. A house has windows, doors, and walls.

  • The house doesn't "inherit" these parts.
  • Instead, the house uses these parts to function properly.

Example 2 for Easy Understanding

// Part 1: Addition component
class Adder {
    public int add(int a, int b) {
        return a + b;
    }
}

// Part 2: Subtraction component
class Subtractor {
    public int subtract(int a, int b) {
        return a - b;
    }
}

class Calculator {
    // Calculator HAS-A Adder and Subtractor (composition!)
    private Adder adder;
    private Subtractor subtractor;

    // Initialize the parts when Calculator is created
    public Calculator() {
        adder = new Adder();
        subtractor = new Subtractor();
    }

    // Use the Adder component
    public int addNumbers(int x, int y) {
        return adder.add(x, y);
    }

    // Use the Subtractor component
    public int subtractNumbers(int x, int y) {
        return subtractor.subtract(x, y);
    }
}

public class CalculatorDemo {
    public static void main(String[] args) {
        Calculator myCalc = new Calculator();

        int sum = myCalc.addNumbers(10, 5);     // Uses Adder
        int difference = myCalc.subtractNumbers(10, 5); // Uses Subtractor

        System.out.println("Sum: " + sum);          // Output: 15
        System.out.println("Difference: " + difference); // Output: 5
    }
}

Interfaces in Java

An interface is a blueprint for a class. It defines a set of methods that a class must implement, but it does not provide the implementations itself. Implementations are provided by other classes. Interfaces are a core part of Java's approach to abstraction and polymorphism.

Key Characteristics of Interfaces in Java

  1. Definition Syntax:
    public interface MyInterface {
        // Constant declaration: these are public, static, and final by default.
    
        // Abstract method
        void myMethod();
    }
  2. Abstract Methods:
    • Before Java 8, interfaces could only contain abstract methods (methods without a body).
    • From Java 8 onwards, interfaces can also have:
      • Default methods: Methods with a default implementation.
      • Static methods: Methods that belong to the interface, not an instance of the class. A static method in an interface belongs to the interface itself, not to any implementing class. It cannot be overridden.
      interface MathUtils {
          static int add(int a, int b) {
              return a + b;
          }
      }
      
      public class Main {
          public static void main(String[] args) {
              int result = MathUtils.add(5, 10); // Calling static method
              System.out.println("Sum: " + result);
          }
      }
  3. Fields in Interfaces:
    • Fields in an interface are implicitly:
      • public
      • static
      • final
    • Example:
      public interface MyInterface {
          int CONSTANT = 100; // Equivalent to public static final int CONSTANT = 100;
      }
  4. Multiple Inheritance:
    • A class can implement multiple interfaces, allowing for multiple inheritance of type.
    • This is Java's way of avoiding the diamond problem that arises with multiple inheritance in classes.
  5. Inheritance in Interfaces:
    • An interface can extend another interface.
    • Example:
      public interface A {
          void methodA();
      }
      
      public interface B extends A {
          void methodB();
      }
  6. Functional Interfaces:
    • Introduced in Java 8, a functional interface is an interface with exactly one abstract method, but it can have:
      • Default methods
      • Static methods
      • Methods from java.lang.Object (like toString(), equals())
    • If it is a functional interface and we add multiple abstract methods, we get a compilation error.
    • These are used for lambda expressions.
    • Example:
      @FunctionalInterface
      public interface MyFunctionalInterface {
          void execute();
      }

Implementing an Interface

A class that implements an interface must provide implementations for all its abstract methods.

Example:

public interface Animal {
    void eat();
    void sleep();
}

public class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("Dog is eating");
    }

    @Override
    public void sleep() {
        System.out.println("Dog is sleeping");
    }
}

If a class does not provide an implementation to all the methods, it must be defined as an abstract class.

interface MyInterface {
    void method1();
    void method2();
}

abstract class MyAbstractClass implements MyInterface {
    @Override
    public void method1() {
        System.out.println("Method1 implemented in abstract class");
    }
    // method2 is not implemented, so the class is abstract
}

class ConcreteClass extends MyAbstractClass {
    @Override
    public void method2() {
        System.out.println("Method2 implemented in concrete class");
    }
}

public class Main {
    public static void main(String[] args) {
        ConcreteClass obj = new ConcreteClass();
        obj.method1(); // Output: Method1 implemented in abstract class
        obj.method2(); // Output: Method2 implemented in concrete class
    }
}

Note:

Why ConcreteClass extends MyAbstractClass and not ConcreteClass implements MyInterface?

The key reason is inheritance hierarchy and code reuse. Here is the explanation:

  • Interface Definition (MyInterface):
    interface MyInterface {
        void method1();
        void method2();
    }
    • Defines two abstract methods, and any class that implements it must provide implementations for both.
  • Abstract Class (MyAbstractClass):
    abstract class MyAbstractClass implements MyInterface {
        @Override
        public void method1() {
            System.out.println("Method1 implemented in abstract class");
        }
        // method2 is not implemented, so the class is abstract
    }
    • This class implements method1, but not method2, so it remains abstract.
  • Concrete Class (ConcreteClass):
    class ConcreteClass extends MyAbstractClass {
        @Override
        public void method2() {
            System.out.println("Method2 implemented in concrete class");
        }
    }
    • Since ConcreteClass extends MyAbstractClass, it inherits the implementation of method1 and only needs to implement method2, making it a concrete (non-abstract) class.

Now, Why extends Instead of implements?

If we had written:

class ConcreteClass implements MyInterface {
    @Override
    public void method1() {
        System.out.println("Method1 implemented");
    }

    @Override
    public void method2() {
        System.out.println("Method2 implemented");
    }
}
  • ConcreteClass would need to implement both method1 and method2, even though method1 is already implemented in MyAbstractClass.
  • By extending MyAbstractClass, we reuse its implementation of method1 and avoid duplicate code.

Key Benefits of Using Interfaces

  1. Abstraction: Separates the definition of behaviors from their implementation.
  2. Multiple Inheritance: Unlike classes, a class can implement multiple interfaces.
  3. Polymorphism: Interfaces allow a class to be treated as a type of the interface, enabling flexible and reusable code.
  4. Standardization: Interfaces define a contract, ensuring that different classes provide consistent behavior.

Real-World Example: Java's List Interface

The List interface is a part of the Java Collections Framework. Classes like ArrayList and LinkedList implement this interface.

import java.util.List;
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        System.out.println(names);
    }
}

In this example, List provides the blueprint, while ArrayList provides the implementation.

Differences Between Interfaces and Abstract Classes

Aspect Interface Abstract Class
Inheritance A class can implement multiple interfaces. A class can extend only one abstract class.
Methods Can have abstract, default, and static methods. Can have abstract and concrete methods.
Fields Fields are public, static, and final by default. Can have instance variables.
Use Case Used to define a contract for behavior. Used to share code among related classes.

Example of Multiple Inheritance Using Interface

Interface A

package Interface;

public interface A {
    void add(int a, int b);
}

Interface B

package Interface;

public interface B {
    void substract(int a, int b);
}

Interface All

package Interface;

public interface All extends A, B {
    void multiply(int a, int b);
}

Implementation Class

package Interface;

public class Implementation implements All {

    @Override
    public void add(int a, int b) {
        System.out.println("Addition: " + (a + b));
    }

    @Override
    public void substract(int a, int b) {
        System.out.println("Subtraction: " + (a - b));
    }

    @Override
    public void multiply(int a, int b) {
        System.out.println("Multiplication: " + (a * b));
    }

    public static void main(String[] args) {
        Implementation obj = new Implementation();

        obj.add(10, 5);          // Example usage of add method
        obj.substract(10, 5);    // Example usage of subtract method
        obj.multiply(10, 5);     // Example usage of multiply method
    }
}

Note:

Why does Java not allow multiple inheritance (with classes)?

Why doesn't the same issue apply to interfaces?

Example: Resolving Default Method Conflict in Multiple Interfaces (Java 8+)

interface A {
    default void show() {
        System.out.println("Interface A: show() method");
    }
}

interface B {
    default void show() {
        System.out.println("Interface B: show() method");
    }
}

// Child class implementing both A and B
class Child implements A, B {
    // Must override show() to resolve conflict
    @Override
    public void show() {
        System.out.println("Child class resolving conflict");
        A.super.show(); // Calling A's version
        B.super.show(); // Calling B's version
    }
}

public class Main {
    public static void main(String[] args) {
        Child obj = new Child();
        obj.show();
    }
}

Output:

Child class resolving conflict
Interface A: show() method
Interface B: show() method

Explanation

  1. Both interfaces A and B have a default method show().
  2. Child implements both, causing a conflict (which one to use?).
  3. Java forces Child to override show() to resolve ambiguity.
  4. Inside Child's show(), we can call A.super.show() or B.super.show() explicitly to select a version.

What Happens If We Don’t Override show()?

We get a compilation error:

class Child inherits unrelated defaults for show() from types A and B

This ensures that developers actively resolve conflicts.

Summary

Enum in Java?

An enum (short for enumeration) is a special data type that lets you define a set of constant values. These constants are type-safe, meaning only allowed values can be used.

πŸ“Œ Think of it as a list of fixed options.

🧠 Example (Real-life)

Say you're building a system that deals with Days of the Week:

enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

You can then use it in code like:

Day today = Day.MONDAY;

Now, today can only be one of the 7 days. You can’t assign it to something like today = "Holiday" β€” this prevents errors.

βœ… Why use enum?

πŸ” Syntax of enum

enum EnumName {
    CONSTANT1, CONSTANT2, CONSTANT3
}

Example:

enum Color {
    RED, GREEN, BLUE
}

Usage:

Color myColor = Color.GREEN;

πŸ” enum in a switch statement

enum Level {
    LOW, MEDIUM, HIGH
}

public class TestEnum {
    public static void main(String[] args) {
        Level level = Level.MEDIUM;

        switch(level) {
            case LOW:
                System.out.println("Low level");
                break;
            case MEDIUM:
                System.out.println("Medium level");
                break;
            case HIGH:
                System.out.println("High level");
                break;
        }
    }
}

βš™οΈ enum with fields, constructors, and methods

Yes! Enums can have fields, constructors, and methods.

enum Status {
    ACTIVE(1), INACTIVE(0);

    private int code;

    // Constructor
    Status(int code) {
        this.code = code;
    }

    // Method
    public int getCode() {
        return this.code;
    }
}

Usage:

System.out.println(Status.ACTIVE.getCode());  // Output: 1

πŸ“š Key Properties of enum

Property Explanation
Fixed Set Defines a fixed number of constant values.
Type-safe Prevents assigning invalid values.
Class-like Can have fields, constructors, and methods.
Enum constants Are implicitly public static final.
Extends Enum All enums extend java.lang.Enum class.
Cannot Extend Enums cannot extend other classes, but can implement interfaces.

❌ What Enums Can’t Do

πŸ“¦ enum vs class vs interface

Feature enum class interface
Purpose Fixed constants Blueprint for objects Blueprint for method signatures
Inheritance Cannot extend other classes Can extend a class Can extend multiple interfaces
Constructor Private Any access No constructors

πŸ§ͺ enum with interface

interface Printable {
    void print();
}

enum DocumentType implements Printable {
    PDF, WORD;

    public void print() {
        System.out.println("Printing " + this);
    }
}

βœ… Summary

Polymorphism in Java

Polymorphism is one of the core concepts of object-oriented programming (OOP) in Java. The word "polymorphism" is derived from the Greek words "poly" (many) and "morph" (forms), meaning "many forms." In Java, polymorphism allows one interface to be used for a general class of actions, enabling a single function or object to behave differently based on the context.

Types of Polymorphism in Java

Java supports two types of polymorphism:

  1. Compile-time Polymorphism (Static Polymorphism):
    • Achieved through method overloading.
    • The method to be called is determined at compile-time based on the method signature (number, type, and order of parameters).
  2. Runtime Polymorphism (Dynamic Polymorphism):
    • Achieved through method overriding.
    • The method to be executed is determined at runtime based on the object's type, not the reference type.

1. Compile-Time Polymorphism (Method Overloading)

What is Method Overloading?

Method overloading allows a class to have multiple methods with the same name but different parameter lists (different signatures). (number, type, and order of parameters).

Example:

class Calculator {
    // Method with two parameters
    public int add(int a, int b) {
        return a + b;
    }

    // Overloaded method with three parameters
    public int add(int a, int b, int c) {
        return a + b + c;
    }

    // Overloaded method with different parameter types
    public double add(double a, double b) {
        return a + b;
    }
}

public class Main {
    public static void main(String[] args) {
        Calculator calc = new Calculator();
        System.out.println("Sum (int, int): " + calc.add(10, 20));
        System.out.println("Sum (int, int, int): " + calc.add(10, 20, 30));
        System.out.println("Sum (double, double): " + calc.add(10.5, 20.5));
    }
}

Output:

Sum (int, int): 30
Sum (int, int, int): 60
Sum (double, double): 31.0

Key Points:

2. Runtime Polymorphism (Method Overriding)

What is Method Overriding?

Method overriding occurs when a subclass provides its own implementation of a method that is already defined in its parent class. The overridden method must have the same name, return type, and parameters.

Example:

class Animal {
    public void sound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("Dog barks");
    }
}

class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("Cat meows");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal1 = new Dog();  // Upcasting
        Animal animal2 = new Cat();

        animal1.sound();  // Calls Dog's sound method
        animal2.sound();  // Calls Cat's sound method
    }
}

Output:

Dog barks
Cat meows

Polymorphism with Interfaces

Polymorphism can also be achieved through interfaces. A single interface can be implemented by multiple classes, each providing its own implementation.

Example:


    interface Shape {
      void draw();
    }
    
    class Circle implements Shape {
      public void draw() {
        System.out.println("Drawing Circle");
      }
    }
    
    class Rectangle implements Shape {
      public void draw() {
        System.out.println("Drawing Rectangle");
      }
    }
    
    public class Main {
      public static void main(String[] args) {
        Shape shape1 = new Circle();
        Shape shape2 = new Rectangle();
        
        shape1.draw(); // Calls Circle's draw method
        shape2.draw(); // Calls Rectangle's draw method
      }
    }
  

Output:

    Drawing Circle
    Drawing Rectangle
  

Why Use Polymorphism?

  1. Code Reusability: Write methods or interfaces that work with general types (e.g., Animal, Shape), and let specific types (e.g., Dog, Circle) provide their own behavior.
  2. Extensibility: You can add new classes without modifying existing code.
  3. Dynamic Behavior: Allows behavior to be determined at runtime, making programs flexible and adaptable.

Key Rules for Polymorphism in Java

  1. Method Overloading:
    • Methods must have the same name but different parameter lists.
    • Can occur within the same class.
    • Return type can be the same or different.
  2. Method Overriding:
    • Methods must have the same name, parameter list, and return type (or compatible type).
    • Must involve inheritance (parent-child relationship).
    • The overridden method in the subclass cannot have a stricter access modifier than the method in the superclass.
  3. Using @Override: It's recommended to use the @Override annotation to avoid mistakes in method signatures.

Abstraction

An abstract class is a class declared with the abstract keyword. It may or may not include abstract methods. An abstract class cannot be instantiated, but another class can make this class its parent class. If a class has an abstract method, then the class should also be declared abstract. If the child class does not provide all the implementations of the parent class's abstract methods, then the child class must also be declared abstract.

Abstract Methods: Methods that are declared without implementation (no {} brackets and should end with a semicolon).

Example:


    abstract void add(int x, int y);
  

Abstract vs Interface

Constructors

Constructors are special functions that are called when an object is created. They:

Final Keyword

Static Keyword

Used to share a variable with all the objects throughout the class.

This:

This reference refers to the current object for which the code is being excecuted.
class Point {
int x;
int y;
Point(int x, int y) { // Constructor
this.x = x;
this.y = y;
}
Point setx(int x) { // Fix: Return Point object
this.x = x;
return this; // "this" means return the current object
}
Point sety(int y) { // Fix: Set value and return Point object
this.y = y;
return this;
}
}
class Main {
public static void main(String args[]) {
Point p = new Point(10, 20);
p.setx(2).sety(2); // Now method chaining works!
System.out.println("Updated Point: (" + p.x + ", " + p.y + ")");
}
}

Lambda Expressions

Lambda expressions in Java were introduced in Java 8 to make coding simpler, cleaner, and more readable. They allow you to create short, anonymous functions (functions without a name) that can be used wherever a functional interface is required.

1. What is a Lambda Expression?

A lambda expression is a concise way to define a function in Java without explicitly declaring a method in a class.

βœ… Why use Lambda Expressions?

2. Syntax of Lambda Expressions

(parameters) -> { body }

Explanation:

3. Example of Lambda Expressions

Example 1: Lambda Expression to Add Two Numbers


    // Functional Interface
    interface Addable {
      int add(int a, int b);
    }
    
    public class LambdaExample {
      public static void main(String[] args) {
        // Using Lambda Expression
        Addable addition = (a, b) -> a + b;
    
        // Calling the function
        System.out.println(addition.add(5, 10)); // Output: 15
      }
    }
  

Explanation:

  1. Addable is a functional interface with a method add(int, int).
  2. (a, b) -> a + b implements this method without writing a separate class.

4. Different Ways to Use Lambda Expressions

(a) Lambda Expression with a Single Parameter


    // Functional Interface
    interface Greeting {
      void sayHello(String name);
    }

    public class LambdaExample {
      public static void main(String[] args) {
        Greeting greet = name -> System.out.println("Hello, " + name);
        
        greet.sayHello("Alice"); // Output: Hello, Alice
      }
    }
  

βœ” Parentheses ( ) are optional if there is only one parameter.
βœ” Curly braces {} are not needed for a single statement.

(b) Lambda Expression with No Parameters


    // Functional Interface
    interface Printer {
      void print();
    }

    public class LambdaExample {
      public static void main(String[] args) {
        Printer printer = () -> System.out.println("Printing...");
        printer.print(); // Output: Printing...
      }
    }
  

βœ” ( ) is used for empty parameters.
βœ” The lambda function directly executes the print statement.

(c) Lambda Expression with Multiple Statements


    interface MathOperation {
      int operate(int a, int b);
    }
    
    public class LambdaMultiLine {
      public static void main(String[] args) {
        MathOperation multiply = (a, b) -> {
          System.out.println("Multiplying " + a + " and " + b);
          return a * b;
        };
        
        System.out.println(multiply.operate(4, 5)); // Output: Multiplying 4 and 5, 20
      }
    }
  

βœ” Curly braces {} are required when there are multiple statements.
βœ” The return statement is required when the body has multiple lines.

5. Functional Interfaces and Built-in Methods

Lambda expressions are mostly used with functional interfaces. A functional interface is an interface that has only one abstract method. Java provides built-in functional interfaces inside the java.util.function package.

Functional Interface Method Signature Usage
Predicate<T> boolean test(T t) Used for filtering
Consumer<T> void accept(T t) Used for processing each element
Function<T, R> R apply(T t) Used for transformations
Supplier<T> T get() Used for generating values

(a) Predicate<T> – Used for Filtering

βœ” Returns true or false based on a condition.


    import java.util.function.*;

    public class PredicateExample {
      public static void main(String[] args) {
        Predicate<Integer> isEven = n -> n % 2 == 0;
        System.out.println(isEven.test(8)); // Output: true
        System.out.println(isEven.test(7)); // Output: false
      }
    }
  

(b) Consumer<T> – Used for Processing Each Element

βœ” Takes an input but does not return anything.


    import java.util.function.*;

    public class ConsumerExample {
      public static void main(String[] args) {
        Consumer<String> printMsg = msg -> System.out.println(msg);
        printMsg.accept("Hello Lambda!"); // Output: Hello Lambda!
      }
    }
  

(c) Function<T, R> – Used for Transforming Data

βœ” Takes one value and returns another.


    import java.util.function.*;

    public class FunctionExample {
      public static void main(String[] args) {
        Function<String, Integer> strToInt = str -> Integer.parseInt(str);
        System.out.println(strToInt.apply("123")); // Output: 123
      }
    }
  

(d) Supplier<T> – Used for Generating Values

βœ” Takes no input and returns a value.


    import java.util.function.*;

    public class SupplierExample {
      public static void main(String[] args) {
        Supplier<Double> randomNum = () -> Math.random();
        System.out.println(randomNum.get()); // Output: Some random number
      }
    }
  

6. Using Lambda Expressions in Java Collections

Example: Sorting a List Using Lambda


    import java.util.*;

    public class LambdaSorting {
      public static void main(String[] args) {
        List names = Arrays.asList("John", "Emma", "Alice", "Bob");
    
        // Sorting using Lambda Expression
        Collections.sort(names, (a, b) -> a.compareTo(b));
    
        System.out.println(names); // Output: [Alice, Bob, Emma, John]
      }
    }
  

βœ” Collections.sort() requires a Comparator, which is provided by the lambda expression.

Example: Filtering a List Using Lambda and Streams


    import java.util.*;

    public class LambdaStreams {
      public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
    
        // Using Stream with Lambda to filter even numbers
        numbers.stream()
               .filter(n -> n % 2 == 0)
               .forEach(n -> System.out.println(n));
    
        // Output: 2 4 6
      }
    }
  

βœ” filter(n -> n % 2 == 0): Keeps only even numbers.
βœ” forEach(n -> System.out.println(n)): Prints each filtered number.

7. When to Use Lambda Expressions?

βœ… Use lambda expressions when:

❌ Avoid lambda expressions when:

8. Summary

βœ” Lambda Expression Syntax:

(parameters) -> { body }

βœ” Common Functional Interfaces:

Stream in Java

A Stream in Java is a sequence of elements that supports sequential and parallel aggregate operations. It was introduced in Java 8 as part of the java.util.stream package.

Key idea: Stream allows you to process collections of objects in a functional style (like map-reduce).

Key Characteristics

  1. Not a data structure: Stream does not store data. It operates on a data source (like Collection, Array, I/O channel, etc.).
  2. Lazy evaluation: Intermediate operations are lazy β€” they're not executed until a terminal operation is invoked.
  3. Pipelining: You can chain multiple operations together.
  4. Internal iteration: The iteration is abstracted and handled by the Stream API (unlike external iteration via loops).

Stream Creation

// From Collection
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();

// From Arrays
Stream<Integer> intStream = Arrays.stream(new Integer[]{1, 2, 3});

// Using Stream.of()
Stream<String> streamOf = Stream.of("one", "two", "three");

// Infinite Streams (with limit)
Stream<Double> infinite = Stream.generate(Math::random).limit(10);
Stream<Integer> iterate = Stream.iterate(0, n -> n + 2).limit(5);

Types of Streams

These primitive streams avoid the cost of boxing/unboxing.

1. Stream<T> – For Reference Types

βœ… Example: Using Stream<T> with reference types

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

names.stream()
    .map(String::toUpperCase)
    .forEach(System.out::println); // Outputs: ALICE, BOB, CHARLIE

Limitations of Stream<T> for primitives:

If you do this:

Stream<Integer> nums = Stream.of(1, 2, 3, 4);

You're using boxed types (Integer), not primitives (int).

This can cause performance overhead due to boxing/unboxing during operations like sum(), max().

Primitive Streams

To avoid boxing overhead, Java provides specialized primitive streams:

Primitive Type Stream Type
int IntStream
long LongStream
double DoubleStream

These are part of the java.util.stream package and offer additional methods such as:

2. IntStream

Used for sequences of int values.

βœ… Example: Summing integers

int sum = IntStream.of(1, 2, 3, 4, 5).sum(); // 15

βœ… Example: Range of integers

IntStream.range(1, 5).forEach(System.out::print);      // 1234
IntStream.rangeClosed(1, 5).forEach(System.out::print); // 12345

βœ… Example: Map and filter

int evenSum = IntStream.rangeClosed(1, 10)
    .filter(n -> n % 2 == 0)
    .sum(); // 2+4+6+8+10 = 30

3. LongStream

Used for sequences of long values.

βœ… Example: Generate long values

LongStream.of(100L, 200L, 300L)
    .map(n -> n / 100)
    .forEach(System.out::println); // 1, 2, 3

βœ… Example: Range with longs

LongStream.rangeClosed(1, 3).forEach(System.out::println); // 1, 2, 3

4. DoubleStream

Used for sequences of double values.

βœ… Example: Average of values

OptionalDouble avg = DoubleStream.of(2.5, 3.0, 4.5).average();
avg.ifPresent(System.out::println); // 3.333...

βœ… Example: Filtering

DoubleStream.of(1.2, 3.4, 5.6)
    .filter(d -> d > 3)
    .forEach(System.out::println); // 3.4, 5.6

Conversion Between Stream Types

You can convert between Stream<T> and primitive streams using mapping methods:

From To Method
Stream<Integer> IntStream .mapToInt()
IntStream Stream<Integer> .boxed()

βœ… Example: Stream<String> β†’ IntStream

Stream<String> words = Stream.of("apple", "banana", "carrot");
IntStream lengths = words.mapToInt(String::length);
lengths.forEach(System.out::println); // 5, 6, 6

βœ… Example: IntStream β†’ Stream<Integer>

IntStream.range(1, 4).boxed().forEach(System.out::println); // 1, 2, 3

Summary
Stream Type Use For Example Method Benefit
Stream<T> Objects (String, etc.) stream() General use
IntStream int primitives IntStream.range() No boxing, has sum(), average()
LongStream long primitives LongStream.of() Efficient for large numbers
DoubleStream double primitives DoubleStream.of() Useful for calculations

Stream Operations

πŸ”Έ 1. Intermediate Operations (Lazy)

1. filter(Predicate<T>)

Filters elements based on a condition.

List<String> names = List.of("Alice", "Bob", "Alex");
List<String> filtered = names.stream()
    .filter(name -> name.startsWith("A"))
    .collect(Collectors.toList()); // ["Alice", "Alex"]

2. map(Function<T, R>)

Transforms each element.

List<String> names = List.of("john", "doe");
List<String> upper = names.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList()); // ["JOHN", "DOE"]

3. flatMap(Function<T, Stream<R>>)

Flattens nested streams (used for List<List<T>>).

List<List<String>> nested = List.of(List.of("a", "b"), List.of("c"));
List<String> flat = nested.stream()
    .flatMap(List::stream)
    .collect(Collectors.toList()); // ["a", "b", "c"]

4. sorted() / sorted(Comparator)

Sorts the stream elements.

List<Integer> numbers = List.of(5, 3, 1, 4);
List<Integer> sorted = numbers.stream()
    .sorted()
    .collect(Collectors.toList()); // [1, 3, 4, 5]

Custom sort:

List<String> names = List.of("Charlie", "Alice", "Bob");
List<String> sorted = names.stream()
    .sorted(Comparator.reverseOrder())
    .collect(Collectors.toList()); // ["Charlie", "Bob", "Alice"]

5. distinct()

Removes duplicates.

List<Integer> nums = List.of(1, 2, 2, 3);
List<Integer> unique = nums.stream()
    .distinct()
    .collect(Collectors.toList()); // [1, 2, 3]

6. limit(n)

Limits the stream to first n elements.

List<Integer> limited = Stream.iterate(1, n -> n + 1)
    .limit(5)
    .collect(Collectors.toList()); // [1, 2, 3, 4, 5]

7. skip(n)

Skips first n elements.

List<Integer> skipped = Stream.of(1, 2, 3, 4, 5)
    .skip(2)
    .collect(Collectors.toList()); // [3, 4, 5]

8. peek(Consumer<T>)

Useful for debugging.

List<String> result = Stream.of("one", "two", "three")
    .peek(s -> System.out.println("Processing: " + s))
    .map(String::toUpperCase)
    .collect(Collectors.toList());

πŸ”Έ 2. Terminal Operations

1. forEach(Consumer<T>)

Applies action to each element.

Stream.of("a", "b", "c").forEach(System.out::println);

2. collect(Collector)

Collects stream elements into a collection or result.

List<String> result = Stream.of("Java", "Python")
    .collect(Collectors.toList()); // ["Java", "Python"]

3. toArray()

Converts stream to an array.

String[] array = Stream.of("x", "y", "z").toArray(String[]::new);

4. reduce()

Combines elements into one.

int sum = Stream.of(1, 2, 3)
    .reduce(0, Integer::sum); // 6

String combined = Stream.of("a", "b", "c")
    .reduce("", String::concat); // "abc"

5. count()

Counts elements in the stream.

long count = Stream.of(1, 2, 3, 4).count(); // 4

6. anyMatch, allMatch, noneMatch

Predicate-based checks.

boolean anyEven = Stream.of(1, 3, 4).anyMatch(n -> n % 2 == 0); // true
boolean allEven = Stream.of(2, 4, 6).allMatch(n -> n % 2 == 0); // true
boolean noneOdd = Stream.of(2, 4, 6).noneMatch(n -> n % 2 != 0); // true

7. findFirst() / findAny()

Finds first or any element.

Optional<String> first = Stream.of("apple", "banana").findFirst(); // "apple"

8. min() / max()

Finds min or max by comparator.

Optional<Integer> min = Stream.of(3, 1, 2).min(Integer::compareTo); // 1
Optional<Integer> max = Stream.of(3, 1, 2).max(Integer::compareTo); // 3

πŸ”· Stream Collectors

Collect to List, Set, Map

List<String> list = Stream.of("A", "B").collect(Collectors.toList());

Set<String> set = Stream.of("A", "B").collect(Collectors.toSet());

Map<Integer, String> map = Stream.of("a", "bb", "ccc")
    .collect(Collectors.toMap(String::length, s -> s)); // may need merge logic

joining()

Concatenates strings.

String joined = Stream.of("a", "b", "c")
    .collect(Collectors.joining("-")); // "a-b-c"

groupingBy()

Groups by a classifier.

Map<String, List<String>> grouped = Stream.of("apple", "banana", "apricot")
    .collect(Collectors.groupingBy(s -> s.substring(0, 1))); // {a=[apple, apricot], b=[banana]}

partitioningBy()

Partitions by predicate (true/false).

Map<Boolean, List<Integer>> partitioned = Stream.of(1, 2, 3, 4)
    .collect(Collectors.partitioningBy(n -> n % 2 == 0)); // true: even, false: odd

counting(), summarizingInt()

long count = Stream.of("a", "bb", "ccc")
    .collect(Collectors.counting()); // 3

IntSummaryStatistics stats = Stream.of("a", "bb", "ccc")
    .collect(Collectors.summarizingInt(String::length));

πŸ”· Stream Reduction

int total = Stream.of(1, 2, 3)
    .reduce(0, (a, b) -> a + b); // 6

With method reference:

int total = Stream.of(1, 2, 3).reduce(0, Integer::sum);

πŸ”· Parallel Streams

List<Integer> numbers = List.of(1, 2, 3, 4, 5);
int total = numbers.parallelStream()
    .map(n -> n * 2)
    .reduce(0, Integer::sum);

Use forEachOrdered() to preserve order:

numbers.parallelStream().forEachOrdered(System.out::println);

πŸ”· Short-Circuiting Operations

These stop processing early.

Stream.of("a", "b", "c").limit(2).forEach(System.out::println); // a b

boolean found = Stream.of(1, 2, 3, 4).anyMatch(n -> n > 3); // true

πŸ”· Custom Collector Example

Collector<String, StringBuilder, String> collector = Collector.of(
    StringBuilder::new,
    StringBuilder::append,
    StringBuilder::append,
    StringBuilder::toString
);

String result = Stream.of("Java", "Stream").collect(collector); // "JavaStream"

Gson Tutorial

What is Gson?

Gson is a Java library from Google used for converting Java objects into their JSON representation and vice versa.

βœ” Supports:

βœ… Maven Dependency

Add this to your pom.xml:

<dependency>
  <groupId>com.google.code.gson</groupId>
  <artifactId>gson</artifactId>
  <version>2.10.1</version>
</dependency>

πŸ”· 1. Gson Class – The Core

The main class used to serialize/deserialize Java objects.

➀ Example: Serialize and Deserialize

import com.google.gson.Gson;

class Person {
    String name;
    int age;
}

public class Main {
    public static void main(String[] args) {
        Gson gson = new Gson();

        // Java object β†’ JSON
        Person person = new Person();
        person.name = "Alice";
        person.age = 25;
        String json = gson.toJson(person);
        System.out.println(json); // {"name":"Alice","age":25}

        // JSON β†’ Java object
        String jsonInput = "{\"name\":\"Bob\",\"age\":30}";
        Person p = gson.fromJson(jsonInput, Person.class);
        System.out.println(p.name); // Bob
        System.out.println(p.age);  // 30
    }
}

βœ… Realistic sources of JSON in Java:

From an HTTP response (API call):

HttpURLConnection conn = (HttpURLConnection) new URL("https://api.example.com/person").openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
    response.append(line);
}
reader.close();
String jsonInput = response.toString();
Person p = gson.fromJson(jsonInput, Person.class);

From a file:

String jsonInput = new String(Files.readAllBytes(Paths.get("person.json")));
Person p = gson.fromJson(jsonInput, Person.class);

From a database (e.g., JSON stored in a text column):

String jsonInput = resultSet.getString("json_column");
Person p = gson.fromJson(jsonInput, Person.class);

From a message queue or WebSocket, etc.

πŸ”· 2. GsonBuilder – For Customization

Use GsonBuilder to configure your Gson instance.

➀ Features:

➀ Example:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class Main {
    public static void main(String[] args) {
        Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                .serializeNulls()
                .create();

        System.out.println(gson.toJson(new Person()));
    }
}

πŸ”· 3. JsonObject – JSON Object Representation

A JsonObject is a map-like object used to build or access JSON manually.

➀ Example:

import com.google.gson.JsonObject;

JsonObject obj = new JsonObject();
obj.addProperty("name", "Alice");
obj.addProperty("age", 25);

System.out.println(obj.toString()); // {"name":"Alice","age":25}

String name = obj.get("name").getAsString();  // "Alice"
int age = obj.get("age").getAsInt();          // 25

πŸ”· 4. JsonArray – JSON Array Representation

Holds multiple elements like a list.

➀ Example:

import com.google.gson.JsonArray;
import com.google.gson.JsonPrimitive;

JsonArray array = new JsonArray();
array.add(new JsonPrimitive("apple"));
array.add(new JsonPrimitive("banana"));

System.out.println(array); // ["apple","banana"]

JsonObject fruit = new JsonObject();
fruit.addProperty("name", "mango");
array.add(fruit);
System.out.println(array); // ["apple", "banana", {"name":"mango"}]

πŸ”· 5. JsonElement – Base for All JSON Types

This is the superclass of:

Useful for parsing unknown or dynamic JSON.

➀ Example:

import com.google.gson.*;

String json = "{\"name\":\"Alice\", \"age\":25}";
JsonElement element = JsonParser.parseString(json);

if (element.isJsonObject()) {
    JsonObject obj = element.getAsJsonObject();
    System.out.println(obj.get("name").getAsString()); // Alice
}

πŸ”· 6. JsonPrimitive – Primitive Value Holder

Wraps single values like:

➀ Example:

import com.google.gson.JsonPrimitive;

JsonPrimitive name = new JsonPrimitive("Alice");
JsonPrimitive age = new JsonPrimitive(25);
JsonPrimitive isActive = new JsonPrimitive(true);

System.out.println(name.getAsString());  // Alice
System.out.println(age.getAsInt());      // 25
System.out.println(isActive.getAsBoolean()); // true

πŸ”· 7. JsonNull – Represents null

A singleton class (JsonNull.INSTANCE) representing null in JSON.

➀ Example:

import com.google.gson.JsonObject;
import com.google.gson.JsonNull;

JsonObject obj = new JsonObject();
obj.add("nickname", JsonNull.INSTANCE);

System.out.println(obj.toString());  // {"nickname":null}

if (obj.get("nickname").isJsonNull()) {
    System.out.println("Nickname is null");
}

βœ… Basic Gson Usage Summary

βœ” Serialize Java Object β†’ JSON

Gson gson = new Gson();
String json = gson.toJson(myObject);

βœ” Deserialize JSON β†’ Java Object

MyClass obj = gson.fromJson(jsonString, MyClass.class);

βœ… 1. How to Exclude Fields in Gson

You might not want to serialize/deserialize all fields (e.g., sensitive info like passwords or internal data). Gson provides multiple ways to exclude fields:

πŸ”Ή a. Using transient Keyword (Built-in Java)

Fields marked transient are skipped during serialization and deserialization.

class User {
    String name;
    transient String password;
}

User user = new User();
user.name = "Alice";
user.password = "secret";

String json = new Gson().toJson(user);
System.out.println(json); // {"name":"Alice"} β†’ password is excluded

πŸ”Ή b. Using @Expose Annotation

Use @Expose and configure Gson to respect it.

import com.google.gson.annotations.Expose;

class User {
    @Expose
    String name;

    @Expose(serialize = false, deserialize = false)
    String password;
}

Gson gson = new GsonBuilder()
    .excludeFieldsWithoutExposeAnnotation()
    .create();

βœ… Only fields with @Expose will be included.

πŸ”Ή c. Using a Custom ExclusionStrategy

Full control to exclude based on logic (e.g., field name, type, annotations).

import com.google.gson.*;
import com.google.gson.annotations.SerializedName;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;

class User {
    String name;
    String password;
}

ExclusionStrategy strategy = new ExclusionStrategy() {
    public boolean shouldSkipField(FieldAttributes f) {
        return f.getName().equals("password");
    }

    public boolean shouldSkipClass(Class clazz) {
        return false;
    }
};

Gson gson = new GsonBuilder()
    .setExclusionStrategies(strategy)
    .create();

βœ… 2. Custom Serializer and Deserializer

Sometimes Gson's default behavior isn’t enough (e.g., custom date formats, flattened fields, encrypted data, etc.).

πŸ”Ή a. Custom Serializer (JsonSerializer)

import com.google.gson.*;

class Product {
    String name;
    double price;
}

class ProductSerializer implements JsonSerializer {
    public JsonElement serialize(Product p, java.lang.reflect.Type typeOfSrc, JsonSerializationContext context) {
        JsonObject obj = new JsonObject();
        obj.addProperty("product_name", p.name);
        obj.addProperty("formatted_price", "$" + p.price);
        return obj;
    }
}

// Register serializer
Gson gson = new GsonBuilder()
    .registerTypeAdapter(Product.class, new ProductSerializer())
    .create();

πŸ”Ή b. Custom Deserializer (JsonDeserializer)

class ProductDeserializer implements JsonDeserializer {
    public Product deserialize(JsonElement json, java.lang.reflect.Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        JsonObject obj = json.getAsJsonObject();
        Product p = new Product();
        p.name = obj.get("product_name").getAsString();
        p.price = Double.parseDouble(obj.get("formatted_price").getAsString().replace("$", ""));
        return p;
    }
}

// Register deserializer
Gson gson = new GsonBuilder()
    .registerTypeAdapter(Product.class, new ProductDeserializer())
    .create();

βœ… You can also combine both using TypeAdapter if needed.

βœ… 3. Gson vs Jackson – Comparison

Feature Gson Jackson
Simplicity βœ… Very simple, minimal setup ❌ More configuration required
Performance βœ… Fast for basic use βœ… Faster with tuning & large datasets
Customization βœ… Easy with GsonBuilder & adapters βœ… More powerful (Mixins, Modules)
Annotations @Expose, @SerializedName @JsonProperty, @JsonIgnore, etc.
Streaming API ❌ Not available βœ… Jackson has Streaming API (JsonParser)
Built-in Java Date support ❌ Needs adapter βœ… Native support with @JsonFormat
Null handling Requires .serializeNulls() Better defaults for nulls
XML support ❌ Not supported βœ… Supports XML via Jackson XML Module
Integration βœ… Well supported βœ… Widely used (Spring Boot default)

πŸ”Ή Example: Custom Field Names

Gson:

@SerializedName("first_name")
String firstName;

Jackson:

@JsonProperty("first_name")
String firstName;

πŸ”Ή Example: Ignore a field

Gson:

@Expose(serialize = false, deserialize = false)
String internalUseOnly;

Jackson:

@JsonIgnore
String internalUseOnly;

πŸ”Ή When to Use Which?

Use Case Recommendation
Simple JSON, no extra setup needed βœ… Gson
Integration with Spring Boot, REST βœ… Jackson
Complex polymorphic objects βœ… Jackson
Minimal dependencies, lightweight app βœ… Gson

βœ… 1. Gson Class Methods

πŸ”Ή toJson(Object src)

Converts a Java object to a JSON string.

Person p = new Person("Alice", 25);
String json = new Gson().toJson(p);
// Output: {"name":"Alice","age":25}

πŸ”Ή toJson(Object src, Type typeOfSrc)

Used for generic types like List.

List names = Arrays.asList("Alice", "Bob");
Type listType = new TypeToken>() {}.getType();

String json = new Gson().toJson(names, listType);
// Output: ["Alice","Bob"]

πŸ”Ή toJson(Object src, Appendable writer)

Writes JSON directly to a Writer or StringBuilder.

StringWriter writer = new StringWriter();
Person p = new Person("Bob", 30);

new Gson().toJson(p, writer);
System.out.println(writer.toString()); // {"name":"Bob","age":30}

πŸ”Ή toJsonTree(Object src)

Returns a JsonElement instead of a string (can be a JsonObject, JsonArray, etc.).

JsonElement tree = new Gson().toJsonTree(p);
System.out.println(tree.getAsJsonObject().get("name").getAsString()); // "Bob"

πŸ”Ή fromJson(String json, Class classOfT)

Converts a JSON string to a Java object.

String json = "{\"name\":\"Alice\",\"age\":25}";
Person p = new Gson().fromJson(json, Person.class);

πŸ”Ή fromJson(Reader json, Class classOfT)

Useful for reading from files or streams.

Reader reader = new FileReader("person.json");
Person p = new Gson().fromJson(reader, Person.class);

πŸ”Ή fromJson(String json, Type typeOfT)

Handles deserialization for generic types (e.g., List).

String json = "[{\"name\":\"A\",\"age\":20},{\"name\":\"B\",\"age\":30}]";
Type listType = new TypeToken>() {}.getType();

List people = new Gson().fromJson(json, listType);

πŸ”Ή fromJson(JsonElement json, Class classOfT)

Deserialize from a JsonElement (not a string).

JsonObject obj = JsonParser.parseString("{\"name\":\"Alice\",\"age\":25}").getAsJsonObject();
Person p = new Gson().fromJson(obj, Person.class);

βœ… 2. GsonBuilder Methods

πŸ”Ή create()

Finalizes and builds a configured Gson instance.

Gson gson = new GsonBuilder().create();

πŸ”Ή setPrettyPrinting()

Pretty prints the JSON with line breaks and indentation.

Gson gson = new GsonBuilder()
    .setPrettyPrinting()
    .create();

System.out.println(gson.toJson(new Person("Alice", 25)));

πŸ”Ή serializeNulls()

Includes null values in output (by default Gson omits them).

class User { String name; String email = null; }

Gson gson = new GsonBuilder().serializeNulls().create();
System.out.println(gson.toJson(new User()));
// {"name":null,"email":null}

πŸ”Ή excludeFieldsWithoutExposeAnnotation()

Only includes fields annotated with @Expose.

class User {
    @Expose String name;
    @Expose(serialize = false) String password;
}

Gson gson = new GsonBuilder()
    .excludeFieldsWithoutExposeAnnotation()
    .create();

πŸ”Ή registerTypeAdapter(Type, Object)

Registers a custom serializer or deserializer.

Gson gson = new GsonBuilder()
    .registerTypeAdapter(Date.class, new DateSerializer())
    .create();

πŸ”Ή setDateFormat(String format)

Specify a custom date format.

Gson gson = new GsonBuilder()
    .setDateFormat("yyyy-MM-dd")
    .create();

πŸ”Ή setLenient()

Allows malformed JSON (e.g., single quotes, trailing commas).

Gson gson = new GsonBuilder()
    .setLenient()
    .create();

πŸ”Ή disableHtmlEscaping()

Avoids escaping of characters like <, >, &.

String unsafe = "&";
String json = new GsonBuilder()
    .disableHtmlEscaping()
    .create()
    .toJson(unsafe);
// Output: "&"

πŸ”Ή enableComplexMapKeySerialization()

Allows maps with complex objects as keys.

Map map = new HashMap<>();
map.put(new Person("Alice", 25), "Engineer");

Gson gson = new GsonBuilder()
    .enableComplexMapKeySerialization()
    .create();

πŸ”Ή setFieldNamingPolicy(FieldNamingPolicy policy)

Changes how field names are mapped to JSON (e.g., camelCase β†’ lower_case_with_underscores).

Gson gson = new GsonBuilder()
    .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
    .create();

πŸ”Ή generateNonExecutableJson()

Adds a security prefix )]}' to the output to prevent XSSI attacks.

Gson gson = new GsonBuilder()
    .generateNonExecutableJson()
    .create();

πŸ”Ή setVersion(double version)

Use with @Since and @Until annotations for version-based exclusion.

class Product {
    @Since(1.0) String name;
    @Until(1.5) double price;
}

Gson gson = new GsonBuilder()
    .setVersion(1.0)
    .create();

πŸ”Ή disableInnerClassSerialization()

Skips serialization of inner/non-static classes.

Gson gson = new GsonBuilder()
    .disableInnerClassSerialization()
    .create();

πŸ”Ή excludeFieldsWithModifiers(...)

Exclude fields with specific Java modifiers (e.g., transient, static, final).

Gson gson = new GsonBuilder()
    .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT)
    .create(); 

βœ… 5. Working with JsonObject, JsonArray, JsonElement

βœ… Create manually:

JsonObject obj = new JsonObject();
obj.addProperty("name", "Alice");
obj.addProperty("age", 30);

βœ… Parse from string:

JsonElement element = JsonParser.parseString("{\"name\":\"Alice\"}");
JsonObject jsonObject = element.getAsJsonObject();

βœ… Reading values:

String name = jsonObject.get("name").getAsString();
int age = jsonObject.get("age").getAsInt();

βœ… 6. Working with Generics

Example: Deserialize List of Users

Type userListType = new TypeToken>(){}.getType();
List users = gson.fromJson(jsonString, userListType);

βœ… 7. Custom Serializer and Deserializer

class DateSerializer implements JsonSerializer {
    public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
        return new JsonPrimitive(new SimpleDateFormat("yyyy-MM-dd").format(src));
    }
}

class DateDeserializer implements JsonDeserializer {
    public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        return new SimpleDateFormat("yyyy-MM-dd").parse(json.getAsString());
    }
}

// Register with GsonBuilder
Gson gson = new GsonBuilder()
    .registerTypeAdapter(Date.class, new DateSerializer())
    .registerTypeAdapter(Date.class, new DateDeserializer())
    .create();

βœ… 8. Gson Annotations

Annotation Purpose
@Expose Mark fields for inclusion (with excludeFieldsWithoutExposeAnnotation)
@SerializedName("name_in_json") Map JSON field name to different Java field
@Since(version) Include if version β‰₯ X (with setVersion)
@Until(version) Include if version < X
public class User {
    @SerializedName("user_name")
    private String name;

    @Expose
    private int age;
}

βœ… 9. Field Naming Policy

Gson gson = new GsonBuilder()
    .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
    .create();
Policy Description
IDENTITY As is
UPPER_CAMEL_CASE MyName
UPPER_CAMEL_CASE_WITH_SPACES My Name
LOWER_CASE_WITH_UNDERSCORES my_name
LOWER_CASE_WITH_DASHES my-name
LOWER_CASE_WITH_DOTS my.name

JDBC Connectivity

JDBC stands for Java Database Connectivity, where we use API to connect and execute the query in the database. JDBC API uses JDBC drivers to connect with the database. There are 4 types of JDBC drivers:

  1. JDBC-ODBC bridge driver
  2. Native-API driver (partially Java driver)
  3. Network Protocol driver (fully Java driver)
  4. Thin driver (fully Java driver)
Java API <- Java application -> JDBC driver -> Database

List of JDBC API Interfaces

Name Description
Driver Represents a database driver and acts as an interface for communication between Java applications and the database.
Connection Represents a connection to a database. Provides methods for creating statements, managing transactions, and handling database-specific settings.
Statement Used for executing static SQL queries.
Example methods:
  • executeQuery(String sql)
  • executeUpdate(String sql)
  • execute(String sql)
PreparedStatement Extends the Statement interface. Used for executing precompiled SQL queries with parameterized inputs. Helps in preventing SQL injection.
CallableStatement Extends the PreparedStatement interface. Used for executing stored procedures in the database.
ResultSet Represents the result set of a query. Provides methods to iterate through rows and retrieve data from columns.
ResultSetMetaData Provides metadata about the columns in a ResultSet.
Methods include:
  • getColumnCount()
  • getColumnName(int column)
DatabaseMetaData Provides metadata about the database as a whole, such as the database product name, version, and supported features.
RowSet Extends the ResultSet interface and represents a set of rows. Often used for disconnected rowsets.
Subinterfaces include:
  • JdbcRowSet
  • CachedRowSet
  • WebRowSet
  • FilteredRowSet
  • JoinRowSet
SQLData Used for mapping SQL user-defined types (UDTs) in Java.
Blob (Binary Large Object) Represents a large binary object (e.g., images, files) stored as a single entity in a database.
Clob (Character Large Object) Represents a large character object (e.g., text documents).
Savepoint Represents a point in a transaction to which you can roll back.
SQLInput Used to read values from SQL user-defined types (UDTs).
SQLOutput Used to write values to SQL user-defined types (UDTs).
DataSource An alternative to DriverManager for managing connections. Allows connection pooling and more efficient resource management.
ParameterMetaData Provides metadata about the parameters in a PreparedStatement.

List of JDBC API Classes

Name Description
DriverManager Manages a list of database drivers.
Responsible for establishing a database connection using the getConnection() method.
Example methods:
  • static Connection getConnection(String url)
  • static Connection getConnection(String url, String user, String password)
  • static void registerDriver(Driver driver)
DriverPropertyInfo Describes properties required for making a database connection. Used for interactive configuration of database connections.
Types Defines constants representing SQL types (e.g., VARCHAR, INTEGER, BOOLEAN). Useful for mapping between SQL types and Java types.
SQLException Represents an exception that provides information on a database access error or other errors.
Subclasses include:
  • SQLWarning
  • BatchUpdateException
SQLWarning A subclass of SQLException that represents database access warnings, which do not stop the execution.
BatchUpdateException A subclass of SQLException thrown when an error occurs during a batch update.
Blob Represents a Binary Large Object (BLOB) as a Java object. Provides methods to manipulate BLOB data.
Clob Represents a Character Large Object (CLOB) as a Java object. Provides methods to manipulate CLOB data.
Array Represents an SQL ARRAY as a Java object. Provides methods to retrieve and manipulate array data.
Ref Represents an SQL REF object, which is a reference to an SQL structured type.
Struct Represents an SQL STRUCT object as a Java object. Used for working with user-defined types in the database.
RowId Represents an SQL ROWID type, which is a unique identifier for a row in a database.
SQLPermission Represents permission for a specific security operation related to JDBC, such as allowing a connection to be set up with a logging system.
Driver Represents a database driver implementation. Usually provided by database vendors (e.g., com.mysql.cj.jdbc.Driver).
ConnectionPoolDataSource Provides a standard way to implement connection pooling.

Transaction Isolation

1. Read Uncommitted

What is "Read Uncommitted"? "Read Uncommitted" is the lowest isolation level in databases. It means one transaction can read data that another transaction has modified but not yet committed. This is risky, because the other transaction might roll back, and the first transaction would have read invalid or temporary data. This problem is called a dirty read.

Analogy: Imagine you and your friend are co-authoring a report. Your friend is editing Section A of the report. Before they save their final version, you peek over and copy the changes into your version. But your friend later says, β€œOops, my changes were wrong!” and undoes them. Now, you have included incorrect information in your copy because you read it before it was finalized β€” this is exactly what a dirty read is.

Example in SQL (Real-World): Imagine a bank account with an initial balance of 100.

  1. Transaction 1 (T1): BEGIN TRANSACTION; UPDATE accounts SET balance = balance - 100 WHERE id = 1; (hasn't committed yet)
  2. Transaction 2 (T2): SELECT balance FROM accounts WHERE id = 1; (Reads the balance as if it has decreased by 100)
  3. Transaction 1 (T1) now decides: ROLLBACK; (Undo the update)
  4. Result: Transaction 2 has read incorrect data because it read the balance after it was changed but before it was committed. That read is called a dirty read β€” and it’s only possible in Read Uncommitted isolation level.

Summary of Issues with Read Uncommitted:

Issue Description
βœ… Fastest No locks, minimal overhead
❌ Dirty Reads Reads data that may be rolled back
❌ Inconsistent Different reads can show different versions of the same data

Why It's Dangerous: You could read temporary or incorrect data. Useful only for reporting or non-critical queries where performance matters more than accuracy.

2. Read Committed

Read Committed means a transaction can only read data that has been committed by other transactions. This avoids the problem of dirty reads (reading uncommitted, temporary data).

Behavior:

Example in SQL: Imagine a bank account with balance = 1000.

  1. Transaction 1 (T1): BEGIN TRANSACTION; UPDATE accounts SET balance = balance - 100 WHERE id = 1; (balance is now 900 internally, but not yet committed)
  2. Transaction 2 (T2): SELECT balance FROM accounts WHERE id = 1; (It will still see 1000, the last committed value, not 900)
  3. Transaction 1 commits: COMMIT; Now, after this, if T2 runs the same SELECT, it will see 900).

Result: No Dirty Reads – T2 cannot read uncommitted changes from T1.

Analogy: Your teammate is editing a Google Doc in "draft mode". You cannot see their unsaved (uncommitted) changes. Once they save (commit), you can see them. This is exactly like Read Committed.

Compared to "Read Uncommitted":

Feature Read Uncommitted Read Committed
Dirty Reads Allowed? βœ… Yes ❌ No
Safer? ❌ No βœ… Yes
Can See Uncommitted? βœ… Yes ❌ No

Problem Still Possible in Read Committed: It prevents dirty reads, but not non-repeatable reads.

Example:

  1. T1: Reads balance = 1000
  2. T2: Updates balance to 900 and commits
  3. T1: Reads balance again β€” now it's 900

The value changed between reads β€” this is a non-repeatable read, and Read Committed allows it. To fix this, you'd use Repeatable Read isolation level.

Use Cases: Safe for most OLTP systems (e.g., banking, ecommerce). Good balance between performance and consistency. Default in PostgreSQL, Oracle, SQL Server.

JDBC Example: Connection conn = DriverManager.getConnection(...); conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

3. Repeatable Read

What is "Repeatable Read"? Repeatable Read means once a transaction reads a row, no other transaction can change or delete that row until the first transaction finishes. This ensures:

Behavior of Repeatable Read:

Example – Repeatable Read:

Let’s say a table has:

id name balance
1 Alice 1000
  1. Transaction 1 (T1): BEGIN TRANSACTION; SELECT balance FROM accounts WHERE id = 1; (returns 1000)
  2. Transaction 2 (T2): BEGIN TRANSACTION; UPDATE accounts SET balance = 2000 WHERE id = 1; (This will BLOCK until T1 commits or rolls back)
  3. T1 again: SELECT balance FROM accounts WHERE id = 1; (Still returns 1000. βœ… It is repeatable β€” value didn’t change.).
  4. T1 commits: COMMIT; (Now T2’s update can proceed).

Prevents "Non-Repeatable Read":

What's a Non-Repeatable Read? When you read the same row twice in one transaction, and get different values, because another transaction modified it in between. Repeatable Read prevents this.

❗ Phantom Read Problem (Still Allowed):

What is a Phantom Read? When new rows are added that match your query filter.

Example:

  1. T1 runs: SELECT * FROM accounts WHERE balance > 1000; (returns 2 rows)
  2. T2 inserts a new row: INSERT INTO accounts (id, name, balance) VALUES (3, 'Bob', 1500); COMMIT;
  3. T1 runs the same query again: SELECT * FROM accounts WHERE balance > 1000; (returns 3 rows now)
  4. βœ… This is a phantom row, and Repeatable Read does not prevent this.

Summary of Behavior:

Problem Type Allowed?
Dirty Reads ❌ No
Non-Repeatable Reads ❌ No
Phantom Reads βœ… Yes

Real-Life Analogy: Imagine you’re filling out a tax form based on your bank statement. Repeatable Read ensures your bank balance doesn’t change halfway while filling the form. But someone may open a new account during that time (phantom).

JDBC Example in Java: Connection conn = DriverManager.getConnection(...); conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); This ensures any SELECT statements are repeatable during the transaction.

When to Use Repeatable Read: You care about consistent reads of the same rows. Example: financial applications, inventory updates. But: Phantom reads are still a problem if you rely on range queries or filters (e.g., WHERE age > 25).

4. Serializable

Serializable ensures full isolation by making concurrent transactions appear as if they were executed one after another, serially, not concurrently. It prevents:

πŸ“Œ It's the highest isolation level and guarantees full consistency, but can significantly impact performance due to locking and blocking.

What Serializable Prevents:

Problem Type Prevented?
Dirty Reads βœ… Yes
Non-Repeatable Reads βœ… Yes
Phantom Reads βœ… Yes

How It Works Internally:

Under the hood, the database might:

In databases like PostgreSQL and SQL Server, range-locks or predicate locks are used to prevent phantom rows from appearing.

Example: Phantom Reads Blocked

Database: accounts

id name balance
1 Alice 1000
2 Bob 2000

Transaction 1 (T1)

BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; SELECT COUNT(*) FROM accounts WHERE balance > 1000; (returns 1 Bob)

Transaction 2 (T2)

BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; INSERT INTO accounts (id, name, balance) VALUES (3, 'Carol', 3000); (❌ BLOCKED or may cause serialization failure)

If T1 finishes first, T2 might:

Compared to Repeatable Read:

Feature Repeatable Read Serializable
Prevent Dirty Read βœ… Yes βœ… Yes
Prevent Non-Repeatable βœ… Yes βœ… Yes
Prevent Phantom Read ❌ No βœ… Yes
Performance 🟒 Fast πŸ”΄ Slow
Locking Row-level Row + Range

Summary of Serializable Behavior:

Real-World Analogy: Imagine a single-line checkout counter at a store. Each customer is served one at a time. No overlapping activity. Very safe, but slow if there are many customers.

JDBC Example (Java): Connection conn = DriverManager.getConnection(...); conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); conn.setAutoCommit(false); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM accounts WHERE balance > 1000"); Now you’re operating in a fully isolated serializable mode.

When to Use Serializable:

Use it when:

Avoid it when:

🚨 Warning:

Summary of Concurrency Issues

Isolation Level Prevents Dirty Reads Prevents Non-Repeatable Reads Prevents Phantom Reads
Read Uncommitted No No No
Read Committed Yes No No
Repeatable Read Yes Yes No
Serializable Yes Yes Yes

JDBC Example Code (Java)


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class TransactionIsolationDemo {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/your_database"; // Replace with your DB URL
        String user = "your_username";
        String password = "your_password";

        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            // Demonstrating transaction isolation levels
            demoReadUncommitted(conn);
            demoReadCommitted(conn);
            demoRepeatableRead(conn);
            demoSerializable(conn);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    private static void demoReadUncommitted(Connection conn) throws SQLException {
        System.out.println("\n--- Read Uncommitted Isolation Level ---");
        conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);

        try {
            conn.setAutoCommit(false);

            // Transaction A: Update without committing
            try (PreparedStatement stmt = conn.prepareStatement("UPDATE accounts SET balance = balance - 100 WHERE id = 1")) {
                stmt.executeUpdate();
                System.out.println("Transaction A: Updated balance, not committed yet.");
            }

            // Transaction B: Read uncommitted data
            try (PreparedStatement stmt = conn.prepareStatement("SELECT balance FROM accounts WHERE id = 1");
                 ResultSet rs = stmt.executeQuery()) {
                if (rs.next()) {
                    System.out.println("Transaction B: Read uncommitted balance = " + rs.getInt("balance"));
                }
            }

            conn.rollback(); // Rollback Transaction A
        } finally {
            conn.setAutoCommit(true);
        }
    }

    private static void demoReadCommitted(Connection conn) throws SQLException {
        System.out.println("\n--- Read Committed Isolation Level ---");
        conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

        try {
            conn.setAutoCommit(false);

            // Transaction A: Update and commit
            try (PreparedStatement stmt = conn.prepareStatement("UPDATE accounts SET balance = balance - 50 WHERE id = 1")) {
                stmt.executeUpdate();
                conn.commit();
                System.out.println("Transaction A: Committed balance update.");
            }

            // Transaction B: Read committed data
            try (PreparedStatement stmt = conn.prepareStatement("SELECT balance FROM accounts WHERE id = 1");
                 ResultSet rs = stmt.executeQuery()) {
                if (rs.next()) {
                    System.out.println("Transaction B: Read committed balance = " + rs.getInt("balance"));
                }
            }
        } finally {
            conn.setAutoCommit(true);
        }
    }

    private static void demoRepeatableRead(Connection conn) throws SQLException {
        System.out.println("\n--- Repeatable Read Isolation Level ---");
        conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);

        try {
            conn.setAutoCommit(false);

            // Transaction B: Read initial balance
            try (PreparedStatement stmt = conn.prepareStatement("SELECT balance FROM accounts WHERE id = 1");
                 ResultSet rs = stmt.executeQuery()) {
                if (rs.next()) {
                    System.out.println("Transaction B: Initial balance = " + rs.getInt("balance"));
                }
            }

            // Transaction A: Update balance
            try (PreparedStatement stmt = conn.prepareStatement("```html

        

Java JDBC Connection Guide

1. Register the Driver Class

The first step is to load the database driver class into memory.
This tells Java which database driver to use for making the connection.

Code Example:

Class.forName("com.mysql.cj.jdbc.Driver");

Explanation:

2. Create the Connection Object

Establish a connection to the database using the DriverManager class.

<>Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/your_database_name", "username", "password");

Explanation:

Snowflake Connection Example

import java.sql.Connection; import java.sql.DriverManager; import java.util.Properties; public class SnowflakeConnectionWithProperties { public static void main(String[] args) { try { // Snowflake connection details String url = "jdbc:snowflake://.snowflakecomputing.com"; String user = "your_username"; String password = "your_password"; // Set connection properties Properties properties = new Properties(); properties.put("user", user); properties.put("password", password); properties.put("warehouse", "your_warehouse"); properties.put("db", "your_database"); properties.put("schema", "your_schema"); // Establish connection Connection con = DriverManager.getConnection(url, properties); System.out.println("Connected to Snowflake successfully!"); // Close connection con.close(); } catch (Exception e) { e.printStackTrace(); } } }

3. Create the Statement Object

Create a Statement object to send SQL queries to the database.

Code Example:

Statement stmt = con.createStatement();

Explanation:

4. Execute the Query

Execute your SQL query using the Statement object and retrieve results if necessary.

ResultSet rs = stmt.executeQuery("SELECT * FROM your_table_name"); while (rs.next()) { System.out.println(rs.getString("column_name")); // Replace with actual column name }

Explanation:

5. Close the Connection Object

Release the database resources by closing the connection, statement, and result set objects.

rs.close(); // Close ResultSet stmt.close(); // Close Statement con.close(); // Close Connection

Explanation:

DriverManager Class

The DriverManager class is part of the java.sql package and serves as a key component of the JDBC API. Its primary purpose is to manage a list of database drivers and establish connections to databases using those drivers.

1. getConnection()

2. registerDriver(Driver driver)

This method allows you to register a database driver with the DriverManager.

Driver mySqlDriver = new com.mysql.cj.jdbc.Driver(); DriverManager.registerDriver(mySqlDriver);

3. deregisterDriver()

This method removes a registered driver from the DriverManager.

Driver mySqlDriver = new com.mysql.cj.jdbc.Driver(); DriverManager.deregisterDriver(mySqlDriver);

4. getDrivers()

This method retrieves a list of all drivers currently registered with the DriverManager.

Enumeration drivers = DriverManager.getDrivers(); while (drivers.hasMoreElements()) { Driver driver = drivers.nextElement(); System.out.println("Driver: " + driver.getClass().getName()); }

5. setLoginTimeout()

This method sets the maximum time (in seconds) that the DriverManager will wait for a database connection.

DriverManager.setLoginTimeout(30); // Waits for 30 seconds before timing out

6. getLoginTimeout()

This method retrieves the currently set login timeout value.

int timeout = DriverManager.getLoginTimeout(); System.out.println("Current login timeout: " + timeout + " seconds");

7. setLogWriter()

This method sets a PrintWriter object to log messages from the DriverManager.

DriverManager.setLogWriter(new PrintWriter(System.out)); // Logs messages to console

8. getLogWriter()

This method retrieves the current PrintWriter used for logging.

PrintWriter logWriter = DriverManager.getLogWriter(); if (logWriter != null) { logWriter.println("Logging is active"); }

Practical Example Combining All Methods

import java.sql.Connection; import java.sql.DriverManager; import java.util.Enumeration; import java.util.Properties; import java.io.PrintWriter; import com.mysql.cj.jdbc.Driver; public class DriverManagerDemo { public static void main(String[] args) { try { // Set login timeout DriverManager.setLoginTimeout(15); // Set log writer DriverManager.setLogWriter(new PrintWriter(System.out)); // Register driver (manual registration) Driver mySqlDriver = new com.mysql.cj.jdbc.Driver(); DriverManager.registerDriver(mySqlDriver); // Get all registered drivers Enumeration drivers = DriverManager.getDrivers(); while (drivers.hasMoreElements()) { System.out.println("Available Driver: " + drivers.nextElement().getClass().getName()); } // Connection using URL and credentials String url = "jdbc:mysql://localhost:3306/mydb"; String user = "username"; String password = "password"; Connection con = DriverManager.getConnection(url, user, password); System.out.println("Connected to database!"); // Close connection con.close(); // Deregister driver DriverManager.deregisterDriver(mySqlDriver); } catch (Exception e) { e.printStackTrace(); } } }

How driver Manger works

When you call driverManager.getConnection(), it scans through the registered driver and finds the best driver with respect to the URL mentioned in the connection.

Complete Code Example

import java.sql.*; public class JdbcExample { public static void main(String[] args) { try { // 1. Register the Driver Class Class.forName("com.mysql.cj.jdbc.Driver"); // 2. Create the Connection Object Connection con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/your_database_name", "username", "password"); // 3. Create the Statement Object Statement stmt = con.createStatement(); // 4. Execute the Query ResultSet rs = stmt.executeQuery("SELECT * FROM your_table_name"); while (rs.next()) { System.out.println(rs.getString("column_name")); // Replace with your column name } // 5. Close the Connection Object rs.close(); stmt.close(); con.close(); } catch (Exception e) { e.printStackTrace(); } } }

Example connecting to MySQL, PostgreSQL, and Snowflake databases:

1. Prerequisites

Before connecting to any database:

2. Connecting to MySQL

Steps:

  1. Install MySQL Client:

    Use mysql CLI tool or libraries such as MySQL Connector for Python, Java, etc.

    For Java (using JDBC):

    <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> <!-- Use the latest version --> </dependency>
  2. Database Configuration:

    • Host: localhost (or your server IP)
    • Port: 3306 (default MySQL port)
    • URL format: jdbc:mysql://<host>:<port>/<database>
  3. Connection Code (Java):

    public class MySQLConnection { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/mydatabase"; String user = "username"; String password = "password"; try (Connection conn = DriverManager.getConnection(url, user, password)) { System.out.println("Connected to MySQL!"); } catch (SQLException e) { e.printStackTrace(); } } }
  4. Test the Connection: Run the above code or use mysql -u username -p from the command line to verify.

3. Connecting to PostgreSQL

Steps:

  1. Install PostgreSQL Client:

    • Use psql CLI tool or libraries such as PostgreSQL JDBC Driver for Java.
    • For Java (using JDBC), add the following Maven dependency: <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.6.0</version> <!-- Use the latest version --> </dependency>
  2. Database Configuration:

    • Host: localhost (or your server IP)
    • Port: 5432 (default PostgreSQL port)
    • URL format: jdbc:postgresql://<host>:<port>/<database>
  3. Connection Code (Java):

    public class PostgreSQLConnection { public static void main(String[] args) { String url = "jdbc:postgresql://localhost:5432/mydatabase"; String user = "username"; String password = "password"; try (Connection conn = DriverManager.getConnection(url, user, password)) { System.out.println("Connected to PostgreSQL!"); } catch (SQLException e) { e.printStackTrace(); } } }
  4. Test the Connection: Use psql -U username -d database_name from the command line or run the code above.

4. Connecting to Snowflake

Steps:

  1. Install Snowflake Client:

    • Use Snowflake's JDBC Driver or SnowSQL CLI tool.
    • For Java (using JDBC), add the following Maven dependency: <dependency> <groupId>net.snowflake</groupId> <artifactId>snowflake-jdbc</artifactId> <version>3.13.32</version> <!-- Use the latest version --> </dependency>
  2. Database Configuration:

    • Host: <account>.snowflakecomputing.com
    • Port: 443 (default)
    • URL format: jdbc:snowflake://<account>.snowflakecomputing.com/
  3. Connection Code (Java):

    public class SnowflakeConnection { public static void main(String[] args) { String url = "jdbc:snowflake://<account>.snowflakecomputing.com/"; String user = "username"; String password = "password"; String schema = "schema_name"; String warehouse = "warehouse_name"; String database = "database_name"; try (Connection conn = DriverManager.getConnection( url, user, password + "?schema=" + schema + "&warehouse=" + warehouse + "&database=" + database)) { System.out.println("Connected to Snowflake!"); } catch (SQLException e) { e.printStackTrace(); } } }
  4. Test the Connection: Run the Java code or use SnowSQL CLI (snowsql -a account_name -u username -d database -s schema).

Common Issues & Tips

Connection Interface

Connection interface in Java is part of the JDBC API and is provided by the java.sql package. It represents a single connection to a database, enabling Java programs to interact with the database for executing queries, managing transactions, and retrieving metadata.

Methods of Connection Interface

  1. createStatement()
    • Creates a statement that allows you to send simple SQL queries to database.
    • Example: Statement stm = con.createStatement();
  2. prepareStatement(String sql)
    • A PreparedStatement is a precompiled SQL statement.
    • It allows you to define a SQL query with placeholders (?) and then bind values to those placeholders before executing the query.
    • Creates a prepared statement object for executing precompiled SQL queries.
    • Useful for queries when there are multiple times with different parameters.
    • Example: PreparedStatement ps = con.prepareStatement("insert into employees(name,age) values (?,?)"); ps.setString(1, 'john'); ps.executeUpdate();
    • You can use PreparedStatement for:
      • SELECT String sql = "SELECT * FROM employees WHERE department = ?"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, "Engineering"); ResultSet rs = stmt.executeQuery();
      • INSERT String sql = "INSERT INTO employees (id, name, department, salary) VALUES (?, ?, ?, ?)"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setInt(1, 10); stmt.setString(2, "Alex"); stmt.setString(3, "HR"); stmt.setDouble(4, 70000.00); stmt.executeUpdate();
      • UPDATE String sql = "UPDATE employees SET salary = ? WHERE name = ?"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setDouble(1, 95000.00); stmt.setString(2, "Alice Johnson"); stmt.executeUpdate();
      • DELETE String sql = "DELETE FROM employees WHERE id = ?"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setInt(1, 3); stmt.executeUpdate();
      • Note: In PreparedStatement, the position always starts from 1, not 0.
  3. prepareCall(String sql)
    • Creates a callable object for executing stored procedures.
    • Example: CallableStatement pc = con.prepareCall("call getDetails(?)");
    • pc.setInt(1);
  4. setAutoCommit(Boolean autoCommit)
    • When auto-commit is true, each SQL statement is automatically saved.
    • Example: con.setAutoCommit(true);
  5. commit()
    • Manually commits the current transaction.
    • Example: try { Connection con = DriverManager.getConnection(); con.setAutoCommit(false); // database operation con.commit(); }
  6. rollback()
    • Rolls back the current transaction, undoing the changes made by the last commit.
    • Savepoint: it's like a bookmark. We can roll back to a savepoint instead of a complete rollback.
    • Example: Connection con = null; Savepoint save = null; try { con = DriverManager.getConnection(""); con.setAutoCommit(false); Statement s = con.createStatement(); s.executeUpdate("insert into students (id,name) values (1,'abc')"); save = con.setSavepoint("savepoint"); con.commit(); } catch (Exception e) { if (con != null) { try { if (save != null) { con.rollback(save); } else { con.rollback(); } } } }
  7. close()
    • Closes the connection and frees the resources.
  8. setTransactionIsolation(int level)
    • TRANSACTION_READ_UNCOMMITTED
    • TRANSACTION_READ_COMMITTED
    • TRANSACTION_REPEATABLE_READ
    • TRANSACTION_SERIALIZABLE
    • Example: con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
  9. getMetaData()
    • Returns metadata about the database, such as version, product name, and other info.
    • Example: DatabaseMetaData md = con.getMetaData(); System.out.println(md.getDatabaseProductName());
  10. isClosed()
    • Checks if the connection is closed.
    • Example: if (con.isClosed()) { System.out.println("Connection is closed"); }
  11. getAutoCommit()
    • Gets the auto-commit mode.
    • Example: boolean b = con.getAutoCommit(); System.out.println(b);
  12. isReadOnly()
    • Checks if the connection is read-only.
  13. setReadOnly(boolean readOnly)
    • Sets the connection to read-only.
    • Example: con.setReadOnly(true);
  14. nativeSQL(String sql)
    • Converts a standard SQL query to a database-specific SQL query.
    • Example: String nativeSql = con.nativeSQL("select * from student");
  15. setCatalog(String catalog)
    • Sets the database for the connection when we don't pass the database in the URL.
    • Example: String url = "jdbc:snowflake://account.region.snowflakecomputing.com/"; Connection conn = DriverManager.getConnection(url, user, password); conn.setCatalog("MY_DATABASE"); // Set database here
    • Example code: import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class SetCatalogExample { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/"; // MySQL connection URL String user = "root"; String password = "password"; try (Connection connection = DriverManager.getConnection(url, user, password)) { System.out.println("Connected to the database server."); // Set catalog to 'sales_db' connection.setCatalog("sales_db"); System.out.println("Catalog set to: " + connection.getCatalog()); // Perform operations on 'sales_db' here... // Change catalog to 'hr_db' connection.setCatalog("hr_db"); System.out.println("Catalog set to: " + connection.getCatalog()); // Perform operations on 'hr_db' here... } catch (SQLException e) { e.printStackTrace(); } } }
  16. getCatalog()
    • Gets the current catalog.
    • Example: String databaseName = con.getCatalog();
  17. setSchema(String schema)
    • Sets the schema.
    • Example: conn.setSchema("PUBLIC");

Statement Interface Methods

The Statement interface uses SQL statements to retrieve data from a database.

1. executeQuery(String sql)

Executes an SQL SELECT query and returns the result as a ResultSet.

Use Case: When you want to retrieve data from the database.

Statement s = con.createStatement();
ResultSet rs = s.executeQuery("SELECT * FROM table");
while (rs.next()) {
    System.out.println("Employee ID: " + rs.getInt("id"));
    System.out.println("Employee Name: " + rs.getString("name"));
}

2. executeUpdate(String sql)

Executes SQL statements like INSERT, DELETE, UPDATE that modify the database. It returns the number of rows affected.

Use Case: When modifying the database.

Statement s = con.createStatement();
int num = s.executeUpdate("UPDATE name SET age='asdas' WHERE id=1");

3. execute(String sql)

Executes any SQL statement. It returns true if the result is a ResultSet (e.g., SELECT), or false if it is an update count (e.g., INSERT, UPDATE, DELETE).

Use Case: When the type of SQL query is not known at compile time.

Statement s = con.createStatement();
boolean isResultSet = s.execute("SELECT * FROM employees");
if (isResultSet) {
    ResultSet rs = s.getResultSet();
    while (rs.next()) {
        System.out.println("ID: " + rs.getInt("id"));
    }
} else {
    int updateCount = s.getUpdateCount();
    System.out.println(updateCount + " row(s) affected.");
}

4. addBatch(String sql)

Adds an SQL command to the current batch of commands. Instead of executing each SQL command individually, the commands are accumulated in a batch and executed together in one go using the executeBatch() method. This approach reduces the overhead of multiple network roundtrips to the database and improves performance, especially for large-scale data manipulation.

Use Case: Used for batch processing.

Statement s = con.createStatement();
s.addBatch("INSERT INTO employees (id, name) VALUES (1, 'John')");
s.addBatch("INSERT INTO employees (id, name) VALUES (2, 'Jane')");
int[] results = s.executeBatch();
System.out.println("Batch execution completed.");
for (int i = 0; i < results.length; i++) {
    System.out.println("Command " + (i + 1) + ": Rows affected = " + results[i]);
}

5. executeBatch()

Executes all SQL commands added to the current batch and returns an array of update counts.

Use Case: Execute multiple SQL commands in a single batch.

int[] results = s.executeBatch();
for (int result : results) {
    System.out.println("Update count: " + result);
}

6. clearBatch()

Clears all SQL commands from the current batch.

Use Case: Reset the batch command list.

s.clearBatch();

7. close()

Closes the Statement object and releases database resources.

Use Case: Clean up after executing statements.

s.close();

8. getResultSet()

Retrieves the current ResultSet object from a Statement. If we don’t directly give like ResultSet rs = s.execute("query"); then we should use getResultSet().

Use Case: Access the result of a previously executed SELECT query.

ResultSet rs = s.getResultSet();

9. getUpdateCount()

Returns the number of rows affected by the last executed SQL command.

Use Case: Retrieve update count after an INSERT, UPDATE, or DELETE statement.

int updateCount = s.getUpdateCount();

10. getMoreResults()

Moves to the next result in a batch of results.

Use Case: Process multiple results from a statement execution.

boolean moreResults = s.getMoreResults();

11. setMaxRows(int max)

Sets the maximum number of rows a ResultSet can contain.

Use Case: Limit query results.

s.setMaxRows(10);

12. getMaxRows()

Retrieves the maximum number of rows for a ResultSet.

Use Case: Check the current row limit.

int maxRows = s.getMaxRows();

13. setQueryTimeout(int seconds)

Sets the time in seconds for a query to execute before timing out.

Use Case: Prevent queries from running indefinitely.

s.setQueryTimeout(30);

14. getQueryTimeout()

Retrieves the current query timeout value.

Use Case: Check the query timeout setting.

int timeout = s.getQueryTimeout();

15. cancel()

Cancels the execution of a running SQL statement.

Use Case: Abort a long-running query.

s.cancel();

16. isClosed()

Checks whether the statement has been closed.

Use Case: Verify the statement's state.

boolean closed = s.isClosed();

17. setFetchSize(int rows)

Gives a hint to the JDBC driver about the number of rows to fetch in each database roundtrip.

Use Case: Optimize performance for large result sets.

s.setFetchSize(50);

18. getFetchSize()

Retrieves the number of rows suggested for each fetch.

Use Case: Monitor fetch size setting.

int fetchSize = s.getFetchSize();

19. getFetchDirection()

Retrieves the current fetch direction for a ResultSet.

Use Case: Determine fetch behavior.

int fetchDirection = s.getFetchDirection();

20. setFetchDirection(int direction)

Sets the fetch direction for rows in a ResultSet (e.g., ResultSet.FETCH_FORWARD).

Use Case: Control traversal direction.

s.setFetchDirection(ResultSet.FETCH_FORWARD);

Scrollable ResultSet – What is it?

A Scrollable ResultSet allows you to move the cursor in multiple directions, not just forward. You can:

How to Create a Scrollable ResultSet?

When you create a Statement object, you specify two parameters:

  1. ResultSet Type – Specifies whether the ResultSet is scrollable and sensitive to database changes.
  2. Concurrency Mode – Specifies whether you can update the ResultSet.
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

Let’s focus on ResultSet Type (scrollability):

ResultSet Types (Scrollability)

ResultSet Type Description
TYPE_FORWARD_ONLY Default Type. Cursor moves forward only. Cannot go backward or jump to specific rows.
TYPE_SCROLL_INSENSITIVE Cursor can move forward, backward, and jump to specific rows. Changes made in the database are NOT reflected in the ResultSet.
TYPE_SCROLL_SENSITIVE Cursor can move forward, backward, and jump to specific rows. Changes made in the database ARE reflected in the ResultSet if the row data is modified.

1. TYPE_FORWARD_ONLY (Default)

Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
while (rs.next()) {
    System.out.println(rs.getInt("id") + " - " + rs.getString("name"));
}

2. TYPE_SCROLL_INSENSITIVE

Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet rs = stmt.executeQuery("SELECT * FROM users");

rs.last(); // Move to the last row
System.out.println("Last Row ID: " + rs.getInt("id"));

rs.first(); // Move to the first row
System.out.println("First Row ID: " + rs.getInt("id"));

rs.absolute(2); // Move to the second row
System.out.println("Second Row ID: " + rs.getInt("id"));

rs.previous(); // Move to the previous row (first row)
System.out.println("Back to First Row ID: " + rs.getInt("id"));

Key Point: Even if data changes in the database after you fetch the ResultSet, those changes are NOT reflected.

3. TYPE_SCROLL_SENSITIVE

Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet rs = stmt.executeQuery("SELECT * FROM users");

rs.absolute(2);
System.out.println("Row 2 Name: " + rs.getString("name"));

// Imagine someone updates this row in the database
// The updated data will be reflected if you access this row again
System.out.println("Row 2 Updated Name: " + rs.getString("name"));

Key Point: If another user or process updates the data in the database, your ResultSet will show the updated values for the current row. Not all drivers support this. It may fall back to TYPE_SCROLL_INSENSITIVE if the driver doesn’t support it.

Concurrency Modes (Optional - Second Parameter)

Concurrency Mode Description
CONCUR_READ_ONLY Default Mode. Data can only be read; cannot be updated through the ResultSet.
CONCUR_UPDATABLE Allows updating the ResultSet and reflecting those changes in the database.
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);

Summary: Comparing ResultSet Types

Type Can Move Backward? Jump to Specific Row? Sees DB Changes? Performance
TYPE_FORWARD_ONLY ❌ No ❌ No βœ… Yes βœ… Fastest
TYPE_SCROLL_INSENSITIVE βœ… Yes βœ… Yes ❌ No ⏳ Slower
TYPE_SCROLL_SENSITIVE βœ… Yes βœ… Yes βœ… Yes 🐒 Slowest

When to Use Which?

Scenario Recommended Type
Just reading data, moving forward TYPE_FORWARD_ONLY (Fastest)
Need to move back & forth, or jump TYPE_SCROLL_INSENSITIVE
Need to see database updates TYPE_SCROLL_SENSITIVE

Key Points to Remember:

Practical Example Summary

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testdb", "root", "password");

// Create Scrollable ResultSet (Insensitive)
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet rs = stmt.executeQuery("SELECT * FROM users");

if (rs.last()) {
    System.out.println("Last Row ID: " + rs.getInt("id"));
}

rs.first();
System.out.println("First Row ID: " + rs.getInt("id"));

rs.absolute(2);
System.out.println("Second Row ID: " + rs.getInt("id"));

rs.previous();
System.out.println("Back to First Row ID: " + rs.getInt("id"));

ResultSet

1. next() Method

2. previous() Method

3. first() Method

4. last() Method

5. absolute(int row) Method

6. relative(int rows) Method

7. beforeFirst() Method

8. afterLast() Method

Methods to Check Cursor Position (No Movement)

9. isBeforeFirst()

10. isAfterLast()

11. isFirst()

12. isLast()

Data Retrieval Methods

1. getString(int columnIndex) / getString(String columnLabel)

2. getInt(int columnIndex) / getInt(String columnLabel)

3. getLong(int columnIndex) / getLong(String columnLabel)

4. getDouble(int columnIndex) / getDouble(String columnLabel)

5. getFloat(int columnIndex) / getFloat(String columnLabel)

6. getBoolean(int columnIndex) / getBoolean(String columnLabel)

7. getByte(int columnIndex) / getByte(String columnLabel)

8. getShort(int columnIndex) / getShort(String columnLabel)

9. getDate(int columnIndex) / getDate(String columnLabel)

10. getTime(int columnIndex) / getTime(String columnLabel)

11. getTimestamp(int columnIndex) / getTimestamp(String columnLabel)

12. getObject(int columnIndex) / getObject(String columnLabel)

13. getBigDecimal(int columnIndex) / getBigDecimal(String columnLabel)

14. getBytes(int columnIndex) / getBytes(String columnLabel)

15. getBlob(int columnIndex) / getBlob(String columnLabel)

16. getClob(int columnIndex) / getClob(String columnLabel)

Update Methods

1. updateString(int columnIndex, String x) / updateString(String columnLabel, String x)

2. updateInt(int columnIndex, int x) / updateInt(String columnLabel, int x)

3. updateDouble(int columnIndex, double x) / updateDouble(String columnLabel, double x)

4. updateObject(int columnIndex, Object x) / updateObject(String columnLabel, Object x)

5. insertRow()

6. updateRow()

7. deleteRow()

8. moveToInsertRow()

9. moveToCurrentRow()

Important Notes!

* The ResultSet must be updatable (CONCUR_UPDATABLE) to use these methods.

* Use updateRow() to save updates, or cancelRowUpdates() to discard them.

Complete Example Combining Update Methods

Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery("SELECT id, name, age, salary FROM users");
if (rs.next()) {
    // Update an existing row
    rs.updateString("name", "John Updated");
    rs.updateInt("age", 40);
    rs.updateRow();
}

// Insert a new row
rs.moveToInsertRow();
rs.updateInt("id", 10);
rs.updateString("name", "New User");
rs.updateInt("age", 25);
rs.updateDouble("salary", 65000);
rs.insertRow();
rs.moveToCurrentRow();

// Delete a row
if (rs.absolute(3)) { rs.deleteRow(); } // Deletes the 3rd row

Metadata and State Methods

1. getMetaData()

2. getConcurrency()

3. getType()

4. getRow()

Miscellaneous Methods

1. wasNull()

2. close()

3. isClosed()

4. getCursorName()

web.xml vs. application.xml vs. server.xml

web.xml (Deployment Descriptor for a Web Application)


<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         version="3.0">
    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>com.example.MyServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/myServlet</url-pattern>
    </servlet-mapping>
</web-app>
  

application.xml (Deployment Descriptor for an EAR Application)


<application xmlns="http://java.sun.com/xml/ns/javaee"
             version="7">
    <module>
        <web>
            <web-uri>mywebapp.war</web-uri>
            <context-root>/myapp</context-root>
        </web>
    </module>
    <module>
        <ejb>myejb.jar</ejb>
    </module>
</application>
  

Key Differences

Feature web.xml application.xml
Scope Web module (WAR) Enterprise application (EAR)
Location WEB-INF/web.xml META-INF/application.xml
Purpose Configures servlets, filters, listeners, security, etc. Defines modules (WARs, EJBs) in an EAR application.
Required? Optional in Servlet 3.0+ (can use annotations) Optional in Java EE 6+ (replaced by @ApplicationPath, beans.xml, etc.)

server.xml

server.xml is the configuration file for the application server, such as IBM WebSphere, Apache Tomcat, or other servlet containers. It is different from web.xml and application.xml, which are used for application-specific configurations.

βœ… Java Code – Connect via JNDI and Query Snowflake


    import javax.naming.InitialContext;
    import javax.naming.Context;
    import javax.sql.DataSource;
    import java.sql.*;
    
    public class SnowflakeJNDIExample {
    
        public static void main(String[] args) {
            Connection conn = null;
    
            try {
                // Step 1: Get the JNDI context
                Context ctx = new InitialContext();
    
                // Step 2: Look up the DataSource
                DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/SnowflakeDS");
    
                // Step 3: Get a connection
                conn = ds.getConnection();
    
                System.out.println("βœ… Connected to Snowflake via JNDI!");
    
                // Step 4: Run a test query
                Statement stmt = conn.createStatement();
                ResultSet rs = stmt.executeQuery("SELECT CURRENT_TIMESTAMP");
    
                while (rs.next()) {
                    System.out.println("Current time from Snowflake: " + rs.getString(1));
                }
    
                rs.close();
                stmt.close();
    
            } catch (Exception e) {
                System.err.println("❌ Error connecting to Snowflake: " + e.getMessage());
                e.printStackTrace();
            } finally {
                try {
                    if (conn != null) conn.close();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
    

Defining Snowflake DataSource in server.xml


    <server description="Snowflake JNDI setup">
    
        <!-- Enable required features -->
        <featureManager>
            <feature>jdbc-4.2</feature>
        </featureManager>
    
        <!-- Snowflake JDBC Driver -->
        <library id="SnowflakeLib">
            <fileset dir="${server.config.dir}/lib" includes="snowflake-jdbc-*.jar"/>
        </library>
    
        <!-- DataSource configuration -->
        <dataSource id="SnowflakeDS" jndiName="jdbc/SnowflakeDS">
            <jdbcDriver libraryRef="SnowflakeLib"/>
            <properties>
                <property name="URL" value="jdbc:snowflake://<account>.snowflakecomputing.com/?user=<user>&password=<pwd>&db=<db>&schema=<schema>&warehouse=<wh>"/>
            </properties>
        </dataSource>
    
    </server>
    

Directory Structure with Snowflake JDBC Driver

    MyWebApp/
    β”œβ”€β”€ WEB-INF/
    β”‚   β”œβ”€β”€ web.xml
    β”‚   └── lib/
    β”‚       └── snowflake-jdbc-<version>.jar  <-- if not in server lib
    └── SnowflakeJNDIExample.java
    

server.xml in Different Servers

1. WebSphere Application Server (server.xml)

Example of server.xml in WebSphere 8.5


<server description="MyServer">
    <featureManager>
        <feature>jsp-2.2</feature>
        <feature>servlet-3.0</feature>
    </featureManager>

    <httpEndpoint id="defaultHttpEndpoint" host="*" httpPort="9080" httpsPort="9443"/>

    <applicationManager autoExpand="true"/>

    <webApplication location="mywebapp.war" contextRoot="/myapp"/>
</server>
  

2. Apache Tomcat (server.xml)

Example of server.xml in Tomcat


<Server port="8005" shutdown="SHUTDOWN">
    <Service name="Catalina">
        <Connector port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443" />
        <Engine name="Catalina" defaultHost="localhost">
            <Host name="localhost" appBase="webapps"
                  unpackWARs="true" autoDeploy="true">
                <Context path="/myapp" docBase="mywebapp"/>
            </Host>
        </Engine>
    </Service>
</Server>
  

Key Differences

Feature web.xml application.xml server.xml
Scope Web module (WAR) Enterprise app (EAR) Application server
Location WEB-INF/web.xml META-INF/application.xml WebSphere: config/,
Tomcat: conf/
Purpose Configures servlets, filters, security, etc. Defines WARs & JARs in an EAR Configures server settings, ports, and app deployment
Who Controls It? Application developer Application assembler Server administrator

When Do You Use Each?

Understanding Network Protocol Layers in Simple Terms πŸš€

Think of the internet as a postal service delivering messages (data) from one place (computer) to another. This entire system works in layers, each handling a different part of the process.

1️⃣ Link Layer (The Delivery Truck 🚚)

What it does:

Examples:

Analogy:

The link layer is like a delivery truck that carries letters (data) from one city (computer) to another.

2️⃣ Internet Layer (Finding the Right Address πŸ“)

What it does:

Examples:

Analogy:

The internet layer is like a postal service assigning addresses to every house so letters (data packets) go to the correct place.

3️⃣ Transport Layer (Ensuring Safe & Fast Delivery πŸ“¦βœˆοΈ)

What it does:

Examples:

Analogy:

TCP is like a courier (FedEx, DHL) that ensures delivery with tracking and confirmation. UDP is like a postcard – sent quickly, but no guarantee it arrives.

4️⃣ Application Layer

Application Layer Explained in Simple Terms πŸš€

The Application Layer is the topmost layer of the network model, and it is the one directly used by humans. It defines how applications (like web browsers, email clients, and file transfer programs) interact with the network. Think of it as the user interface of the internetβ€”it ensures that when you visit a website, send an email, or download a file, the data is sent and received in a way your software understands.

πŸ”Ή What Does the Application Layer Do?

  1. Provides Network Services to Users
    • Applications like Google Chrome, Gmail, and WhatsApp rely on this layer to send and receive data.
  2. Formats Data for Communication
    • Converts data into the correct format before sending (e.g., a web page request).
  3. Handles Authentication & Encryption
    • Secure connections using SSL/TLS (like HTTPS websites).

πŸ”Ή Real-World Examples of the Application Layer Protocols

Protocol Purpose Example Usage
HTTP (Hypertext Transfer Protocol) Loads web pages Browsing websites (Google, Facebook, YouTube)
HTTPS (Secure HTTP) Loads web pages securely Secure transactions (Online banking, shopping)
FTP (File Transfer Protocol) Transfers files Uploading/downloading files (Google Drive, OneDrive)
SMTP (Simple Mail Transfer Protocol) Sends emails Sending emails (Gmail, Outlook)
IMAP (Internet Message Access Protocol) Retrieves emails Reading emails without downloading them
POP3 (Post Office Protocol v3) Downloads emails Fetching emails to store on your device
DNS (Domain Name System) Translates domain names to IP addresses When you type google.com, it finds the right IP address
SSH (Secure Shell Protocol) Securely connects to another computer Logging into a remote server
Telnet Remote access to a device (Rarely used now, replaced by SSH)

πŸ”Ή Application Layer in Action (Everyday Example)

πŸ“Œ Scenario: You Visit Google.com
1️⃣ You type www.google.com in your browser.
2️⃣ The browser uses DNS to find Google's IP address.
3️⃣ The browser sends an HTTP request to that IP address.
4️⃣ Google's server sends back the web page using HTTP.
5️⃣ Your browser displays the webpage.
πŸ”Ή The Application Layer handles steps 2-5, making sure your request reaches Google and the webpage loads properly.

πŸ”Ή Analogy: The Application Layer is Like a Waiter at a Restaurant 🍽️

πŸ”Ή Why is the Application Layer Important?

🎯 Final Takeaway

πŸ”Ή Real-Life Analogy: Sending a Letter πŸ“¬

Imagine you’re sending a letter to a friend in another city. The process involves several steps, just like data traveling through network layers.

Network Layer Real-Life Analogy (Letter Sending) What It Does in Networking
Application Layer You write the letter πŸ“ Creates and formats data (web page, email, file transfer)
Transport Layer You put it in an envelope βœ‰οΈ and write the address Breaks data into smaller packets & ensures delivery (TCP/UDP)
Internet Layer The postal system finds the best route πŸ“ Assigns IP addresses & finds the best path to the destination
Link Layer The delivery truck physically transports it 🚚 Moves data over Wi-Fi, Ethernet, or cables

Now let’s see how these layers work together when you visit a website.

πŸ”Ή Example: Visiting Google.com

  1. Application Layer (What the User Sees)
    • You type www.google.com into a browser.
    • The browser sends an HTTP request to Google’s server.
    • The Application Layer formats the request and sends it to the Transport Layer.
    • Protocols Used: HTTP/HTTPS, FTP, SMTP (emails), DNS
  2. Transport Layer (Breaking Data Into Packets)
    • The Transport Layer breaks the HTTP request into smaller pieces called packets.
    • It labels each packet with numbers so they can be reassembled correctly.
    • It chooses whether to use:
      • TCP (for reliable delivery, like web browsing & emails)
      • UDP (for speed, like video calls & online gaming)
    • The packets are then sent to the Internet Layer.
    • Protocols Used: TCP, UDP
  3. Internet Layer (Finding the Right Address)
    • The Internet Layer assigns an IP address to the packets.
    • It decides the best route for the data to travel.
    • Each packet is labeled with:
      • Source IP (Your Computer’s Address)
      • Destination IP (Google’s Server Address)
    • The packets are sent to the Link Layer for transmission.
    • Protocols Used: IPv4, IPv6
  4. Link Layer (Sending the Data Over the Network)
    • The Link Layer physically sends the packets over Wi-Fi, Ethernet, or fiber cables.
    • It ensures packets reach the next hop (router, switch, etc.) in the journey.
    • If a packet gets lost, it resends it.
    • Finally, the data reaches Google’s server, and the process happens in reverse to send the webpage back to you!
    • Protocols Used: Wi-Fi, Ethernet, PPP

πŸ”Ή How the Layers Work Together (Step-by-Step Data Flow)

πŸ“Œ When You Visit a Website (e.g., Google.com)

Step Layer What Happens?
1 Application Layer You type www.google.com and hit enter. HTTP sends a request.
2 Transport Layer The request is broken into packets and assigned numbers.
3 Internet Layer Each packet is given a source & destination IP address.
4 Link Layer The packets are sent physically over the internet.
5 Google's Server Receives packets, processes request, and sends webpage data back.
6 Reverse Process The webpage data is sent back using the same layers.
7 Final Display Your browser reassembles the packets and displays the website.

πŸ”Ή Final Summary

Layer What It Does? Analogy
Application Layer User interacts with apps (Web browsing, Email, FTP) Writing a letter ✍️
Transport Layer Breaks data into packets & ensures correct delivery (TCP/UDP) Putting the letter in an envelope & numbering pages πŸ“©
Internet Layer Finds the best route & assigns IP addresses (IPv4/IPv6) Choosing the best postal route πŸ“
Link Layer Physically transfers data over cables/Wi-Fi Delivery truck transports the letter 🚚

Understanding TCP and IP (TCP/IP Model)

TCP (Transmission Control Protocol) and IP (Internet Protocol) are core components of the TCP/IP model, which is the foundation of internet communication.

TCP and IP work together:

IP (Internet Protocol)

IP is responsible for addressing and routing packets from source to destination across different networks.

Key Features of IP:

  1. Logical Addressing (IP Address)
    • Every device on a network has a unique IP address (e.g., 192.168.1.1 for IPv4, 2001:db8::ff00:42:8329 for IPv6).
  2. Packet-Switched Communication
    • Data is broken into packets and sent independently.
  3. Connectionless Protocol
    • IP does not establish a connection before sending packets.
  4. Best-Effort Delivery
    • No guarantee that packets will arrive in order or at all.

IP Addressing & Routing:

Example: A device with IP 192.168.1.10 sends a packet to 8.8.8.8 (Google’s DNS). The packet is forwarded by routers to reach the destination.

TCP (Transmission Control Protocol)

TCP ensures reliable, ordered, and error-checked delivery of data between applications.

Key Features of TCP:

  1. Connection-Oriented
    • Establishes a connection before data transfer (using a three-way handshake).
  2. Reliable Delivery
    • Ensures all packets arrive and are reassembled in the correct order.
  3. Error Detection & Correction
    • Uses checksums to detect errors and acknowledgments (ACKs) to confirm receipt.
  4. Flow Control & Congestion Control
    • Adjusts transmission speed based on network conditions.

TCP Three-Way Handshake (Connection Establishment):

  1. SYN β†’ Client sends a SYN (synchronize) packet to initiate a connection.
  2. SYN-ACK β†’ Server responds with SYN-ACK (synchronize acknowledgment).
  3. ACK β†’ Client sends an ACK, and the connection is established.
Client ---- SYN ----> Server
Client <-- SYN-ACK -- Server
Client ---- ACK ----> Server  (Connection Established)
    

TCP Communication Example (Sending Data):

  1. TCP breaks data into packets.
  2. Each packet gets a sequence number.
  3. The receiver acknowledges received packets.
  4. If a packet is lost, TCP retransmits it.

HTTP (HyperText Transfer Protocol)

HTTP is an application-layer protocol used for communication between web browsers and servers. It is built on TCP/IP.

Key Features of HTTP:

  1. Stateless
    • Each request is independent; the server does not remember previous requests.
  2. Uses TCP for Reliable Delivery
    • HTTP runs over TCP (typically port 80 for HTTP, 443 for HTTPS).
  3. Request-Response Model
    • The client (browser) sends a request, and the server responds.
  4. Supports Different Methods (HTTP Verbs)
    • GET, POST, PUT, DELETE, etc.
  5. Supports Headers for Metadata
    • Headers contain details like content type, caching, authentication, etc.

HTTP Request-Response Cycle:

  1. Client Request:
    GET /index.html HTTP/1.1
    Host: www.example.com
                
  2. Server Response:
    HTTP/1.1 200 OK
    Content-Type: text/html
    Content-Length: 1024
    (Followed by the actual webpage content.)
                

Common HTTP Methods:

Method Purpose
GET Retrieve data from the server
POST Submit data to the server
PUT Update existing data
DELETE Remove data from the server
PATCH Partially update data

HTTP vs. HTTPS

Summary of TCP, IP, and HTTP

Feature IP TCP HTTP
Layer Network Transport Application
Purpose Addressing & routing Reliable data delivery Web communication
Connection Type Connectionless Connection-oriented Stateless
Main Function Routes packets between networks Ensures all packets arrive correctly Transfers web data
Protocol Example IPv4, IPv6 FTP, HTTP, SMTP use TCP REST APIs, websites

Understanding HTTP Request Methods in Detail

1. GET (Retrieve Data)

GET /products?category=laptops&page=2 HTTP/1.1
Host: example.com

Example Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "products": [
    { "id": 101, "name": "Laptop A", "price": 1000 },
    { "id": 102, "name": "Laptop B", "price": 1200 }
  ]
}

Handling Large Data

2. POST (Send Data)

POST /users HTTP/1.1
Host: example.com
Content-Type: application/json

{
  "name": "Alice",
  "email": "alice@example.com"
}

3. HEAD (Retrieve Headers Only)

4. TRACE (Debugging and Testing)

5. PUT (Update or Create Resource)

6. DELETE (Remove Resource)

7. OPTIONS (Check Allowed Methods)

Comparison of HTTP Methods

MethodSafe?Idempotent?Request Body?Use Case
GETYesYesNoRetrieve data
POSTNoNoYesCreate resource
HEADYesYesNoCheck headers
TRACEYesYesNoDebugging
PUTNoYesYesUpdate or create
DELETENoYesNoDelete resource
OPTIONSYesYesNoCheck methods

Request and Response Format

GET vs POST

GET POST
Default request methodNot default
No bodyHas body
Limited dataCan carry large data
ASCII dataSupports binary data
Not secure (visible in URL)Secure (body content)
Can be bookmarkedCannot be bookmarked
Can be cachedNot cached
Used for downloadsUsed for uploads

HTTP Status Codes

Server Overview

A server is a hardware machine running server software, accepting requests, processing them, and sending responses.

Server vs Container

Tomcat Server Configuration

Servlet Interface Hierarchy

Servlet Life cycle:

  1. Servlet class is loaded:
    When a client requests for web application, the web container first looks for the servlet class. And if the request is for the first time, the servlet class is loaded into the memory. This loading happens only once per servlet class.
  2. Servlet instance is created:
    After the servlet class is loaded, its instances are created to handle the servlet request. This also happens only once per servlet life cycle.
  3. INIT() method is invoked:
    Once the init method is called (this method is invoked only once to initialize the servlet), inside the method the servlet can perform initialization tasks like (database connection, configuration).
  4. Request Handling – service():
    This is used for handling the request. In this phase, service method is invoked.
  5. destroy():
    Once the server is turned off, all the servlet and associated resources are removed.

Web.xml (deployment descriptor)

Where should the web.xml be created: It should be created in WEBAPP β†’ (HTML, CSS), WEB-INF β†’ web.xml

The web.xml syntax:

<webapp>
    <servlet>
        <servlet-name>any name as per our wish</servlet-name>
        <servlet-class>the class in which servlet logic is written</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>any name as per our wish but should be same as above</servlet-name>
        <url-pattern>the url from which we will get the call for servlet class</url-pattern>
    </servlet-mapping>
</webapp>
    

Note: In latest versions it's not mandatory to create a web.xml, we use annotations instead.

Servlet Annotation:

@WebServlet: This annotation is used where we don’t want to create a URL mapping using web.xml

Servlet Interface:

HttpServletRequest

βœ… 1. Request Info

MethodDescription
getMethod()Returns the HTTP method (GET, POST, etc)
getRequestURI()Returns the part of the URL after the domain
getRequestURL()Full URL (protocol + server + URI)
getContextPath()App root path (e.g., /myapp)
getServletPath()Path to the servlet
getPathInfo()Extra path after the servlet
getProtocol()HTTP version (e.g., HTTP/1.1)
getServerName()Hostname (e.g., localhost)
getServerPort()Port number (e.g., 8080)
getRemoteAddr()Client IP address

βœ… 2. Request Parameters

MethodDescription
getParameter(String name)Get a single form/query parameter
getParameterValues(String name)Get multiple values (e.g., checkboxes)
getParameterMap()Returns a map of all request parameters
getQueryString()Returns the raw query string
getInputStream()Binary request body (e.g., file upload)
getReader()Character input stream from the request body (e.g., JSON)

βœ… 3. Headers and Cookies

MethodDescription
getHeader(String name)Get a specific request header
getHeaders(String name)All values for a header
getHeaderNames()Enumeration of all headers
getCookies()Returns all cookies sent by the client

βœ… 4. Session and Attributes

MethodDescription
getSession() / getSession(boolean)Get or create HTTP session
getAttribute(String name)Get attribute set on request
setAttribute(String name, Object value)Add custom data to request
removeAttribute(String name)Remove attribute
getLocale()Client locale (language preference)
getRequestDispatcher(String path)Forward or include another resource (used with forward() or include())

HttpServletResponse

βœ… 1. Status & Headers

MethodDescription
setStatus(int sc)Set HTTP status code (e.g., 200, 404)
sendError(int sc)Send error response
sendError(int sc, String msg)Send custom error message
sendRedirect(String location)Redirect client to another URL
setHeader(String name, String value)Add or overwrite header
addHeader(String name, String value)Add additional header (doesn’t overwrite)
setIntHeader(String name, int value)Integer header
setDateHeader(String name, long date)Date/time header

βœ… 2. Content Settings

MethodDescription
setContentType(String type)Set response MIME type (e.g., text/html, application/json)
setCharacterEncoding(String charset)Set character encoding (e.g., UTF-8)
setContentLength(int len)Set response size
setBufferSize(int size)Set response buffer size
flushBuffer()Force content to be sent immediately

βœ… 3. Output Streams

MethodDescription
getWriter()Character stream to write text data
getOutputStream()Binary stream to write binary data
reset()Clears buffer before response is committed
resetBuffer()Clears only the buffer

βœ… 4. Cookies

MethodDescription
addCookie(Cookie cookie)Send a cookie to the client

Simple program 

Servlert class
package servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Addservlet extends HttpServlet{

public void service(HttpServletRequest req , HttpServletResponse res) throws IOException
{
    int num1= Integer.parseInt(req.getParameter("num1"));
    int num2= Integer.parseInt(req.getParameter("num2"));
    int result=num1+num2;

    PrintWriter pw=res.getWriter();
    pw.print(result);
}

}

Index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form  action="add">

 Enter num1:<input type='text' name='num1'> 
 Enter num2:<input type='text' name='num2'> 
 <input type="submit">
</form>
</body>
</html>

Web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
  <display-name>servlet</display-name>
  
  <servlet>
  <servlet-name>add</servlet-name>
  <servlet-class>servlet.Addservlet</servlet-class>
  </servlet>
  <servlet-mapping>
  <servlet-name>add</servlet-name>
  <url-pattern>/add</url-pattern>
  </servlet-mapping>
  
</web-app>

Request Dispatcher
Calling one servlet from another servlet this can be done using request dispatcher.
Index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="add">
enter num1:<input type="text" name="num1" >
enter num2:<input type="text" name="num2" >
<input type="submit"'>
</form>
</body>
</html>

Add servlet
package servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Addservlet extends HttpServlet {

    public void service(HttpServletRequest req,HttpServletResponse res) throws IOException, ServletException
    {
        int num1=Integer.parseInt(req.getParameter("num1"));
        int num2=Integer.parseInt(req.getParameter("num2"));
        int result=num1+num2;
        //PrintWriter pw=res.getWriter();
        //pw.print(result);
        req.setAttribute("num1", num1);
        req.setAttribute("num2", num2);
        RequestDispatcher rd= req.getRequestDispatcher("sq");
        rd.forward(req, res);
    }
}

Sqservlet 
package servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Sq extends HttpServlet {

    public void service(HttpServletRequest req,HttpServletResponse res) throws IOException
    {
        int num1= (int) req.getAttribute("num1");
        int num2=(int) req.getAttribute("num2");
        int result= num1*num2;
        PrintWriter pw=res.getWriter();
        pw.print(result);
    //    RequestDispatcher rd= req.getRequestDispatcher("sq");
    }
}

Web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
  <display-name>Add</display-name>
  <servlet>
  <servlet-name>add</servlet-name>
  <servlet-class>servlet.Addservlet</servlet-class>
  </servlet>
  <servlet-mapping>
   <servlet-name>add</servlet-name>
   <url-pattern>/add</url-pattern>
 </servlet-mapping>
   <servlet>
  <servlet-name>square</servlet-name>
  <servlet-class>servlet.Sq</servlet-class>
  </servlet>
  <servlet-mapping>
   <servlet-name>square</servlet-name>
   <url-pattern>/sq</url-pattern>
 </servlet-mapping>

</web-app>

Calling one servlet using another servlet using reponse.sendRedirect("servet")
package servlet;

import java.io.IOException;
import java.net.http.HttpRequest;

import javax.servlet.http.*;
import javax.servlet.http.HttpServletResponse;

public class AddServlet extends HttpServlet {

    public void service(HttpServletRequest req,HttpServletResponse res) throws IOException
    {
        int num1=Integer.parseInt(req.getParameter("num1"));
        int num2=Integer.parseInt(req.getParameter("num2"));
        int result= num1+num2;
        res.sendRedirect("sq");
        // or passing results in the url along with redirecting
        res.sendRedirect("sq?="+result);
    }
}

Sqservlet  :
package servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Sqservlet  extends HttpServlet{
    public void service(HttpServletRequest req,HttpServletResponse res) throws IOException
    {
        int result= Integer.parseInt(req.getParameter("result"));
        PrintWriter pw=res.getWriter();
        pw.print(result);
    }
}

Http Session:
package servlet;

import java.io.IOException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class Addservlet extends HttpServlet{

public void service(HttpServletRequest req, HttpServletResponse res) throws IOException
{
    int num1=Integer.parseInt(req.getParameter("num1"));
    int num2=Integer.parseInt(req.getParameter("num2"));
    int result=num1+num2;
    
    HttpSession s=req.getSession();
    s.setAttribute("result", result );
    res.sendRedirect("sq");
            
}
}

package servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class Sqservlet extends HttpServlet{

public void service(HttpServletRequest req, HttpServletResponse res) throws IOException
{
    HttpSession s=req.getSession();
    int result=(int) s.getAttribute("result");
    PrintWriter pw=res.getWriter();
    pw.print(result);
}
}

Cookie:

Cookie c=new Cookie(name, value) note:value should be cookie
Response.addCookie(c);
Example:
Int res=10;
Cookie c=new Cookie("res",k+"");   we are converting int k to string k to pass in cookie

To fetch all the cokies sent
Int result;
Cookie c[]=req.getCookies():
For(Cookie ck: c)
{
    If((ck.getName()).equals("result"))
    {
        Result=ck.getValuse()
    }
}

Example 
addservlet
package servlet;

import java.io.IOException;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Addservlet extends HttpServlet {

    public void service(HttpServletRequest req,HttpServletResponse res ) throws IOException
    {
        int num1= Integer.parseInt(req.getParameter("num1"));
        int num2= Integer.parseInt(req.getParameter("num2"));
    
        Cookie c=new Cookie("num1",num1+"");
        Cookie c1=new Cookie("num2",num2+"");
        res.addCookie(c);
        res.addCookie(c1);
        res.sendRedirect("sq");
        
    }
    
}

sqservlet
package servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Sqservlet  extends HttpServlet {

    int num1=0;
    int num2=0;
    public void service(HttpServletRequest req,HttpServletResponse res ) throws IOException
    {
        Cookie c1[]=req.getCookies();
        
        for (Cookie c:c1 )
        {
            if((c.getName()).equals("num1"))
            {
                num1=Integer.parseInt(c.getValue());
            }
        
            else if((c.getName()).equals("num2"))
            {
                num2=Integer.parseInt(c.getValue());
            }
        }
            
    int result =num1+num2;
    System.out.println(result);
    PrintWriter pw=res.getWriter();
    pw.print(result);
        
    }
}

JSP

There are three types of tags in JSP

  1. Scriptlet
    • <% %> Scriptlet tag
    • <%! %> Declaration tag
    • <%= %> Expression tag
  2. Directive Tags - <%@ %>
    • Page directive: <%@ page attribute="value" %> (refer next page)
    • Include directive
    • Taglib directive
  3. Action Tags

Scriptlet Details

  1. <% %> - Anything written here is like writing inside void service(HttpServletRequest, HttpServletResponse)
  2. <%! %> - Anything written here is like writing outside the service method. You can create variables or other methods.
  3. <%@ page import=”java.*”, β€œjava.util.*” %> - Directive tags send instructions to the JSP container.
  4. <%= k %> - Equivalent to out.print(k)
  5. <jsp:... /> - Action tag

@page Directive in JSP

The @page directive in JSP is used to define global settings for a JSP page, such as its scripting language, buffer size, content type, error handling, and more. It provides instructions to the JSP container on how to process the page.

<%@ page attribute="value" %>

πŸ”₯ Attributes of @page Directive

Attribute Description Example
language Specifies the scripting language (default: Java) <%@ page language="java" %>
contentType Defines the MIME type and character encoding of the response <%@ page contentType="text/html; charset=UTF-8" %>
pageEncoding Specifies the character encoding of the JSP page <%@ page pageEncoding="UTF-8" %>
session Enables or disables the implicit session object (default: true) <%@ page session="true" %>
buffer Defines the size of the output buffer <%@ page buffer="16kb" %>
autoFlush Controls whether the buffer should be flushed automatically <%@ page autoFlush="true" %>
isThreadSafe Defines whether multiple threads can access the JSP page <%@ page isThreadSafe="true" %>
errorPage Specifies the error page for handling exceptions <%@ page errorPage="error.jsp" %>
isErrorPage Indicates if this JSP page is an error page <%@ page isErrorPage="true" %>
import Imports Java classes <%@ page import="java.util.*, java.io.*" %>

Examples for Each Attribute

1️⃣ Set Content Type (HTML with UTF-8)

<%@ page contentType="text/html; charset=UTF-8" %>

βœ” Ensures the page is displayed correctly in different languages.

2️⃣ Import Java Classes

<%@ page import="java.util.Date, java.util.List" %>

βœ” Lets you use Date and List in your JSP page without writing full package names.

3️⃣ Enable/Disable Session

<%@ page session="false" %>

βœ” If your page does not need session tracking (for better performance), set it to "false".

4️⃣ Error Handling (Redirect to Error Page)

<%@ page errorPage="error.jsp" %>

βœ” If an error occurs, the user is sent to error.jsp.

βœ… Inside error.jsp, add this:

<%@ page isErrorPage="true" %>

βœ” Now, error.jsp can access exception objects like:

<%= exception.getMessage() %>

Understanding <%@ include file="..." %> (Static Include)

Imagine you’re writing an essay for school. Your teacher gave you a header that every student must use. Instead of writing it again, you just copy-paste it at the top of your document.

Now, in JSP Terms

<%@ include file="header.jsp" %>

Example

πŸ“Œ header.jsp

<h1>Welcome to My Website</h1>
<hr>

πŸ“Œ index.jsp

<%@ include file="header.jsp" %>
<p>This is the homepage.</p>

πŸ”Ή Final Output

<h1>Welcome to My Website</h1>
<hr>
<p>This is the homepage.</p>

⚠ What’s the Catch?

If header.jsp is changed later, index.jsp won’t update automatically!

Real-World Analogy 🏠

Static Include = Copy-Pasting

When to Use <%@ include %>?

Quick Comparison

Feature <%@ include file="..." %> (Static Include)
Includes at Compile-time (before execution)
Reflects changes? ❌ No, needs server restart
Speed βœ… Faster (since it’s pre-compiled)
Best for Static files like headers, footers

<%@ taglib %>

Imagine This:

You're running a cafΓ© β˜•, and every time a customer comes in, you have to greet them manually:
πŸ‘‰ β€œHello, [Customer’s Name]! Welcome to our cafΓ©!”

JSP tag libraries (<%@ taglib %>) work the same way!

πŸ“Œ Without <%@ taglib %> (Manual Way)

<%
    String name = request.getParameter("name");
    if (name != null) {
        out.println("<h1>Hello, " + name + "!</h1>");
    } else {
        out.println("<h1>Hello, Guest!</h1>");
    }
%>

πŸ“Œ With <%@ taglib %> (Smart Way)

Step 1: Define Tag Library (mytags.tld)

<taglib xmlns="http://java.sun.com/xml/ns/j2ee" version="1.2">
    <tlib-version>1.0</tlib-version>
    <short-name>MyTags</short-name>
    
    <tag>
        <name>greetUser</name>
        <tag-class>com.example.GreetUserTag</tag-class>
        <body-content>empty</body-content>
    </tag>
</taglib>

Step 2: Java Class (GreetUserTag.java)

package com.example;

import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class GreetUserTag extends SimpleTagSupport {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void doTag() throws JspException, IOException {
        String message = (name != null) ? "Hello, " + name + "!" : "Hello, Guest!";
        getJspContext().getOut().print("<h1>" + message + "</h1>");
    }
}

Step 3: Use in JSP (index.jsp)

<%@ taglib uri="/WEB-INF/tlds/mytags.tld" prefix="mytag" %>
<mytag:greetUser name="John" />

βœ” This prints:

<h1>Hello, John!</h1>

Final Comparison

Without <%@ taglib %> With <%@ taglib %>
Writing Java code in every JSP file πŸ“ Using a simple custom tag 🎯
Repeating the same logic 😩 Code is clean and reusable 😊
Hard to manage and maintain ⚠ Easy to update and extend πŸš€

πŸ”Ή Final Takeaway

JSP Action Tags Explained Simply
JSP action tags are special tags that let you control the behavior of your JSP page dynamically. They help include files, forward requests, work with JavaBeans, and more. Let’s go through all the important action tags one by one.
________________________________________
1. <jsp:include> – Include Another File
πŸ“Œ What it does?
It adds the content of another file (like a JSP or HTML file) into the current page at runtime.
πŸ“Œ Example:
If you have a header file (header.jsp), you can include it like this:
<jsp:include page="header.jsp" />
πŸ“Œ Why use it?
If header.jsp changes, all pages using it will automatically get the updated content.
________________________________________
2. <jsp:forward> – Redirect to Another Page
πŸ“Œ What it does?
It forwards the request to another page. The user never sees the original page.
πŸ“Œ Example:
If a user is not logged in, redirect them to a login page:
<jsp:forward page="login.jsp" />
πŸ“Œ Why use it?
To move users to another page based on conditions.
________________________________________
3. <jsp:param> – Pass Extra Data
πŸ“Œ What it does?
It sends extra information when including or forwarding pages.
πŸ“Œ Example (Passing a username to another JSP file):
<jsp:include page="welcome.jsp">
    <jsp:param name="username" value="JohnDoe" />
</jsp:include>
πŸ“Œ Why use it?
To send small pieces of information like user details.
________________________________________
4. <jsp:useBean> – Create/Access JavaBean
πŸ“Œ What it does?
It creates or fetches an existing JavaBean (a simple Java object used in JSP).
πŸ“Œ Example (Creating a Bean for a User Object):
<jsp:useBean id="user" class="com.example.User" scope="session" />
πŸ“Œ Why use it?
To store user details, settings, or any reusable data.
________________________________________
5. <jsp:setProperty> – Set a Bean Property
πŸ“Œ What it does?
It updates the values of a JavaBean’s variables.
πŸ“Œ Example (Setting a user’s name in a bean):
<jsp:setProperty name="user" property="name" value="John Doe" />
πŸ“Œ Why use it?
To easily update values without writing Java code.
________________________________________
6. <jsp:getProperty> – Get a Bean Property
πŸ“Œ What it does?
It retrieves a JavaBean’s variable value and displays it.
πŸ“Œ Example (Getting the name of a user from a bean):
<jsp:getProperty name="user" property="name" />
πŸ“Œ Why use it?
To display stored data like user names, emails, etc.
________________________________________
7. <jsp:plugin> – Embed Applets (Rarely Used)
πŸ“Œ What it does?
It inserts a Java applet (a small Java program that runs in a browser). This is rarely used today.
πŸ“Œ Example:
<jsp:plugin type="applet" code="MyApplet.class" width="300" height="300">
    <jsp:param name="bgcolor" value="blue" />
</jsp:plugin>
πŸ“Œ Why use it?
You probably don’t need this, as Java applets are outdated.
________________________________________
8. <jsp:fallback> – Provide Backup for <jsp:plugin>
πŸ“Œ What it does?
If a browser does not support Java applets, this tag shows alternative content.
πŸ“Œ Example:
<jsp:plugin type="applet" code="MyApplet.class" width="300" height="300">
    <jsp:fallback>
        Sorry, your browser does not support Java applets.
    </jsp:fallback>
</jsp:plugin>
πŸ“Œ Why use it?
Again, this is rarely used today.
________________________________________
Final Summary of JSP Action Tags
Tag	Purpose
<jsp:include>	Adds another file dynamically.
<jsp:forward>	Redirects to another page.
<jsp:param>	Passes additional data.
<jsp:useBean>	Creates or fetches a JavaBean.
<jsp:setProperty>	Updates a JavaBean's variable.
<jsp:getProperty>	Displays a JavaBean's variable.
<jsp:plugin>	Embeds a Java applet (outdated).
<jsp:fallback>	Shows alternate content for plugins.
What is Expression Language (EL)?
Imagine you’re using a vending machine πŸ₯€. Instead of opening the machine and manually pulling out a drink, you just press a button (e.g., "Coca-Cola") and it magically appears!
Expression Language (EL) is like that buttonβ€”it helps you get data easily from Java objects without writing complex Java code inside JSP pages.
________________________________________
πŸ”Ή Why Use Expression Language (EL)?
❌ Without EL (Old Java Way – Too Much Code 😩)
If we want to print a username stored in a request attribute, we write this:
<%
    String user = (String) request.getAttribute("username");
    out.println("Welcome, " + user);
%>
😑 Too much Java code inside JSP! Messy and hard to read!
βœ… With EL (Simple and Clean πŸ˜ƒ)
<p>Welcome, ${username}</p>
πŸŽ‰ Much easier! No need for Java code!
________________________________________
πŸ”Ή EL Syntax (How It Works)
Expression Language (EL) always starts with ${} and can access:
β€’	Request Attributes β†’ ${requestScope.name}
β€’	Session Attributes β†’ ${sessionScope.user}
β€’	Application Attributes β†’ ${applicationScope.data}
β€’	Parameters (Form Inputs, URLs) β†’ ${param.email}
β€’	Cookies β†’ ${cookie.userName.value}
β€’	Headers β†’ ${header["User-Agent"]}
β€’	Functions (String, Math, Boolean) β†’ ${10 + 20}, ${"hello".toUpperCase()}
________________________________________
πŸ”Ή EL Examples with Real Code
1️⃣ Accessing Request Attributes (Getting values stored in request scope)
πŸ“Œ Java Code (Servlet that sets data)
request.setAttribute("username", "John123");
πŸ“Œ JSP Code (Get Data Using EL)
<p>Welcome, ${username}!</p>
βœ” Output:
Welcome, John123!
________________________________________
2️⃣ Accessing Session Attributes (Getting values stored in session scope)
πŸ“Œ Java Code (Setting Data in Session)
session.setAttribute("user", "Alice");
πŸ“Œ JSP Code (Get Data from Session)
<p>Hello, ${sessionScope.user}!</p>
βœ” Output:
Hello, Alice!
________________________________________
3️⃣ Getting Form Data (Parameters from URL or Form)
πŸ“Œ URL: http://example.com/page.jsp?email=john@example.com
πŸ“Œ JSP Code (Access Query Parameter)
<p>Your email: ${param.email}</p>
βœ” Output:
Your email: john@example.com
________________________________________
4️⃣ Accessing Cookies πŸͺ
πŸ“Œ Java Code (Setting a Cookie)
Cookie cookie = new Cookie("userName", "Mike");
response.addCookie(cookie);
πŸ“Œ JSP Code (Get Cookie Value Using EL)
<p>Welcome back, ${cookie.userName.value}!</p>
βœ” Output:
Welcome back, Mike!
________________________________________
5️⃣ Using EL for Math Calculations
<p>Sum: ${10 + 5}</p>
<p>Multiplication: ${4 * 3}</p>
<p>Division: ${20 / 4}</p>
βœ” Output:
Sum: 15
Multiplication: 12
Division: 5
________________________________________
6️⃣ Using EL Functions (String and Math)
βœ” Convert to Uppercase:
<p>${"hello".toUpperCase()}</p>
βœ” Length of a String:
<p>Length: ${fn:length("Hello World")}</p>
βœ” Math Functions:
<p>Random Number: ${Math.random() * 100}</p>

2️⃣ Ternary Operator (? :) – If-Else in EL

Syntax:

${condition ? value_if_true : value_if_false}

Example: Checking User Role

<p>${userRole == "admin" ? "You have admin access" : "You are a regular user"}</p>

βœ” If userRole == "admin", it prints:
You have admin access

βœ” Otherwise, it prints:
You are a regular user


3️⃣ Logical Operators (&&, ||, !)

Syntax:

Example: Checking if user is an admin & logged in

<c:if test="${userRole == 'admin' && not empty username}">
    <p>Welcome, Admin ${username}!</p>
</c:if>

βœ” If userRole is "admin" AND username exists, it prints:
Welcome, Admin John!


4️⃣ Checking If a Value is Empty

<p>${empty username ? "Guest" : "Welcome, " + username}</p>

βœ” If username is empty, it prints:
Guest

βœ” Otherwise, it prints:
Welcome, John!


πŸ”Ή Why Use Expression Language?

πŸ”Ή Final Takeaway 🎯