Java Backend Complete
Variable
It is a named memory location to store value.
Types of Variables:
-
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); } -
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 } -
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:
-
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., @, #, %).
-
Cannot Start with a Digit:
- Identifiers must begin with a letter, _, or $.
int _count = 0; // Valid int $money = 100; // Valid int 1stNumber = 10; // Invalid -
No Reserved Keywords:
- Keywords like
class,if,else,int, etc., cannot be used as identifiers.
- Keywords like
-
Case Sensitivity:
- Identifiers are case-sensitive.
myVarandmyvarare distinct identifiers.
-
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:
- Value Storage: Stored directly in the stack memory (for local variables). Variables hold actual values.
- Speed: Faster than non-primitive types since no references or objects are involved.
- Immutability: Values of primitives cannot be changed, but the variable can hold a new value.
- Usage: Used for simple, low-level operations requiring high performance.
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:
- Value Storage: Reference is stored in the stack memory, but the actual object resides in the heap memory. Variables do not hold actual data; they hold a reference (memory address) to the object.
- Complexity: More versatile and complex than primitives. Can have methods and properties.
-
Default Value: Non-primitive variables are initialized
to
nullif not assigned. - Memory Usage: Requires more memory because they store references and actual data on the heap.
-
Examples in Usage: A
Stringobject can store a sequence of characters with additional functionality like concatenation and substring.
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?
- Method Call Frames (Stack Frames):
- Every time a method is called, a stack frame is created in the stack memory.
- It stores the method's local variables and execution information.
- When the method execution is completed, its stack frame is removed automatically.
Example:
public class StackMemoryExample { public static void main(String[] args) { int result = add(5, 10); // Stack frame for main() created System.out.println(result); } public static int add(int a, int b) { // Stack frame for add() created return a + b; // Stack frame for add() is removed after execution } }Stack Execution Flow:
- main() starts β Stack frame for main()
- add() is called β Stack frame for add()
- add() returns β Stack frame for add() is removed
- main() ends β Stack frame for main() is removed
- Primitive Variables (for local methods):
- All local primitive variables (int, float, char, etc.) are stored in stack memory.
- These variables exist only while the method is running and get deleted once the method ends.
Example:
public class Example { public static void main(String[] args) { int num = 10; // 'num' is stored in stack memory display(); } public static void display() { char letter = 'A'; // 'letter' is stored in stack memory } // 'letter' is removed when display() ends } - References to Non-Primitive Objects:
- Object references (memory addresses) are stored in the stack.
- The actual objects are stored in the heap memory.
Example:
class Person { String name; } public class Example { public static void main(String[] args) { Person p = new Person(); // 'p' is stored in stack, but the object is in heap p.name = "John"; } }- p (reference) is stored in stack memory.
- The actual Person object (with name = "John") is stored in heap memory.
- When main() ends, the reference p is removed from the stack, but the object may still exist in the heap (until garbage collection).
Key Characteristics of Stack Memory:
- β Fast β Stack memory is faster than heap memory.
- β Auto-managed β No need for manual memory management; stack frames are removed automatically when methods return.
- β Limited size β Stack memory is smaller than heap memory and can cause StackOverflowError if too many nested method calls occur.
Heap Memory
- Definition: Heap memory is used for:
- Storing objects and class-level variables.
- Features:
- Managed by the Garbage Collector (cleans up unused objects).
- Slower access compared to the stack.
- Can grow dynamically based on program requirements.
- Example:
String str = "Hello"; // The "Hello" object is stored in heap Person person = new Person(); // `person` object is created in heap
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
- Primitive types are simpler and more lightweight; they reside in the stack.
- Non-primitive types are versatile and allow more complex data structures but are less efficient in terms of memory usage.
- The stack is for short-term storage (local variables, references), and the heap is for long-term storage (objects).
- 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:
- Also known as widening or automatic type conversion.
- Occurs when Java automatically converts a smaller data type to a larger one (e.g.,
inttolong).
Rules:
- Compatible Data Types: The source and target data types must be compatible (e.g., numeric to numeric).
- 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:
byte β short β int β long β float β double- A
floatcan store values as large as Β±3.4 Γ 10Β³βΈ, which is far beyond the range oflong(Β±9 Γ 10ΒΉβΈ). charcan also convert tointdue to its numeric representation in Unicode.
2. Explicit Type Conversion (Narrowing)
Definition:
- Also known as narrowing or manual type conversion.
- Requires a cast operator to convert a larger data type into a smaller one (e.g.,
doubletoint).
Rules:
- Potential Data Loss: The target type may lose precision or truncate values.
- 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:
double β float β long β int β short β byte- Non-compatible types like
chartointorinttocharmay also require explicit casting.
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.
- 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:
- Arithmetic Operators (
+,-,*,/,%) - Relational (Comparison) Operators (
>,>=,<,<=,!=) - Logical Operators (
&&,||,!) - Bitwise Operators
- Assignment Operators (
=,+=,-=,*=,/=) - Unary Operators
- Ternary Operator
- 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
- Part of: java.io package.
- Performance: High performance due to buffering; ideal for reading large files.
- Input Type: Works with character streams (Reader objects).
- Buffering: Buffers input, reducing the number of I/O operations.
- Thread Safety: Not synchronized (explicit synchronization required for thread safety).
Constructors
- BufferedReader(Reader in)
- BufferedReader(Reader in, int sz) (The second parameter specifies the buffer size.)
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
- Faster than unbuffered streams like InputStreamReader.
- Ideal for reading large text files or streams.
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
- Part of: java.util package.
- Performance: Slower compared to BufferedReader for large files (due to additional parsing logic).
- Input Type: Works with InputStream, File, or String objects.
- Parsing: Can parse primitive data types directly (e.g., int, double) and tokenize input.
- Thread Safety: Not synchronized.
Constructors
- Scanner(InputStream source)
- Scanner(File source)
- Scanner(String source)
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
- Easier to use for parsing user input or tokenized strings.
- Built-in support for parsing primitive types.
- Flexible for working with different input sources (e.g., files, strings, console).
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?
- Use BufferedReader:
- For reading large text files or streams efficiently.
- When you donβt need parsing or tokenization.
- When performance is critical.
- Use Scanner:
- For interactive console input or parsing structured text.
- When you need to process tokens or parse primitive data types directly.
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
- %c: character
- %d: decimal
- %s: string
- %f: float
- %x: hexadecimal
- %o: octal
- %e: scientific notation
- %n: newline
- %h: hash code
- %b: Boolean
Example: System.out.format("%h", "abc");, System.out.format("%b", 5 > 3); β Output: true
2) Flags
- -: Left justify within the given field (default is right justify). Example: System.out.format("%-10s", "hello");
- +: Add a + sign for a positive number. Example: System.out.format("%+d", 100);, System.out.format("%+d", -100);
- Space: Leaves space between the positive number. Example: System.out.format("% d", 100); (Note: there is a space between % and d)
- 0: Pads with zero instead of space. Example: System.out.format("%03d", 10);
- ,: Keeps , as a 1000βs separator. Example: System.out.format("%,10d", 1000000);
- (: Encloses negative number with (). Example: System.out.format("%(10d", -1000);
- Width: Minimum number of characters to be printed. If the value is shorter, it is padded with space. Example: System.out.format("%10s", "hello");
- *: Width with an additional parameter argument. Example: System.out.format("%*d", 10, 102);
- .number: Specifies the number of digits after the floating point number or the maximum number of characters for a string. Example: System.out.format("%.2f", 100.100);
- .*: Precision with an additional parameter. Example: System.out.format("%.*f", 2, 3.142);
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
- Class: A blueprint or template for creating objects. It defines the properties (attributes) and methods (behaviors) that objects of the class will have.
- Object: An instance of a class. It is a specific representation of the class with its own values for the properties. (Each instance of an object has its own value)
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:
- Properties (attributes): an element which stores value(These are like the features of the house, such as the number of rooms or the color.)
- Methods (behaviors): These are like the actions you can do with the house, such as opening doors or turning on lights.
2. Encapsulation
- Hiding the internal details of an object and exposing only what is necessary through well-defined interfaces.
- Achieved by using access modifiers (e.g., private, protected, public) to restrict access to certain attributes or methods.
3. Inheritance
- Enables one class (child/subclass) to inherit properties and behaviors from another class (parent/superclass).
- Promotes code reuse and a hierarchical relationship between classes.
4. Polymorphism
- Allows objects to take many forms.
- A single interface can represent different underlying forms (e.g., method overloading and method overriding).
- Example: A
draw()method behaves differently based on whether the object is aCircleor aSquare.
5. Abstraction
- Simplifying complex systems by modeling classes appropriate to the problem.
- Focuses on what an object does rather than how it does it.
- Achieved through abstract classes and interfaces.
Benefits of OOP
- Modularity: Code is organized into distinct classes and objects, making it easier to understand, debug, and maintain.
- Reusability: Inheritance and polymorphism allow for code reuse across different parts of a program.
- Scalability: Adding new features or modifying existing functionality becomes more straightforward.
- Maintainability: Encapsulation ensures that changes in one part of the system do not adversely affect other parts.
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;
}
}
- Public: Can be accessed anywhere.
- Private: Can be accessed only within the declared class.
- Default: Can be accessed within the same package.
- Protected: Can be accessed within the same package and in subclasses (regardless of package). Protected is commonly used when you want to allow controlled access to class members for subclasses while keeping those members hidden from unrelated classes.
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:
- Within the same package (like default modifier).
- 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:
- Object (supreme parent class for all classes) β Parent class β Child class.
- Since the child class inherits all features of the parent class and the
Objectclass, the child is also a parent and the child is also anObjectbecause it has inherited both parent andObject.
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
-
Access Parent Class Variables
If a subclass has a variable with the same name as in the parent class,
superhelps 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 -
Call Parent Class Methods
If a subclass overrides a method,
supercan 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 -
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:
superis used only in a subclass to refer to its parent.super()must be the first statement in a constructor.superhelps access overridden methods and hidden variables in the parent class.- If the parent has a parameterized constructor,
super(arguments)must be used.
Additional Functionalities of super
-
Calling Parent Classβs Parameterized Constructor:
If the parent class has a parameterized constructor, the child class must explicitly call it using
super(arguments). Ifsuper()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 ConstructorImportant: If the parent class only has a parameterized constructor, you must call it explicitly using
super(arguments), otherwise, compilation will fail. -
superCannot Be Used in a Static Context:Since
superrefers 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:
supercannot be referenced from a static context. -
Using
superin Method Overloading:If a parent class has multiple overloaded methods,
supercan 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 -
Using
superwithinstanceof:Even though
superitself cannot be used withinstanceof, 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:
trueThis confirms that
objis an instance ofParent. -
superand Multiple Levels of Inheritance:If there are multiple levels of inheritance,
superonly 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 ParentNote: If you want to call the
GrandParent's method, you must dosuper.super.greet();but Java does not allow this. Instead, you need to modify theParentto callsuper.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
-
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 } } -
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 } } -
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(); } } -
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:
- Accessing parent class methods:
super.methodName() - Accessing parent class fields:
super.fieldName - 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
- final Classes: Prevent a class from being inherited.
final class Parent { } class Child extends Parent { } // ERROR: Cannot inherit a final class - 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
- Code Reusability: Shared code in the parent class can be reused in child classes.
- Extensibility: Allows easy extension of functionality.
- Polymorphism: Supports dynamic method dispatch through overridden methods.
Disadvantages of Inheritance
- Tight Coupling: Subclasses are tightly coupled with the parent class, making changes in the parent affect the subclasses.
- Limited Scope for Changes: Extending a class with deep inheritance hierarchies can become rigid and harder to modify.
- Increased Complexity: Misuse of inheritance can lead to complex and hard-to-maintain code.
Best Practices
- Use inheritance only when there is a clear is-a relationship (e.g., Dog is a type of Animal).
- Avoid deep inheritance hierarchies; prefer composition over inheritance where possible.
- Use the
superkeyword 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:
- A
Carclass has anEngineandWheelclasses as members.
Imagine a Toy Car
A toy car is made of different parts like:
- Wheels
- Engine
- Lights
Now, the toy car doesn't inherit these parts. Instead, it has these parts. This means:
- The car has wheels.
- The car has an engine.
- The car has lights.
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:
- My class (e.g.,
Car) has-a part (e.g.,Engine). - Instead of inheriting the engine, the car uses the engine by keeping it inside as a member.
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?
- The
Carclass does not know how the engine works. It only knows the engine hasstart()andstop()methods. - The
Carclass uses the engine to perform its tasks.
Why is Composition Useful?
- Independent Parts: If you want to change the
Engineclass (e.g., make it an electric engine), you can replace it without touching theCarclass. - Reuse Code: If you create another class (like a
Truck), it can also use the sameEngineclass. - 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
- Definition Syntax:
public interface MyInterface { // Constant declaration: these are public, static, and final by default. // Abstract method void myMethod(); } - 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); } }
- 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; }
- Fields in an interface are implicitly:
- 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.
- Inheritance in Interfaces:
- An interface can extend another interface.
- Example:
public interface A { void methodA(); } public interface B extends A { void methodB(); }
- 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(liketoString(),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(); }
- Introduced in Java 8, a functional interface is an interface with exactly one abstract method, but it can have:
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 notmethod2, so it remains abstract.
- This class implements
- Concrete Class (ConcreteClass):
class ConcreteClass extends MyAbstractClass { @Override public void method2() { System.out.println("Method2 implemented in concrete class"); } }- Since
ConcreteClassextendsMyAbstractClass, it inherits the implementation ofmethod1and only needs to implementmethod2, making it a concrete (non-abstract) class.
- Since
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");
}
}
ConcreteClasswould need to implement bothmethod1andmethod2, even thoughmethod1is already implemented inMyAbstractClass.- By extending
MyAbstractClass, we reuse its implementation ofmethod1and avoid duplicate code.
Key Benefits of Using Interfaces
- Abstraction: Separates the definition of behaviors from their implementation.
- Multiple Inheritance: Unlike classes, a class can implement multiple interfaces.
- Polymorphism: Interfaces allow a class to be treated as a type of the interface, enabling flexible and reusable code.
- 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)?
- It is mainly to avoid the "diamond problem," where a child class inherits from two parent classes with the same method or data. Constructors of parent classes would be ambiguous to call.
Why doesn't the same issue apply to interfaces?
- Because interfaces (until Java 8) do not contain implementations, there is no ambiguity in method resolution.
- From Java 8, though, interfaces can have default methods.
- So, if a class implements two interfaces with the same default method, it must override the method in the child class.
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
- Both interfaces
AandBhave a default methodshow(). Childimplements both, causing a conflict (which one to use?).- Java forces
Childto overrideshow()to resolve ambiguity. - Inside
Child'sshow(), we can callA.super.show()orB.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
- Java 8+ allows
defaultmethods in interfaces. - If multiple interfaces have the same
defaultmethod, the child class must override it. - The child class can route to specific interface versions using
InterfaceName.super.method().
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?
- Type safety: Only allowed values can be used.
- Code readability: Self-explanatory values like
Status.ACTIVEinstead of"A". - Avoids bugs: No accidental typos or invalid values.
- Can contain methods and constructors (advanced use).
π 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
- Cannot extend a class (they already extend
Enum). - Cannot be instantiated using
new. - Cannot inherit another enum.
π¦ 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
enumis a type-safe way to define a fixed set of named constants.- You use it when your variable should only have predefined values.
- It improves code clarity, safety, and maintainability.
- Enums are more powerful than constants β they can have fields, methods, and constructors.
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:
- 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).
- 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:
- Overloading is resolved at compile-time.
- It improves code readability and flexibility.
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?
- 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.
- Extensibility: You can add new classes without modifying existing code.
- Dynamic Behavior: Allows behavior to be determined at runtime, making programs flexible and adaptable.
Key Rules for Polymorphism in Java
-
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.
-
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.
- 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
- In an abstract class, we can define non-final and non-static variables and methods can be declared as public, private, or protected.
- In an interface, variables are by default
public static finaland methods arepublicby default. - Classes can extend only one class (either a normal class or an abstract class), whereas we can implement any number of interfaces in a class.
Constructors
Constructors are special functions that are called when an object is created. They:
- Do not have a return type.
- Have the same name as the class name.
- If not explicitly created, the compiler creates a default constructor and assigns default values for primitive types (and
nullfor non-primitive types).
Final Keyword
- Final variables cannot be re-initialized.
- Final methods cannot be overridden.
- Final classes cannot be inherited.
- If a final variable in a class is not initialized, then if we call that variable, we will get a compiler error. We must initialize it either directly, through a constructor, or through an initializer block.
- All final variables are usually named in capital letters.
Static Keyword
Used to share a variable with all the objects throughout the class.
- Static method: They are mainly used to access static variables. Static variables can also be modified by non-static methods, but it is not recommended.
- Static methods are also useful when we donβt want to create an object to access a method. We can directly use
ClassName.staticMethodName(). - Static methods cannot access non-static members.
- Static methods cannot use the
thisreference.
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?
- Reduces boilerplate code (no need for anonymous classes).
- Improves readability and conciseness.
- Enhances functional programming capabilities.
- Simplifies working with collections and streams.
2. Syntax of Lambda Expressions
(parameters) -> { body }
Explanation:
parameters: Inputs to the function (can be empty, one, or multiple).->: The lambda arrow operator separates parameters from the body.{ body }: The function body that contains expressions or statements.
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:
Addableis a functional interface with a methodadd(int, int).(a, b) -> a + bimplements 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:
- You need short, simple functions.
- You want to replace anonymous inner classes.
- You're working with streams, collections, and functional programming.
β Avoid lambda expressions when:
- The logic is too complex (better use a regular method).
- You're implementing multiple methods (use a normal class instead).
8. Summary
β Lambda Expression Syntax:
(parameters) -> { body }
β Common Functional Interfaces:
Predicate<T>βboolean test(T t)(Filter)Consumer<T>βvoid accept(T t)(Process)Function<T, R>βR apply(T t)(Transform)Supplier<T>βT get()(Generate)
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
- Not a data structure: Stream does not store data. It operates on a data source (like Collection, Array, I/O channel, etc.).
- Lazy evaluation: Intermediate operations are lazy β they're not executed until a terminal operation is invoked.
- Pipelining: You can chain multiple operations together.
- 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
Stream<T>β For reference types.IntStream,LongStream,DoubleStreamβ For primitive types (specialized).
These primitive streams avoid the cost of boxing/unboxing.
1. Stream<T> β For Reference Types
- This is the most commonly used stream type.
- Works with objects like String, Employee, Person, etc.
- Elements in the stream are references (objects).
- Uses boxing/unboxing if dealing with primitive types (which is less efficient).
β 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:
sum()average()min(),max()range(),rangeClosed()
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
Summary3>
| 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:
- Java Object β JSON conversion (Serialization & Deserialization)
- Complex/nested objects
- Collections like List, Map, etc.
- Generics
- Custom type adapters
β 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:
- Pretty printing
- Serializing nulls
- Custom date format
- Exclusion strategies
- Type adapters
β€ 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:
- JsonObject
- JsonArray
- JsonPrimitive
- JsonNull
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:
- String
- Number
- Boolean
β€ 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
β 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:
- JDBC-ODBC bridge driver
- Native-API driver (partially Java driver)
- Network Protocol driver (fully Java driver)
- 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:
|
| 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:
|
| 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:
|
| 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:
|
| 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 | 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.
- Transaction 1 (T1):
BEGIN TRANSACTION; UPDATE accounts SET balance = balance - 100 WHERE id = 1;(hasn't committed yet) - Transaction 2 (T2):
SELECT balance FROM accounts WHERE id = 1;(Reads the balance as if it has decreased by 100) - Transaction 1 (T1) now decides:
ROLLBACK;(Undo the update) - 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:
- If Transaction A updates a row but hasn't committed, Transaction B cannot see that update.
- Transaction B will only see the change after Transaction A commits.
Example in SQL: Imagine a bank account with balance = 1000.
- Transaction 1 (T1):
BEGIN TRANSACTION; UPDATE accounts SET balance = balance - 100 WHERE id = 1;(balance is now 900 internally, but not yet committed) - Transaction 2 (T2):
SELECT balance FROM accounts WHERE id = 1;(It will still see 1000, the last committed value, not 900) - 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:
- T1: Reads balance = 1000
- T2: Updates balance to 900 and commits
- 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:
- No Dirty Reads β
- No Non-Repeatable Reads β
- But allows Phantom Reads β (weβll explain this too)
Behavior of Repeatable Read:
- If Transaction A reads a row, it will see the exact same data if it reads it again later β even if another transaction commits a change.
- Any UPDATE or DELETE to that row by another transaction is blocked until Transaction A finishes.
Example β Repeatable Read:
Letβs say a table has:
| id | name | balance |
|---|---|---|
| 1 | Alice | 1000 |
- Transaction 1 (T1):
BEGIN TRANSACTION; SELECT balance FROM accounts WHERE id = 1;(returns 1000) - Transaction 2 (T2):
BEGIN TRANSACTION; UPDATE accounts SET balance = 2000 WHERE id = 1;(This will BLOCK until T1 commits or rolls back) - T1 again:
SELECT balance FROM accounts WHERE id = 1;(Still returns 1000. β It is repeatable β value didnβt change.). - 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:
- T1 runs:
SELECT * FROM accounts WHERE balance > 1000;(returns 2 rows) - T2 inserts a new row:
INSERT INTO accounts (id, name, balance) VALUES (3, 'Bob', 1500); COMMIT; - T1 runs the same query again:
SELECT * FROM accounts WHERE balance > 1000;(returns 3 rows now) - β 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:
- β Dirty Reads
- β Non-Repeatable Reads
- β Phantom Reads
π 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:
- Use strict locking (on rows and ranges)
- Or use serialization graphs to detect conflicts
- Ensure that concurrent execution behaves like serial execution
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:
- Be forced to retry (e.g., in PostgreSQL)
- Or get a serialization error
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:
- Ensures complete consistency
- All concurrent transactions appear serialized
- Most safe, least concurrent
- Can lead to:
- Lock contention
- Serialization failures (requiring retries)
- Reduced throughput
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:
- Data must be absolutely consistent
- Use cases:
- Banking
- Ledger systems
- Critical audit logs
- Any system requiring ACID at maximum strength
Avoid it when:
- You need high concurrency
- You can tolerate minor read inconsistencies
π¨ Warning:
- Serializable is expensive
- Use only when needed
- Consider optimistic locking or application-level checks for performance
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:
- Replace
"com.mysql.cj.jdbc.Driver"with the driver class name for your database (e.g.,"org.postgresql.Driver"for PostgreSQL,"oracle.jdbc.driver.OracleDriver"for Oracle). - In modern JDBC versions, this step is optional as the driver is often loaded automatically if available on the classpath.
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:
- Replace
"jdbc:mysql://localhost:3306/your_database_name"with your database URL.jdbc:mysql://specifies the protocol.localhost:3306specifies the host and port of the database.your_database_nameis the name of your database.
- Replace
"username"and"password"with your database credentials.
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:
- The
Statementobject allows you to execute SQL queries. - Use
PreparedStatementorCallableStatementfor dynamic or parameterized queries.
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:
- Use
executeQuery()forSELECTqueries andexecuteUpdate()forINSERT,UPDATE,DELETE, or DDL statements. - The
ResultSetobject holds the results of a query, allowing you to iterate through rows and fetch column values.
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:
- Always close resources in the reverse order of their creation to avoid memory leaks.
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()
-
Using Connection URL Only:
This variant is suitable for URLs containing all required credentials (username and password).
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb"); -
Using URL and Credentials:
Explicitly provide the username and password separately.
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "username", "password"); -
Using URL and Properties:
Use a
Propertiesobject for better configurability, such as adding custom parameters.Properties properties = new Properties(); properties.put("user", "username"); properties.put("password", "password"); Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", properties);
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:
- Install the corresponding database client libraries.
- Ensure you have the database host, port, username, password, and database name (if applicable).
- Verify network access to the database server.
2. Connecting to MySQL
Steps:
-
Install MySQL Client:
Use
mysqlCLI 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> -
Database Configuration:
- Host:
localhost(or your server IP) - Port:
3306(default MySQL port) - URL format:
jdbc:mysql://<host>:<port>/<database>
- Host:
-
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(); } } } - Test the Connection: Run the above code or use
mysql -u username -pfrom the command line to verify.
3. Connecting to PostgreSQL
Steps:
Install PostgreSQL Client:
- Use
psqlCLI 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>
- Use
Database Configuration:
- Host:
localhost(or your server IP) - Port:
5432(default PostgreSQL port) - URL format:
jdbc:postgresql://<host>:<port>/<database>
- Host:
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(); } } }- Test the Connection: Use
psql -U username -d database_namefrom the command line or run the code above.
4. Connecting to Snowflake
Steps:
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>
Database Configuration:
- Host:
<account>.snowflakecomputing.com - Port:
443(default) - URL format:
jdbc:snowflake://<account>.snowflakecomputing.com/
- Host:
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(); } } }- Test the Connection: Run the Java code or use SnowSQL CLI (
snowsql -a account_name -u username -d database -s schema).
Common Issues & Tips
- Firewall Settings: Ensure your IP is whitelisted in the database server's firewall.
- SSL Certificates: Use SSL for secure connections (e.g.,
useSSL=truein MySQL). - Driver Versions: Ensure you use compatible JDBC driver versions for the database.
- Time Zones: Set appropriate time zone settings to avoid discrepancies.
- Pooling: Use connection pooling libraries like HikariCP for better performance in production.
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
- createStatement()
- Creates a statement that allows you to send simple SQL queries to database.
- Example:
Statement stm = con.createStatement();
- 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.
- prepareCall(String sql)
- Creates a callable object for executing stored procedures.
- Example:
CallableStatement pc = con.prepareCall("call getDetails(?)"); pc.setInt(1);
- setAutoCommit(Boolean autoCommit)
- When auto-commit is true, each SQL statement is automatically saved.
- Example:
con.setAutoCommit(true);
- commit()
- Manually commits the current transaction.
- Example: try { Connection con = DriverManager.getConnection(); con.setAutoCommit(false); // database operation con.commit(); }
- 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(); } } } }
- close()
- Closes the connection and frees the resources.
- setTransactionIsolation(int level)
- TRANSACTION_READ_UNCOMMITTED
- TRANSACTION_READ_COMMITTED
- TRANSACTION_REPEATABLE_READ
- TRANSACTION_SERIALIZABLE
- Example:
con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
- getMetaData()
- Returns metadata about the database, such as version, product name, and other info.
- Example: DatabaseMetaData md = con.getMetaData(); System.out.println(md.getDatabaseProductName());
- isClosed()
- Checks if the connection is closed.
- Example: if (con.isClosed()) { System.out.println("Connection is closed"); }
- getAutoCommit()
- Gets the auto-commit mode.
- Example: boolean b = con.getAutoCommit(); System.out.println(b);
- isReadOnly()
- Checks if the connection is read-only.
- setReadOnly(boolean readOnly)
- Sets the connection to read-only.
- Example:
con.setReadOnly(true);
- nativeSQL(String sql)
- Converts a standard SQL query to a database-specific SQL query.
- Example: String nativeSql = con.nativeSQL("select * from student");
- 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(); } } }
- getCatalog()
- Gets the current catalog.
- Example: String databaseName = con.getCatalog();
- 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:
- Move forward (
next()). - Move backward (
previous()). - Jump to the first row (
first()). - Jump to the last row (
last()). - Move to a specific row number (
absolute(int row)). - Move relative to the current position (
relative(int rows)).
How to Create a Scrollable ResultSet?
When you create a Statement object, you specify two parameters:
- ResultSet Type β Specifies whether the
ResultSetis scrollable and sensitive to database changes. - 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)
- Moves forward only using
next(). - You cannot move backward, or jump to specific rows.
- Most efficient and commonly used.
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
- Cursor can move in all directions (forward, backward, jump to a specific row).
- Changes made in the database are NOT visible in the
ResultSetwhile you are working with it. - Slower than
TYPE_FORWARD_ONLYbecause it creates a temporary snapshot of data.
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
- Cursor can move in all directions.
- Changes made in the database ARE visible in the
ResultSetif the data for the current row is updated. - Performance is usually slower than
TYPE_SCROLL_INSENSITIVEbecause it keeps track of database changes.
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:
TYPE_FORWARD_ONLYis the default (most efficient).- Scrollable
ResultSetneedsTYPE_SCROLL_INSENSITIVEorTYPE_SCROLL_SENSITIVE. - Not all JDBC drivers support
TYPE_SCROLL_SENSITIVE; it may fall back toTYPE_SCROLL_INSENSITIVE. TYPE_SCROLL_INSENSITIVEis the safer choice when you need scrollability.
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
- Moves the cursor to the next row.
- Returns:
trueif there is a valid row,falseif no more rows exist. - Use Case: Used in loops to iterate through all rows in the result set.
- How It Works:
- Initially, the cursor is positioned before the first row.
- First call moves the cursor to the first row.
- Each subsequent call moves the cursor through subsequent rows.
- When no rows are left, it returns
false. - This is how you loop through the
ResultSet. - Example:
while (rs.next()) {
System.out.println(rs.getInt("id") + " - " + rs.getString("name"));
}
- Here,
rs.next()moves the cursor forward for each row until there are no more rows.
2. previous() Method
- Moves the cursor to the previous row.
- Returns:
trueif thereβs a valid row,falseif no previous row exists. - Requires: A scrollable
ResultSet(TYPE_SCROLL_INSENSITIVEorTYPE_SCROLL_SENSITIVE). - How It Works:
- Starts from the current position and moves backwards.
- You can use
last()to move to the last row first and then move backward. - Example:
if (rs.last()) {
do {
System.out.println(rs.getInt("id"));
} while (rs.previous());
}
rs.last()moves to the last row.do-whilewithprevious()moves backward through each row until it reaches before the first row.
3. first() Method
- Moves the cursor to the first row.
- Returns:
trueif the first row exists,falseif theResultSetis empty. - How It Works:
- Moves the cursor to the first row regardless of its current position.
- Faster than
absolute(1). - Example:
if (rs.first()) {
System.out.println(rs.getInt("id"));
}
rs.first()moves to the first row if it exists and prints theid.
4. last() Method
- Moves the cursor to the last row.
- Returns:
trueif the last row exists,falseif theResultSetis empty. - How It Works:
- Moves the cursor to the last row regardless of its current position.
- Faster than some other methods to move directly to the last row.
- Example:
if (rs.last()) {
System.out.println(rs.getInt("id"));
}
rs.last()moves to the last row if it exists and prints theid.
5. absolute(int row) Method
- Moves the cursor to the specified row number.
- Positive Number: Moves from the start (e.g.,
absolute(1)is the first row). - Negative Number: Moves from the end (e.g.,
absolute(-1)is the last row). - Returns:
trueif the row exists,falseotherwise. - How It Works:
absolute(1)moves to the first row.absolute(3)moves to the third row.absolute(-1)moves to the last row.absolute(-2)moves to the second last row.- Example:
if (rs.absolute(2)) { // Move to second row
System.out.println(rs.getInt("id"));
}
if (rs.absolute(-1)) { // Move to last row
System.out.println(rs.getInt("id"));
}
- First Call: Moves to the second row and prints the
id. - Second Call: Moves to the last row and prints the
id.
6. relative(int rows) Method
- Moves the cursor by the given number of rows from the current position.
- Positive Number: Moves forward (e.g.,
relative(2)moves forward by 2 rows). - Negative Number: Moves backward (e.g.,
relative(-1)moves back by 1 row). - Returns:
trueif the new position is valid,falseotherwise. - How It Works:
- Useful to skip rows efficiently or move back based on your requirements.
- Example:
if (rs.first()) {
rs.relative(2); // Moves from the first row to the third
System.out.println(rs.getInt("id"));
}
- Here, if the
ResultSetstarts at the first row,rs.relative(2)moves forward by 2 rows.
7. beforeFirst() Method
- Moves the cursor to a position before the first row.
- Returns:
void(no return value). - Use: Resets the cursor so that you can loop through the rows again.
- Example:
rs.beforeFirst();
while (rs.next()) {
System.out.println("First row again? No, if the result set is not empty, it loops from the first row.");
}
- This is equivalent to resetting the
ResultSet.
8. afterLast() Method
- Moves the cursor to a position after the last row.
- Returns:
void(no return value). - Use: To move the cursor past the last row, so you can process backwards using
previous(). - Example:
rs.afterLast();
while (rs.previous()) { // Process rows backwards
System.out.println(rs.getInt("id"));
}
- This is useful if you need to process rows in reverse order.
Methods to Check Cursor Position (No Movement)
- Methods that check where the cursor is positioned, but do not move it:
isBeforeFirst()isAfterLast()isFirst()isLast()
9. isBeforeFirst()
- Returns:
trueif cursor is before the first row. - Example:
if (rs.isBeforeFirst()) {
System.out.println("Before first row");
}
- This method is useful to check if you are at the start of the
ResultSet.
10. isAfterLast()
- Returns:
trueif cursor is after the last row. - Example:
if (rs.isAfterLast()) {
System.out.println("After last row");
}
- This method is useful to check if you have passed the end of the
ResultSet.
11. isFirst()
- Returns:
trueif cursor is at the first row. - Example:
if (rs.isFirst()) {
System.out.println("First row");
}
- Useful to know if you are on the first row for conditional logic.
12. isLast()
- Returns:
trueif cursor is at the last row. - Example:
if (rs.isLast()) {
System.out.println("Last row");
}
- Useful to know if you are on the last row because maybe you can use multiple cursors with
break;here.
Data Retrieval Methods
1. getString(int columnIndex) / getString(String columnLabel)
- Returns the column value as a
String. - Use: For text data like
VARCHAR(names, descriptions, etc.). - Examples:
String name = rs.getString(2);(retrieves second column asString)String name = rs.getString("name");(retrieves column "name")
2. getInt(int columnIndex) / getInt(String columnLabel)
- Returns the column value as an
int. - Use: For
INTEGER-like data. - Examples:
int id = rs.getInt(1);int age = rs.getInt("age");
3. getLong(int columnIndex) / getLong(String columnLabel)
- Returns the column value as a
long. - Use: For
BIGINTand other large integers.
4. getDouble(int columnIndex) / getDouble(String columnLabel)
- Returns the column value as a
double. - Use: For
DOUBLEorFLOATvalues (precision required).
5. getFloat(int columnIndex) / getFloat(String columnLabel)
- Returns the column value as a
float. - Use: For
FLOATvalues (less precise than double).
6. getBoolean(int columnIndex) / getBoolean(String columnLabel)
- Returns the column value as a
boolean. - Use: For
BOOLEANtypes (orTINYINT(1)in MySQL).
7. getByte(int columnIndex) / getByte(String columnLabel)
- Returns the column value as a
byte. - Use: For
TINYINTor binary data.
8. getShort(int columnIndex) / getShort(String columnLabel)
- Returns the column value as a
short. - Use: For
SMALLINT-like data.
9. getDate(int columnIndex) / getDate(String columnLabel)
- Returns the column value as
java.sql.Date. - Use: For
DATEvalues (without time).
10. getTime(int columnIndex) / getTime(String columnLabel)
- Returns the column value as
java.sql.Time. - Use: For
TIMEvalues (without date).
11. getTimestamp(int columnIndex) / getTimestamp(String columnLabel)
- Returns the column value as
java.sql.Timestamp. - Use: For
DATETIME/TIMESTAMPvalues.
12. getObject(int columnIndex) / getObject(String columnLabel)
- Returns the column value as a Java
Object. - Use: For dynamic retrieval when you donβt know the type in advance.
13. getBigDecimal(int columnIndex) / getBigDecimal(String columnLabel)
- Returns the column value as
BigDecimal. - Use: For high-precision
DECIMAL/NUMERICvalues.
14. getBytes(int columnIndex) / getBytes(String columnLabel)
- Returns the column value as a
byte[]array. - Use: For BLOB or binary data.
15. getBlob(int columnIndex) / getBlob(String columnLabel)
- Returns the column value as a
Blobobject. - Use: For BLOB (binary large objects).
16. getClob(int columnIndex) / getClob(String columnLabel)
- Returns the column value as a
Clobobject. - Use: For CLOB (character large objects).
Update Methods
1. updateString(int columnIndex, String x) / updateString(String columnLabel, String x)
- Updates the specified column (via index or label) with a
Stringvaluex. - This is in memory and requires
updateRow()to save to the database.
2. updateInt(int columnIndex, int x) / updateInt(String columnLabel, int x)
- Updates the specified column with an
intvaluex.
3. updateDouble(int columnIndex, double x) / updateDouble(String columnLabel, double x)
- Updates the specified column with a
doublevaluex.
4. updateObject(int columnIndex, Object x) / updateObject(String columnLabel, Object x)
- Updates the specified column with an
Object x. Allows flexibility in updating with any Java object.
5. insertRow()
- Inserts a new row into the database based on current row content (prepared via
moveToInsertRow()andupdateXxx()calls). - Steps to use:
- Move to the "insert row" using
moveToInsertRow(). - Set new column values with
updateXxx()methods. - Call
insertRow()to insert into the database. - Use
moveToCurrentRow()to return to the original cursor position beforemoveToInsertRow(). - Example:
rs.moveToInsertRow();
rs.updateString(2, "John Doe");
rs.insertRow();
rs.moveToCurrentRow();
6. updateRow()
- Saves all changes made to the current row in memory to the database.
- Steps to use:
- Move the cursor to the row you want to update.
- Use
updateXxx()methods to modify columns. - Call
updateRow()to save changes to the database. - Example:
rs.next();
rs.updateInt("age", 35); // Modify age column
rs.updateRow(); // Save changes to the database
7. deleteRow()
- Deletes the current row from both the
ResultSetand the underlying database. - Example:
rs.next();
rs.deleteRow(); // Deletes the current row
8. moveToInsertRow()
- Moves the cursor to a special insert row buffer.
- Clear the row buffer and allow setting new values with
updateXxx()methods. - You must call
insertRow()to actually insert the row into the database.
9. moveToCurrentRow()
- Moves the cursor back to the row it was on before
moveToInsertRow()was called (if applicable).
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()
- Returns general information about the columns (names, types, etc.) in this
ResultSet. - Returns a
ResultSetMetaDataobject. - Example:
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount(); // Number of columns
2. getConcurrency()
- Returns the concurrency type of this
ResultSet(e.g.,CONCUR_READ_ONLYorCONCUR_UPDATABLE). - Use: Check if
ResultSetis updatable. - Example:
int concurrency = rs.getConcurrency();
if (concurrency == ResultSet.CONCUR_UPDATABLE) {
System.out.println("Updatable ResultSet");
}
3. getType()
- Returns the type of this
ResultSet(e.g.,TYPE_FORWARD_ONLY,TYPE_SCROLL_INSENSITIVE, orTYPE_SCROLL_SENSITIVE). - Example:
int type = rs.getType(); // E.g., ResultSet.TYPE_FORWARD_ONLY
4. getRow()
- Returns the current row number (starting from 1) of this
ResultSet. - Returns
0if before the first row, after the last row, or ifResultSetis empty. - Example:
int rowNumber = rs.getRow(); // If on 3rd row, returns 3
Miscellaneous Methods
1. wasNull()
- Used immediately after
getXxx()orupdateXxx()to check if the retrieved or updated value was SQLNULL. - Use: Ensures that the value is not
nullbefore processing it. - Example:
int age = rs.getInt("age");
if (rs.wasNull()) {
System.out.println("Age is NULL");
}
2. close()
- Closes this
ResultSetand releases resources. - Automatically freed when associated
Statementis closed or the next result is retrieved. - Best Practice: Always close the
ResultSetexplicitly after use.
3. isClosed()
- Checks if this
ResultSetis already closed.
4. getCursorName()
- Returns the name of the SQL cursor associated with this
ResultSet. - Rarely used. Most DBMSs will not use named cursors implicitly.
web.xml vs. application.xml vs. server.xml
web.xml (Deployment Descriptor for a Web Application)
- Found inside WEB-INF/ of a web module (WAR file).
- Defines configurations for servlets, filters, listeners, error pages, security constraints, etc.
- It is specific to web applications.
<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)
- Found inside META-INF/ of an Enterprise Application Archive (EAR) file.
- Defines the structure of an Enterprise Application that can contain multiple modules (WARs and EJB JARs).
- Used to declare and assemble web modules (WAR), EJB modules (JAR), and other components.
<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)
- Located inside profile_root/config/cells/cellName/nodes/nodeName/servers/serverName/server.xml.
- Used to configure the WebSphere application server instance, including ports, data sources, security settings, logging, thread pools, and deployed applications.
- Changes require a server restart in most cases.
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)
- Found inside $CATALINA_HOME/conf/server.xml.
- Defines the HTTP connector ports, hosts, engine, context settings, and logging.
- Modifications usually require a Tomcat restart.
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?
- Modify web.xml β If you need to define servlets, filters, or security rules for your web app.
- Modify application.xml β If youβre working with an EAR and need to define multiple modules.
- Modify server.xml β If you need to change WebSphere/Tomcat server settings (ports, SSL, logging, etc.).
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:
- Responsible for physically moving data over a network.
- Uses cables, Wi-Fi, or cellular networks to send bits (1s and 0s).
Examples:
- PPP (Point-to-Point Protocol): Used for dial-up connections.
- Wi-Fi: Wireless communication.
- DSL (Digital Subscriber Line): Used for internet via telephone lines.
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:
- Assigns addresses to devices and helps route data between them.
- Uses IP addresses to ensure data reaches the right destination.
Examples:
- IPv4: Uses 32-bit addresses (e.g., 192.168.1.1).
- IPv6: Uses 128-bit addresses (e.g., 2001:0db8:85a3::8a2e:0370:7334) to handle more devices.
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:
- Breaks data into packets and ensures they arrive correctly.
- Manages speed, reliability, and error checking.
Examples:
- TCP (Transmission Control Protocol):
- Ensures data arrives fully & in order (like sending a fragile package with tracking).
- Used in websites, emails, file transfers.
- UDP (User Datagram Protocol):
- Faster but no guarantee of delivery (like sending a postcard).
- Used in live streaming, gaming, VoIP calls.
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?
- Provides Network Services to Users
- Applications like Google Chrome, Gmail, and WhatsApp rely on this layer to send and receive data.
- Formats Data for Communication
- Converts data into the correct format before sending (e.g., a web page request).
- 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 π½οΈ
- You (User) β Place an order (request for a web page).
- Waiter (Application Layer) β Translates your order into the kitchen's language (formats and sends the request).
- Chef (Server) β Cooks your food (processes your request).
- Waiter (Application Layer) β Brings the food to your table (delivers the webpage). Without the Waiter (Application Layer), You Can't Communicate with the Chef (Server)!
πΉ Why is the Application Layer Important?
- β It makes the internet usable for humans.
- β It manages web browsing, emails, file transfers, and more.
- β It secures data transmission using HTTPS & SSL/TLS.
π― Final Takeaway
- The Application Layer is the closest to users and enables them to browse websites, send emails, download files, and more.
- It formats data, ensures security, and manages network requests.
- Without it, using the internet as we know it would be impossible!
πΉ 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
- 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
- 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
- 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
- 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 handles addressing and routing of packets across networks.
- TCP ensures reliable, ordered, and error-checked delivery of data.
IP (Internet Protocol)
IP is responsible for addressing and routing packets from source to destination across different networks.
Key Features of IP:
- Logical Addressing (IP Address)
- Every device on a network has a unique IP address (e.g.,
192.168.1.1for IPv4,2001:db8::ff00:42:8329for IPv6).
- Every device on a network has a unique IP address (e.g.,
- Packet-Switched Communication
- Data is broken into packets and sent independently.
- Connectionless Protocol
- IP does not establish a connection before sending packets.
- Best-Effort Delivery
- No guarantee that packets will arrive in order or at all.
IP Addressing & Routing:
- Every device on a network has an IP address.
- Routers forward packets to their destination based on the IP address.
- Uses subnetting and routing tables for efficient communication.
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:
- Connection-Oriented
- Establishes a connection before data transfer (using a three-way handshake).
- Reliable Delivery
- Ensures all packets arrive and are reassembled in the correct order.
- Error Detection & Correction
- Uses checksums to detect errors and acknowledgments (ACKs) to confirm receipt.
- Flow Control & Congestion Control
- Adjusts transmission speed based on network conditions.
TCP Three-Way Handshake (Connection Establishment):
- SYN β Client sends a SYN (synchronize) packet to initiate a connection.
- SYN-ACK β Server responds with SYN-ACK (synchronize acknowledgment).
- 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):
- TCP breaks data into packets.
- Each packet gets a sequence number.
- The receiver acknowledges received packets.
- 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:
- Stateless
- Each request is independent; the server does not remember previous requests.
- Uses TCP for Reliable Delivery
- HTTP runs over TCP (typically port 80 for HTTP, 443 for HTTPS).
- Request-Response Model
- The client (browser) sends a request, and the server responds.
- Supports Different Methods (HTTP Verbs)
- GET, POST, PUT, DELETE, etc.
- Supports Headers for Metadata
- Headers contain details like content type, caching, authentication, etc.
HTTP Request-Response Cycle:
- Client Request:
GET /index.html HTTP/1.1 Host: www.example.com - 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
- HTTP: Unencrypted communication (port 80).
- HTTPS: Uses SSL/TLS encryption (port 443) for security.
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)
- Requests data from the server using a URL.
- Does not modify server data.
- No request body; data sent as query parameters.
- Cacheable and idempotent.
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
- Use pagination, compression, or streaming for large responses.
2. POST (Send Data)
- Sends data in the request body to create resources.
- Not idempotent.
POST /users HTTP/1.1
Host: example.com
Content-Type: application/json
{
"name": "Alice",
"email": "alice@example.com"
}
3. HEAD (Retrieve Headers Only)
- Similar to GET but does not return the response body.
- Used for checking metadata or existence of a resource.
4. TRACE (Debugging and Testing)
- Returns the received request to the client for debugging.
- Should be disabled in production.
5. PUT (Update or Create Resource)
- Uploads or replaces the entire resource at a given URL.
- Idempotent.
6. DELETE (Remove Resource)
- Deletes the resource at the given URL.
- Idempotent.
7. OPTIONS (Check Allowed Methods)
- Used to determine supported methods on a resource.
- Common in CORS preflight checks.
Comparison of HTTP Methods
| Method | Safe? | Idempotent? | Request Body? | Use Case |
|---|---|---|---|---|
| GET | Yes | Yes | No | Retrieve data |
| POST | No | No | Yes | Create resource |
| HEAD | Yes | Yes | No | Check headers |
| TRACE | Yes | Yes | No | Debugging |
| PUT | No | Yes | Yes | Update or create |
| DELETE | No | Yes | No | Delete resource |
| OPTIONS | Yes | Yes | No | Check methods |
Request and Response Format
- Header: Metadata such as content type, length, etc.
- Body: Actual data (only in POST/PUT responses)
GET vs POST
| GET | POST |
|---|---|
| Default request method | Not default |
| No body | Has body |
| Limited data | Can carry large data |
| ASCII data | Supports binary data |
| Not secure (visible in URL) | Secure (body content) |
| Can be bookmarked | Cannot be bookmarked |
| Can be cached | Not cached |
| Used for downloads | Used for uploads |
HTTP Status Codes
- 100β199: Informational
- 200β299: Success
- 300β399: Redirection
- 400β499: Client Error
- 500β599: Server Error
Server Overview
A server is a hardware machine running server software, accepting requests, processing them, and sending responses.
- Web Server: Handles static content and JSP/Servlets.
- Application Server: Adds support for EJB, JNDI, JMS, JTA, etc. (e.g., GlassFish, WebSphere).
Server vs Container
- Server interacts with the client; container interacts with the server.
- Static content: handled by server; dynamic content: handled by container.
- Types of containers: Web container (servlets/JSP), EJB container.
Tomcat Server Configuration
- Download the ZIP version, not the installer.
- While configuring, select only till the server folder (not /bin).
- Select web.xml when creating a dynamic web project.
Servlet Interface Hierarchy
Servlet(interface) βGenericServletβHttpServletGenericServlet: protocol-independentHttpServlet: used for HTTP-specific requests (GET, POST, etc.)
Servlet Life cycle:
-
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. -
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. -
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). -
Request Handling β service():
This is used for handling the request. In this phase, service method is invoked. -
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
| Method | Description |
|---|---|
| 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
| Method | Description |
|---|---|
| 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
| Method | Description |
|---|---|
| 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
| Method | Description |
|---|---|
| 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
| Method | Description |
|---|---|
| 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
| Method | Description |
|---|---|
| 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
| Method | Description |
|---|---|
| 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
| Method | Description |
|---|---|
| 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
- Scriptlet
<% %>Scriptlet tag<%! %>Declaration tag<%= %>Expression tag
- Directive Tags -
<%@ %>- Page directive:
<%@ page attribute="value" %>(refer next page) - Include directive
- Taglib directive
- Page directive:
- Action Tags
Scriptlet Details
<% %>- Anything written here is like writing insidevoid service(HttpServletRequest, HttpServletResponse)<%! %>- Anything written here is like writing outside the service method. You can create variables or other methods.<%@ page import=βjava.*β, βjava.util.*β %>- Directive tags send instructions to the JSP container.<%= k %>- Equivalent toout.print(k)<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" %>
- The content of
header.jspis copied into the main JSP page before the page runs. - It happens only once, when the page is first loaded.
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 %>?
- β When the included file doesnβt change often (e.g., static headers, footers).
- β When you want better performance (since it's compiled once).
- β Donβt use it if the included file changes frequently! Use
<jsp:include>instead.
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
- π‘
<%@ taglib %>lets you create custom JSP tags that replace repetitive Java code. - π‘ It makes JSP pages cleaner, easier to read, and reusableβjust like using a shortcut or mod in a video game!
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:
&&β AND||β OR!β NOT
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?
- β Cleaner JSP Code β No Java code inside JSP!
- β
Short and Simple β
${name}is easier than writing<% %>blocks. - β Faster Development β No need to cast objects manually.
πΉ Final Takeaway π―
- π₯ Expression Language (
${}) makes JSP pages simpler and easier to read. - π₯ It replaces messy Java code with short expressions.
- π₯ Great for accessing form data, cookies, sessions, and more!