Java Tutorials

Understand how Java works from the ground up with a structured, practical Java tutorial from Hejex Technology. Start with Java fundamentals, JDK, JRE and JVM, variables, data types, operators and control flow, then progress into arrays, strings, methods and object-oriented programming. Explore classes, inheritance, polymorphism, interfaces, exception handling, collections, file handling, multithreading, Java 8 features and JDBC through clear explanations, practical coding examples, program outputs and real-world use cases. Whether you are learning Java for the first time, revising core concepts, or preparing to build Java applications, this tutorial helps you develop a strong understanding of Java programming step by step.

Java Introduction

Understand Java, its features, architecture, applications, and the basic structure of a Java program.

Java is a high-level, general-purpose, object-oriented programming language designed to build applications that can run on different platforms. Java was originally developed at Sun Microsystems and is now maintained by Oracle and the Java community.

One of the most important characteristics of Java is its platform independence. Java source code is compiled into bytecode, which is executed by the Java Virtual Machine (JVM). Because JVM implementations are available for different operating systems, the same compiled Java application can run on multiple platforms.

Core idea: Java follows the principle "Write Once, Run Anywhere". The same Java bytecode can be executed on different operating systems as long as a compatible JVM is available.

Why Learn Java?

Platform Independent

Java programs are compiled into bytecode that can run on different operating systems through the JVM.

Object-Oriented

Java uses classes and objects to organize application data and behavior and supports encapsulation, inheritance, polymorphism and abstraction.

Secure

Java provides features such as strong type checking, bytecode verification and managed memory that contribute to safer application development.

Large Ecosystem

Java is widely used with frameworks and technologies such as Spring, Spring Boot, Hibernate, JDBC and many enterprise development tools.

Where Is Java Used?

Area Examples
Web Development Spring Boot, Jakarta EE
Enterprise Applications Banking, ERP, business applications
Backend Development REST APIs, microservices
Desktop Applications JavaFX, Swing
Database Applications JDBC, Hibernate, JPA
Big Data Hadoop ecosystem and related technologies

First Java Program

A Java application normally starts execution from the main() method. A basic Java program looks like this:


                  public class Main {              
                    public static void main(String[] args) {
                        System.out.println("Hello, Java!");
                    }
                  }
Output:
Hello, Java!
Understanding the Program
  • public class Main defines a class named Main.
  • public static void main(String[] args) is the entry point used to start a traditional Java application.
  • System.out.println() displays text in the console.
  • Java statements normally end with a semicolon ;.
Important: Java is case-sensitive. For example, String, string and STRING are different identifiers.

JDK, JRE & JVM Architecture

Understand how Java source code is compiled and executed.

To understand how Java works, it is important to distinguish between the JDK, JRE and JVM. These components have different responsibilities in Java development and execution.

Component Meaning Main Purpose
JDK Java Development Kit Develop, compile and run Java applications
JRE Java Runtime Environment Provides the environment required to run Java applications
JVM Java Virtual Machine Executes Java bytecode

Relationship Between JDK, JRE and JVM

JDK
Development Tools + JRE
JRE
JVM + Runtime Libraries
JVM
Executes Bytecode

How Java Code Executes

  1. Developer writes Java source code in a .java file.
  2. The Java compiler javac compiles the source code.
  3. The compiler produces a .class file containing bytecode.
  4. The JVM loads the bytecode.
  5. The JVM verifies and executes the bytecode.

                    Source Code
                        ↓
                    .java file
                        ↓
                    javac compiler
                        ↓
                    Bytecode
                        ↓
                    .class file
                        ↓
                    JVM
                        ↓
                    Machine-level execution
                  

Compile and Run from Command Prompt

Suppose the file is named Main.java.


                    javac Main.java
                    java Main
                  

The first command compiles the source code. The second command starts the Java application through the JVM.

Remember: javac is used for compilation, while java is used to launch a compiled Java class.

Java Installation and VS Code Setup

Before writing Java programs, you need a Java Development Kit (JDK) and a development environment. In this section, you will learn how to install the JDK, verify the installation, configure Visual Studio Code, install the required Java extensions, and run your first Java program.

1

Install the Java Development Kit (JDK)

The JDK provides everything required to develop, compile, debug, and run Java applications.

Java programs are written using Java source code and then compiled into bytecode. The Java Development Kit contains the tools required for this development process.

Download JDK

Download a suitable JDK distribution for your operating system. For beginners, use a current Long-Term Support (LTS) release.

Install JDK

Run the installer and follow the installation wizard. Keep the default installation location unless you have a specific reason to change it.

Verify

After installation, open Command Prompt or PowerShell and verify that Java is available from the command line.

2

Verify Java Installation

Use the command line to confirm that the JDK was installed correctly.

Open Command Prompt or PowerShell and execute the following command:

java -version

This command displays the installed Java runtime version. You can also check the Java compiler using:

javac -version
Expected Result

Both commands should return a Java version instead of showing an error such as 'java' is not recognized.

Important: java is commonly used to launch Java applications, while javac is the Java compiler used to compile .java source files into bytecode.
3

Configure JAVA_HOME and PATH

Environment variables allow operating systems and development tools to locate the Java installation.

What is JAVA_HOME?

JAVA_HOME is an environment variable that points to the location where the JDK is installed. Many Java-based tools, build systems, and frameworks use this variable to locate Java.

What is PATH?

The PATH environment variable contains locations where the operating system searches for executable commands. Adding the JDK's bin directory to PATH allows commands such as java and javac to be executed from any terminal location.

Variable Purpose Example
JAVA_HOME Points to the JDK installation directory. C:\Program Files\Java\jdk...
PATH Allows Java commands to be executed from the terminal. %JAVA_HOME%\bin
Note: If java -version and javac -version already work from Command Prompt, you normally do not need to manually modify PATH just to start learning Java.
4

Install Visual Studio Code

Visual Studio Code can be used as a lightweight Java development environment.

Download and install Visual Studio Code from its official website. During installation, keep the standard options enabled unless you have a specific configuration requirement.

Recommended Setup

  • Windows, macOS, or Linux
  • Java Development Kit installed
  • Visual Studio Code
  • Java Extension Pack
  • Terminal access

Why VS Code?

VS Code provides syntax highlighting, code completion, debugging, project navigation, integrated terminal support, and Java extensions without requiring a heavy IDE for basic Java development.

5

Install Java Extension Pack in VS Code

Java support in VS Code is provided through extensions.

  1. Open Visual Studio Code.
  2. Select the Extensions icon from the left sidebar.
  3. Search for Extension Pack for Java.
  4. Select the Java Extension Pack published by Microsoft.
  5. Click Install.
What does the extension pack provide?

It provides Java language support, code completion, debugging, testing support, project management features, and other tools needed for Java development inside VS Code.

6

Create Your First Java Project

Now create a simple Java project and run your first program.

Create a project folder

Create a folder for your Java practice programs. For example:


                        JavaPractice
                        │
                        ├── src
                        │   └── Main.java
                        │
                        └── README.md

Open the JavaPractice folder in VS Code. The src folder is commonly used to keep Java source files in a structured project.

Create Main.java


                        public class Main {
            
                          public static void main(String[] args) {
                      
                              System.out.println("Hello, Java!");
                      
                          }
                          
                        }

Run the program

You can run the program directly using the Run option provided by the Java extension in VS Code. You can also use the integrated terminal.


                        javac Main.java
                        java Main
                      
Output:
Hello, Java!
7

Understand What Happens When Java Runs

Java follows a compile-and-run process rather than directly executing the source file.

Source Code
Main.java
Compiler
javac
Bytecode
Main.class
JVM Executes Bytecode

The Java Virtual Machine interprets or compiles the bytecode for execution on the target system.

8

Common Java Setup Problems

These are some common errors beginners may encounter during Java installation.

Problem Possible Cause What to Check
java is not recognized Java may not be available in PATH. Check the JDK installation and PATH configuration.
javac is not recognized The JDK compiler cannot be located. Verify that a JDK is installed rather than only a runtime.
Java extension not working Java Extension Pack may not be installed correctly. Check the Extensions panel in VS Code.
Wrong Java version Multiple Java installations may exist. Run java -version and javac -version.

Datatypes, Variables & Operators

Learn how Java stores values and performs operations.

Every Java program works with data. A program may need to store an employee's name, calculate salary, compare values, maintain a product quantity or perform mathematical calculations.

Java is a statically typed language. This means the type of a variable is known and checked at compile time.

Primitive Data Types

Type Typical Size Example Used For
byte 8-bit byte age = 25; Small integer values
short 16-bit short year = 2026; Small to medium integers
int 32-bit int salary = 50000; Common integer calculations
long 64-bit long population = 1000000L; Large integer values
float 32-bit float price = 99.5f; Decimal values with lower precision
double 64-bit double pi = 3.14159; Decimal calculations
char 16-bit char grade = 'A'; Single characters
boolean Language-defined boolean active = true; True/false conditions

Variables in Java

A variable is a named memory location used to store a value that a program can use and, in most cases, change during execution. Every variable in Java has a data type, a name, and a value.

For example, an employee management application may need variables to store an employee's ID, name, salary, department, and employment status.


                    int employeeId = 101;
                    String employeeName = "Arun";
                    double salary = 45000.50;
                    boolean active = true;
Variable Declaration

Declaration tells Java that a variable exists and specifies the type of value that the variable can store.


                    int age;
                    double salary;
                    String name;

At this point, the variables have been declared but no value has been explicitly assigned to them.

Variable Initialization

Initialization means assigning the first value to a variable.


                    int age;
                    age = 22;
                    
                    double salary;
                    salary = 35000.50;
Declaration and Initialization Together

Java allows declaration and initialization to be performed in a single statement.


                    int age = 22;
                    double salary = 35000.50;
                    String name = "Arun";
Remember: int age; is a declaration, age = 22; is an assignment, and int age = 22; is declaration plus initialization.
Changing a Variable Value

A variable can be assigned a new value after it has been initialized. The new value must be compatible with the variable's declared type.


                    int marks = 75;                
                    marks = 90;
                    
                    System.out.println(marks);
Output:
90

The original value 75 is replaced by 90.

Multiple Variables

Multiple variables of the same type can be declared in a single statement. However, separate declarations are often easier to read and maintain.

int x = 10, y = 20, z = 30;

The following form is usually clearer in larger programs:


                    int x = 10;
                    int y = 20;
                    int z = 30;
Variable Naming Rules

Java follows specific rules when naming variables.

Rule Example
Must begin with a letter, _, or $ age, _count, $value
Cannot begin with a number 1age
Cannot contain spaces employee name
Java keywords cannot be used as variable names int class;
Variable names are case-sensitive age and Age are different
Variable Naming Convention

Java commonly uses camelCase for variable names. The first word begins with lowercase and subsequent words begin with uppercase letters.

Recommended
  • studentName
  • totalMarks
  • employeeSalary
  • maximumValue
Avoid
  • student_name
  • StudentName
  • student name
  • 123student
Constants Using final

By default, a variable can be assigned a new value. If a value should not change after initialization, Java provides the final keyword.


                    final double PI = 3.14159;
                    
                    System.out.println(PI);

Once a value has been assigned to a final variable, it cannot be assigned another value.


                    final int MAX_USERS = 100;
                    
                    // MAX_USERS = 200;   // Error
Convention: Constants are commonly written using uppercase letters with underscores, such as MAX_USERS, DEFAULT_TIMEOUT, and TAX_RATE.
Local Variables

A variable declared inside a method, constructor, or block is called a local variable. Its scope is limited to the block in which it is declared.


                    public static void main(String[] args) {
                    
                        int age = 22;
                    
                        System.out.println(age);
                    }

The variable age can be accessed inside the main() method where it was declared, but not outside its scope.

Variable Scope

Scope defines the part of a program where a variable can be accessed. A variable declared inside a block is generally available only inside that block.


                    if (true) {                
                        int number = 100;
                    
                        System.out.println(number);
                    }
                    
                    // number cannot be accessed here
Important: Understanding scope becomes especially important when working with methods, loops, conditional statements, and classes.
The var Keyword

Modern Java also supports local variable type inference using var. The compiler determines the variable's type from the value assigned to it.


                    var age = 22;
                    var name = "Arun";
                    var salary = 35000.50;

In the above example, Java infers age as an integer, name as a String, and salary as a floating-point type.

Important: var does not make Java dynamically typed. The compiler still determines a fixed type for the variable. Also, var is used for local variables and must be initialized when declared.

Operators in Java

Operators are special symbols used to perform operations on values and variables. They allow programs to perform calculations, compare values, combine conditions, assign values, and make decisions.

For example, an application may use operators to calculate an employee's salary, determine whether a customer is eligible for a discount, or check whether two values are equal.

Types of Operators
Operator Type Operators Purpose
Arithmetic + - * / % Perform mathematical calculations
Unary ++ -- + - ! Operate on a single operand
Relational == != > < >= <= Compare values
Logical && || ! Combine or reverse conditions
Assignment = += -= *= /= %= Assign and update values
Ternary ? : Choose between two expressions
Bitwise & | ^ ~ Perform operations at bit level
Shift << >> >>> Shift binary bits
Arithmetic Operators

Arithmetic operators are used for mathematical calculations.

Operator Name Example Result
+ Addition 10 + 5 15
- Subtraction 10 - 5 5
* Multiplication 10 * 5 50
/ Division 10 / 5 2
% Modulus 10 % 3 1
Integer Division

When both operands of the division operator are integers, Java performs integer division. The decimal portion is discarded.


                    int a = 10;
                    int b = 3;
                    
                    System.out.println(a / b);
Output:
3

The mathematical result is approximately 3.33, but because both operands are integers, the result is an integer.

If a decimal result is required, at least one operand should be a floating-point value.


                    double result = 10.0 / 3;
                    
                    System.out.println(result);
Modulus Operator

The modulus operator % returns the remainder after division. It is frequently used to determine whether a number is even or odd.


                    int number = 25;
                    
                    System.out.println(number % 2);
Output:
1

Since the remainder is 1, the number is odd.

String Concatenation Using +

The + operator is also used to concatenate strings. When one operand is a String, Java can combine the values into a single String result.


                    String name = "Arun";
                    int age = 22;
                    
                    System.out.println("Name: " + name);
                    System.out.println("Age: " + age);
Output:
Name: Arun
Age: 22
Unary Operators

Unary operators work with a single operand.

Operator Purpose
+ Indicates a positive value
- Changes the sign of a numeric value
++ Increments a value by one
-- Decrements a value by one
! Reverses a boolean value
Increment Operator

The ++ operator increases a numeric variable by one.


                    int count = 5;
                    
                    count++;
                    
                    System.out.println(count);
Output:
6
Decrement Operator

                    int count = 5;
                    
                    count--;
                    
                    System.out.println(count);
Output:
4
Pre-Increment and Post-Increment

Increment and decrement operators can be placed before or after a variable. The position becomes important when the expression is being evaluated.

Post-Increment

With post-increment, the current value is used first and then the variable is incremented.


                    int x = 5;
                    
                    int result = x++;
                    
                    System.out.println(result);
                    System.out.println(x);
Output:
5
6
Pre-Increment

With pre-increment, the variable is incremented first and the updated value is then used.


                    int x = 5;
                    
                    int result = ++x;
                    
                    System.out.println(result);
                    System.out.println(x);
Output:
6
6
Relational Operators

Relational operators compare two values. The result of a relational expression is always a boolean value: true or false.

Operator Meaning Example
== Equal to 10 == 10
!= Not equal to 10 != 5
> Greater than 10 > 5
< Less than 5 < 10
>= Greater than or equal to 10 >= 10
<= Less than or equal to 5 <= 10

                    int age = 22;                
                    System.out.println(age >= 18);
                    System.out.println(age < 18);
Output:
true
false
Logical Operators

Logical operators are used to combine multiple boolean expressions. They are especially important when writing conditions.

Operator Name Meaning
&& Logical AND Both conditions must be true
|| Logical OR At least one condition must be true
! Logical NOT Reverses the boolean result
Logical AND

The && operator returns true only when both expressions are true.


                    int age = 25;
                    boolean hasLicense = true;
                    
                    boolean canDrive = age >= 18 && hasLicense;
                    
                    System.out.println(canDrive);
Output:
true
Logical OR

The || operator returns true when at least one condition is true.


                    boolean weekend = false;
                    boolean holiday = true;
                    
                    boolean dayOff = weekend || holiday;
                    
                    System.out.println(dayOff);
Output:
true
Logical NOT

The ! operator reverses a boolean expression.


                    boolean active = true;                
                    System.out.println(!active);
Output:
false
Short-Circuit Evaluation

Java's && and || operators use short-circuit evaluation. This means Java may stop evaluating the remaining expression when the final result is already known.

With &&, if the first condition is false, the remaining condition does not need to be evaluated.

With ||, if the first condition is true, the remaining condition does not need to be evaluated.

Why is this useful? Short-circuit evaluation can improve efficiency and can also be used to safely control whether another expression should be evaluated.
Assignment Operators

Assignment operators assign values to variables. Java also provides compound assignment operators that combine an arithmetic operation with assignment.

Operator Equivalent Expression Example
= Direct assignment x = 10
+= x = x + 5 x += 5
-= x = x - 5 x -= 5
*= x = x * 5 x *= 5
/= x = x / 5 x /= 5
%= x = x % 5 x %= 5

                    int balance = 1000;                    
                    balance += 500;
                    System.out.println(balance);
                    
                    balance -= 200;
                    System.out.println(balance);
Output:
1500
1300
Ternary Operator

The ternary operator is a compact way of choosing one of two expressions based on a condition.

Its syntax is:

condition ? expression1 : expression2;

If the condition is true, the first expression is selected. Otherwise, the second expression is selected.


                    int age = 20;
                    String result = age >= 18 ? "Adult" : "Minor";
                    
                    System.out.println(result);
Output:
Adult
Tip: The ternary operator is useful for simple decisions. For complex conditions, regular if-else statements are generally easier to understand.
Operator Precedence

When an expression contains multiple operators, Java follows operator precedence rules to determine which operation is performed first.


                    int result = 10 + 5 * 2;                
                    System.out.println(result);
Output:
20

Multiplication has higher precedence than addition, so Java evaluates 5 * 2 first and then adds 10.

Using Parentheses

Parentheses can be used when you want to explicitly control the order of evaluation.

int result = (10 + 5) * 2;
                
                System.out.println(result);
Output:
30
Best Practice: Even when you know the precedence rules, parentheses can make complex expressions easier for other developers to understand.

Practical Example: Employee Salary Calculation

Variables and operators are commonly used together in real applications. For example, suppose an employee receives a basic salary and a bonus. We can calculate the total salary using variables and arithmetic operators.


                    double basicSalary = 35000;
                    double bonus = 5000;
                    
                    double totalSalary = basicSalary + bonus;
                    
                    System.out.println("Basic Salary: " + basicSalary);
                    System.out.println("Bonus: " + bonus);
                    System.out.println("Total Salary: " + totalSalary);
Output:
Basic Salary: 35000.0
Bonus: 5000.0
Total Salary: 40000.0

Practical Example: Calculate Percentage

Operators can also be combined to solve common programming problems. The following example calculates a student's percentage.


                    int maths = 85;
                    int science = 90;
                    int english = 80;
                    
                    int total = maths + science + english;
                    
                    double percentage = total / 3.0;
                    
                    System.out.println("Total: " + total);
                    System.out.println("Percentage: " + percentage);
Output:
Total: 255
Percentage: 85.0

Key Points to Remember

  • A variable stores a value that can be used by a program.
  • Every variable has a data type and a name.
  • final is used when a variable should not be reassigned.
  • Variable names are case-sensitive.
  • Java commonly uses camelCase for variable names.
  • Arithmetic operators perform mathematical calculations.
  • Relational operators return true or false.
  • Logical operators combine boolean conditions.
  • Assignment operators update variable values.
  • The ternary operator provides a compact form of a simple decision.
  • Parentheses can be used to make expression evaluation explicit.

Conditional Statements & Loops

Control program execution using conditions, decisions, and repetition.

A Java program normally executes statements from top to bottom. However, real applications rarely follow only one fixed execution path. Programs need to make decisions, repeat operations, skip certain operations, and stop execution when a particular condition is satisfied.

Java provides control-flow statements for this purpose. They allow a program to decide which code should execute, when it should execute, and how many times it should execute.

Types of Control-Flow Statements

Category Statements Purpose
Decision Making if, if-else, else-if, switch Choose which block of code should execute.
Iteration for, while, do-while Execute a block repeatedly.
Jump Statements break, continue, return Change the normal flow of execution.

1. if Statement

The if statement executes a block of code only when its condition evaluates to true.


                    if (condition) {
                        // statements
                    }

For example, a voting application can check whether a person has reached the required age.


                    int age = 20;
                    
                    if (age >= 18) {
                        System.out.println("Eligible to vote");
                    }
Output:
Eligible to vote

If age were less than 18, the condition would be false and the statement inside the if block would not execute.

Conditions Must Produce a Boolean Result

Java does not automatically treat numbers such as 1 or 0 as boolean values. A condition used with if must produce either true or false.


                    int age = 20;
                    
                    if (age >= 18) {
                        System.out.println("Adult");
                    }

Here, age >= 18 produces a boolean result, which is why it can be used as the condition.

2. if-else Statement

The if-else statement provides two possible execution paths. If the condition is true, the if block executes. Otherwise, the else block executes.


                    if (condition) {
                        // true block
                    } else {
                        // false block
                    }

                    int number = 15;
                    
                    if (number % 2 == 0) {
                        System.out.println("Even");
                    } else {
                        System.out.println("Odd");
                    }
Output:
Odd

The modulus operator returns the remainder. If a number divided by 2 produces a remainder of 0, it is even; otherwise, it is odd.

Using Multiple Conditions

Logical operators can be combined with conditional statements to represent more complex business rules.


                    int age = 25;
                    boolean hasLicense = true;
                    
                    if (age >= 18 && hasLicense) {
                        System.out.println("Can drive");
                    } else {
                        System.out.println("Cannot drive");
                    }
Output:
Can drive

3. else-if Ladder

An else-if ladder is used when there are multiple possible conditions. Java evaluates the conditions from top to bottom and executes the first block whose condition is true.


                    int marks = 82;
                    
                    if (marks >= 90) {
                        System.out.println("A+");
                    } else if (marks >= 80) {
                        System.out.println("A");
                    } else if (marks >= 70) {
                        System.out.println("B");
                    } else if (marks >= 60) {
                        System.out.println("C");
                    } else {
                        System.out.println("Fail");
                    }
Output:
A
Important: Once Java finds a true condition in an else-if ladder, the remaining conditions are skipped.

4. Nested if Statement

An if statement placed inside another if statement is called a nested if. It is useful when one decision depends on another decision.


                    int age = 25;
                    boolean citizen = true;
                    
                    if (age >= 18) {
                    
                        if (citizen) {
                            System.out.println("Eligible to vote");
                        }
                    
                    }

The inner condition is checked only when the outer condition is true.

5. switch Statement

A switch statement is useful when one expression needs to be compared against multiple fixed values.

Instead of writing a long series of equality checks using if-else, a switch can make the code easier to read when the possible values are known.


                    int day = 2;
                    
                    switch (day) {
                    
                        case 1:
                            System.out.println("Monday");
                            break;
                    
                        case 2:
                            System.out.println("Tuesday");
                            break;
                    
                        case 3:
                            System.out.println("Wednesday");
                            break;
                    
                        default:
                            System.out.println("Invalid day");
                    }
Output:
Tuesday
Parts of a switch Statement
Part Purpose
switch Defines the expression whose value will be evaluated.
case Defines a possible matching value.
break Exits the switch after a matching case is executed.
default Executes when none of the cases match.
Switch Fall-Through

In the traditional switch syntax, if a matching case does not contain break, execution continues into the following case. This behavior is called fall-through.


                    int number = 1;
                    
                    switch (number) {
                    
                        case 1:
                            System.out.println("One");
                    
                        case 2:
                            System.out.println("Two");
                    
                        default:
                            System.out.println("Other");
                    }
Why is this important? Without break, Java can continue executing subsequent cases. This can be intentional in some programs, but accidental fall-through is a common beginner mistake.

6. for Loop

A for loop is commonly used when the number of iterations is known or can be controlled using a counter.


                    for (initialization; condition; update) {
                        // loop body
                    }
How a for Loop Works
  1. The initialization runs once.
  2. The condition is checked.
  3. If the condition is true, the loop body executes.
  4. The update expression executes.
  5. The condition is checked again.

                    for (int i = 1; i <= 5; i++) {
                        System.out.println(i);
                    }
Output:
1
2
3
4
5
for Loop Execution Flow
Step Expression Action
1 int i = 1 Initialize counter
2 i <= 5 Check condition
3 System.out.println(i) Execute loop body
4 i++ Update counter
5 i <= 5 Repeat until condition becomes false

7. while Loop

A while loop repeatedly executes a block as long as its condition remains true.

The condition is checked before every iteration. Therefore, the loop body may execute zero times if the condition is initially false.


                    int i = 1;
                    
                    while (i <= 5) {
                    
                        System.out.println(i);
                          
                        i++;
                    }
Output:
1
2
3
4
5
When to Use while?

A while loop is useful when the number of repetitions is not necessarily known in advance and the loop should continue while a condition remains true.


                    int attempts = 0;
                    boolean loggedIn = false;
                    
                    while (attempts < 3 && !loggedIn) {
                    
                        attempts++;
                    
                        System.out.println("Attempt: " + attempts);
                    
                        // Login logic would be performed here.
                    }

8. do-while Loop

A do-while loop is similar to a while loop, but the condition is checked after the loop body.

Therefore, the body of a do-while loop always executes at least once.


                    int i = 1;
                    
                    do {
                    
                        System.out.println(i);
                    
                        i++;
                    
                    } while (i <= 5);
Output:
1
2
3
4
5
while vs do-while
Feature while do-while
Condition checked Before body After body
Minimum executions 0 1
Best suited for Execute only when the condition is initially true. Execute once before checking whether to continue.

9. Nested Loops

A loop placed inside another loop is called a nested loop. The inner loop executes completely for each iteration of the outer loop.


                    for (int i = 1; i <= 3; i++) {
                    
                        for (int j = 1; j <= 3; j++) {
                    
                            System.out.println("i = " + i + ", j = " + j);
                        }
                    }

Nested loops are commonly used for tables, matrices, patterns, and processing two-dimensional data.

10. Infinite Loop

An infinite loop is a loop whose condition never becomes false. It continues executing until the program is stopped or a jump statement exits the loop.


                    while (true) {
                    
                        System.out.println("Running...");
                    
                    }
Warning: Infinite loops should be intentional. An accidental infinite loop can cause a program to continuously consume CPU resources.

11. break Statement

The break statement immediately terminates the nearest loop or switch statement.


                    for (int i = 1; i <= 10; i++) {
                    
                        if (i == 5) {
                            break;
                        }
                    
                        System.out.println(i);
                    }
Output:
1
2
3
4

When i becomes 5, the break statement terminates the loop immediately.

12. continue Statement

The continue statement skips the remaining statements of the current iteration and moves to the next iteration of the loop.


                    for (int i = 1; i <= 5; i++) {

                        if (i == 3) {
                            continue;
                        }
                    
                        System.out.println(i);
                    }
Output:
1
2
4
5

When i becomes 3, the current iteration is skipped. The loop then continues with the next iteration.

break vs continue
Statement Effect Loop Execution
break Terminates the loop No further iterations
continue Skips current iteration Next iteration continues

13. return Statement

The return statement transfers control back to the caller of a method. It can also return a value from a method.


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

Here, return sends the calculated value back to the code that called the add() method.


                    int result = add(10, 20);
              
                    System.out.println(result);
Output:
30

Choosing the Right Loop

Loop Use When Condition Check
for Number of iterations is known or counter-controlled. Before each iteration
while Continue while a condition remains true. Before each iteration
do-while The body must execute at least once. After each iteration

Common Mistakes

Mistake Problem
Using = instead of == Assignment and comparison are different operations.
Forgetting break in traditional switch Execution may fall through to subsequent cases.
Forgetting to update a loop variable Can result in an infinite loop.
Using incorrect loop boundaries May cause missing or extra iterations.
Using too many nested conditions Can make code difficult to understand and maintain.

Key Points to Remember

  • if executes code only when a condition is true.
  • if-else provides two execution paths.
  • else-if is useful when multiple conditions must be evaluated.
  • Nested if statements allow one decision to depend on another.
  • switch is useful for comparing one expression against multiple fixed cases.
  • for, while, and do-while are used for repetition.
  • A while loop may execute zero times, while a do-while loop executes at least once.
  • Nested loops are useful for patterns, tables, and multidimensional data.
  • break terminates the nearest loop or switch.
  • continue skips the current iteration and proceeds to the next one.
  • return exits a method and can optionally return a value.

Arrays in Java

Store and process multiple values of the same type using arrays.

An array is an object used to store a fixed number of values of the same data type. Instead of creating separate variables for every value, an array allows multiple related values to be stored under one variable name.

For example, instead of creating five separate variables for five student marks, an array can store all five values together.


                    int mark1 = 80;
                    int mark2 = 75;
                    int mark3 = 90;
                    int mark4 = 85;
                    int mark5 = 88;

The same data can be represented more efficiently using an array:

int[] marks = {80, 75, 90, 85, 88};
Important: Java arrays have a fixed length. Once an array is created, its size cannot be changed.

Array Index

Each element in an array is identified using an index. Java uses zero-based indexing, which means the first element is stored at index 0.

Index Value
0 80
1 75
2 90
3 85
4 88
Remember: If an array contains n elements, its valid indexes are 0 through n - 1.

Declaring an Array

An array variable can be declared using square brackets [].

int[] numbers;

At this stage, the variable is declared but an actual array object has not yet been created.

Creating an Array

The new keyword can be used to create an array with a specific size.

int[] numbers = new int[5];

This creates an integer array capable of storing five values. The indexes will be from 0 to 4.

Index Initial Value
0 0
1 0
2 0
3 0
4 0
Default values: Numeric arrays are initialized with 0, boolean arrays with false, and reference-type arrays with null.

Declaring and Initializing an Array

If the values are already known, an array can be declared and initialized in a single statement.

int[] numbers = {10, 20, 30, 40, 50};

Java automatically determines the array size from the number of supplied values.

Accessing Array Elements

An individual element can be accessed using its index.


                    int[] numbers = {10, 20, 30, 40, 50};
                    
                    System.out.println(numbers[0]);
                    System.out.println(numbers[2]);
                    System.out.println(numbers[4]);
Output:
10
30
50

Modifying Array Elements

Array elements are not read-only. An existing value can be replaced by assigning a new value to its index.


                    int[] numbers = {10, 20, 30};
                      
                      numbers[1] = 200;
                      
                      System.out.println(numbers[1]);
Output:
200

The original value 20 at index 1 is replaced with 200.

Array Length

Every Java array provides a length property that returns the number of elements in the array.


                    int[] numbers = {10, 20, 30, 40, 50};
                    
                    System.out.println(numbers.length);
Output:
5
Important: Use array.length for arrays. Do not confuse it with the length() method used by String.

Traversing an Array

Traversing means visiting each element of an array one by one. A traditional for loop is commonly used when the index is required.


                    int[] numbers = {10, 20, 30, 40, 50};
                    
                    for (int i = 0; i < numbers.length; i++) {
                    
                        System.out.println("Index: " + i);
                        System.out.println("Value: " + numbers[i]);
              }

Using numbers.length instead of manually writing the array size makes the loop work correctly even when the array contains a different number of elements.

Enhanced for Loop

Java provides an enhanced for loop, also called the for-each loop, for simple traversal when the index is not required.


                    int[] numbers = {10, 20, 30, 40};
                    
                    for (int number : numbers) {
                    
                        System.out.println(number);
              }
Use for-each when: You only need the values. Use the traditional for loop when you need the index or want to modify elements using their positions.

Taking Array Input

Arrays are frequently populated using values entered by a user. The Scanner class can be used to read values from the keyboard.


                    import java.util.Scanner;
                    
                    Scanner scanner = new Scanner(System.in);
                    
                    int[] numbers = new int[3];
                    
                    for (int i = 0; i < numbers.length; i++) {
                    
                        System.out.print("Enter number: ");
                        numbers[i] = scanner.nextInt();
                    }
                    
                    System.out.println("Values:");
                    
                    for (int number : numbers) {
                        System.out.println(number);
                    }
                    
                    scanner.close();

This approach is useful when the number of values is known but the actual values are provided at runtime.

Searching an Array

A simple way to search an array is to compare each element with the required value.


                    int[] numbers = {10, 20, 30, 40, 50};
                    
                    int search = 30;
                    boolean found = false;
                    
                    for (int number : numbers) {
                    
                        if (number == search) {
                            found = true;
                            break;
                        }
                    }
                    
                    if (found) {
                        System.out.println("Value found");
                    } else {
                        System.out.println("Value not found");
                    }
Output:
Value found

Two-Dimensional Arrays

A two-dimensional array is commonly used to represent data arranged in rows and columns, such as marks of multiple students or values in a matrix.


                    int[][] marks = {
                        {80, 75, 90},
                        {70, 85, 88},
                        {92, 78, 95}
                    };

The first index represents the row and the second index represents the column.


                    System.out.println(marks[0][1]);
                    System.out.println(marks[2][2]);
Output:
75
95

Traversing a Two-Dimensional Array

Nested loops are commonly used to process rows and columns of a two-dimensional array.


                    int[][] marks = {
                        {80, 75, 90},
                        {70, 85, 88}
                    };
                    
                    for (int row = 0; row < marks.length; row++) {
                    
                        for (int col = 0; col < marks[row].length; col++) {
                    
                            System.out.print(marks[row][col] + " ");
                        }
                    
                        System.out.println();
                    }
Output:

                    80 75 90
                    70 85 88
Important: For a two-dimensional array, marks.length gives the number of rows, while marks[row].length gives the number of columns in that particular row.

Arrays of Objects and Strings

Arrays are not limited to primitive data types. They can also store references to objects, including String objects.


                    String[] names = {
                        "Arun",
                        "Kumar",
                        "Priya",
                        "Divya"
                    };
                    
                    for (String name : names) {
                        System.out.println(name);
                    }

Copying an Array

Arrays have fixed size, but their values can be copied into another array. Java provides utility methods such as Arrays.copyOf() for this purpose.


                    import java.util.Arrays;
                    
                    int[] numbers = {10, 20, 30};
                    
                    int[] copy = Arrays.copyOf(numbers, numbers.length);
                    
                    System.out.println(Arrays.toString(copy));
Output:
[10, 20, 30]

Useful Methods of Arrays Class

Java provides the java.util.Arrays class with useful operations for working with arrays.

Method Purpose
Arrays.toString() Converts a one-dimensional array into a readable string.
Arrays.sort() Sorts array elements in ascending order.
Arrays.copyOf() Creates a copy of an array.
Arrays.equals() Compares two arrays based on their elements.
Arrays.binarySearch() Searches for an element in a sorted array.

Sorting an Array


                    import java.util.Arrays;
                      
                      int[] numbers = {50, 20, 40, 10, 30};
                      
                      Arrays.sort(numbers);
                      
                      System.out.println(Arrays.toString(numbers));
Output:
[10, 20, 30, 40, 50]

ArrayIndexOutOfBoundsException

Trying to access an index outside the valid range causes an ArrayIndexOutOfBoundsException.


                    int[] numbers = {10, 20, 30};
                    
                    System.out.println(numbers[3]);
Problem: Valid indexes are 0, 1, and 2. Index 3 does not exist.

Array Limitations

  • Array size is fixed after creation.
  • All elements must be compatible with the declared array type.
  • Inserting or removing elements from the middle is not automatically supported.
  • Arrays do not provide high-level operations such as dynamic resizing.
  • For dynamic collections and more flexible operations, Java provides the Collections Framework, including ArrayList, LinkedList, and other collection types.

Key Points to Remember

  • An array stores multiple values of the same type.
  • Array indexing starts from 0.
  • The last valid index is always length - 1.
  • The size of an array is fixed after creation.
  • Use array.length to determine the number of elements.
  • Use a traditional for loop when the index is required.
  • Use an enhanced for loop when only the values are required.
  • Two-dimensional arrays can represent row-and-column data.
  • Arrays provides utility methods for sorting, copying, searching, and comparing arrays.
  • Use collections such as ArrayList when a dynamic collection is more appropriate than a fixed-size array.

Strings in Java

Work with text, compare strings, manipulate characters and build dynamic text using Java's string classes.

A String represents a sequence of characters. Strings are used throughout Java applications for names, messages, addresses, passwords, descriptions, file paths, user input and other textual data.

Unlike primitive data types such as int and double, String is a class. Therefore, a String is an object.


                    String name = "Java";
                    
                    System.out.println(name);
Important: Java provides special language support for creating String objects using string literals, which makes String convenient to use even though it is a class.

Creating Strings

Strings can commonly be created using a string literal or by explicitly creating a String object.


                    String language = "Java";
                    
                    String framework = new String("Spring Boot");

In normal application code, string literals are generally preferred because they are simpler and allow Java to make use of the String pool.

String Pool

Java maintains a special area called the String pool for string literals. When identical string literals are used, Java can reuse the same pooled String object instead of creating another identical object.


                    String a = "Java";
                    String b = "Java";
                    
                    System.out.println(a == b);
Output:
true

This happens because both variables can refer to the same pooled String object. However, this does not mean that == should normally be used for comparing String content.

String Immutability

Strings in Java are immutable. Once a String object is created, its contents cannot be changed.

When an operation appears to modify a String, Java actually creates another String object containing the new value.


                    String text = "Java";
                    
                    text = text + " Programming";
                    
                    System.out.println(text);
Output:
Java Programming

The original "Java" String was not modified. A new String value was created as a result of the concatenation, and the variable text now refers to that value.

Why does immutability matter? It makes Strings safer to share and use as values, but repeatedly creating new Strings during heavy modification can be inefficient. For such situations, use StringBuilder.

Finding String Length

The length() method returns the number of characters in a String.


                    String message = "Hello Java";
                    
                    System.out.println(message.length());
Output:
10
Remember: Arrays use array.length, while Strings use string.length().

Accessing Characters with charAt()

The charAt() method returns the character at a specified index. String indexes start from 0.


                    String language = "Java";
                    
                    System.out.println(language.charAt(0));
                    System.out.println(language.charAt(2));
Output:
J
v
Important: Attempting to access an invalid index results in StringIndexOutOfBoundsException.

Traversing a String

A String can be processed character by character using a loop and charAt().


                    String word = "Java";
                    
                    for (int i = 0; i < word.length(); i++) {
                        System.out.println(word.charAt(i));
                    }
Output:
J
a
v
a

Comparing Strings

String comparison is an important concept in Java. The == operator checks whether two references refer to the same object, while equals() compares the contents of the Strings.

Using equals()

                    String a = "Java";
                    String b = new String("Java");
                    
                    System.out.println(a.equals(b));
Output:
true

Although the two variables may refer to different objects, equals() returns true because their contents are the same.

Using ==

                    String a = new String("Java");
                    String b = new String("Java");
                    
                    System.out.println(a == b);
Output:
false
Common mistake: Do not use == when your intention is to compare String content. Use equals() instead.

Case-Insensitive Comparison

equalsIgnoreCase() compares two Strings without considering uppercase and lowercase differences.


                    String username = "Admin";
              
                    System.out.println(username.equalsIgnoreCase("admin"));
Output:
true

Changing Letter Case


                    String text = "Java Programming";
                    
                    System.out.println(text.toUpperCase());
                    System.out.println(text.toLowerCase());
Output:
JAVA PROGRAMMING
java programming

Checking Content with contains()

The contains() method checks whether a String contains a specified sequence of characters.


                    String course = "Java Full Stack Development";
                    
                    System.out.println(course.contains("Java"));
                    System.out.println(course.contains("Python"));
Output:
true
false

startsWith() and endsWith()

These methods are useful when you need to check the beginning or ending of a String.


                    String file = "report.pdf";
                    
                    System.out.println(file.startsWith("report"));
                    System.out.println(file.endsWith(".pdf"));
Output:
true
true

Extracting Text with substring()

The substring() method extracts a portion of a String.


                    String text = "Java Programming";
                    
                    System.out.println(text.substring(5));
                    System.out.println(text.substring(0, 4));
Output:
Programming
Java
Remember: The starting index is inclusive, while the ending index in substring(start, end) is exclusive.

Finding Characters and Text with indexOf()

The indexOf() method returns the position of the first occurrence of a character or sequence.


                    String text = "Java Programming";
                    
                    System.out.println(text.indexOf("Java"));
                    System.out.println(text.indexOf("Programming"));
                    System.out.println(text.indexOf("Python"));
Output:
0
5
-1

A result of -1 means that the requested text was not found.

Replacing Text

The replace() method returns a new String with matching characters or sequences replaced.


                    String message = "Java is easy";
                    
                    String result = message.replace("easy", "powerful");
                    
                    System.out.println(result);
Output:
Java is powerful
Remember: Because Strings are immutable, replace() does not modify the original String. It returns a new String.

Removing Extra Whitespace

The trim() method removes leading and trailing whitespace from a String.


                    String name = "   Arun   ";
                    
                    String result = name.trim();
                    
                    System.out.println(result);
Output:
Arun

Checking Empty Strings

The isEmpty() method returns true when the String contains zero characters.


                    String username = "";
              
                    System.out.println(username.isEmpty());
Output:
true

Java also provides isBlank(), which is useful when a String contains only whitespace characters.


                    String value = "   ";
                    
                    System.out.println(value.isBlank());
Output:
true

String Concatenation

Strings can be joined using the + operator.


                    String firstName = "Arun";
                    String lastName = "Kumar";
                    
                    String fullName = firstName + " " + lastName;
                    
                    System.out.println(fullName);
Output:
Arun Kumar

The + operator can also combine Strings with numbers and other data types.


                    String name = "Arun";
                    int age = 22;
                    
                    System.out.println("Name: " + name);
                    System.out.println("Age: " + age);

Comparing Strings with compareTo()

The compareTo() method compares Strings lexicographically. It returns:

  • 0 when both Strings are equal.
  • A negative value when the first String comes before the second.
  • A positive value when the first String comes after the second.

                    String a = "Apple";
                    String b = "Banana";
                    
                    System.out.println(a.compareTo(b));

This method is particularly useful when Strings need to be ordered or sorted.

Splitting a String

The split() method divides a String into multiple parts based on a delimiter and returns a String array.


                    String data = "Java,Python,SQL";
                    
                    String[] languages = data.split(",");
                    
                    for (String language : languages) {
                        System.out.println(language);
                    }
Output:
Java
Python
SQL

Converting Other Values to String

The String.valueOf() method can convert primitive values and other values into their String representation.


                    int age = 22;
                    double salary = 35000.50;
                    
                    String ageText = String.valueOf(age);
                    String salaryText = String.valueOf(salary);
                    
                    System.out.println(ageText);
                    System.out.println(salaryText);

Common String Methods

Method Purpose
length() Returns the number of characters.
charAt() Returns the character at a specified index.
equals() Compares String contents.
equalsIgnoreCase() Compares String contents without case sensitivity.
toUpperCase() Converts text to uppercase.
toLowerCase() Converts text to lowercase.
contains() Checks whether text contains another sequence.
startsWith() Checks the beginning of a String.
endsWith() Checks the ending of a String.
substring() Extracts part of a String.
indexOf() Finds the position of a character or sequence.
replace() Returns a String with matching content replaced.
trim() Removes leading and trailing whitespace.
isEmpty() Checks whether the String contains no characters.
isBlank() Checks whether the String is empty or contains only whitespace.
split() Splits a String into a String array.

StringBuilder

Because String objects are immutable, repeatedly modifying a String can result in the creation of multiple String objects. StringBuilder provides a mutable sequence of characters that can be modified without creating a new String object for every operation.


                    StringBuilder builder = new StringBuilder();
                    
                    builder.append("Java");
                    builder.append(" ");
                    builder.append("Programming");
                    
                    System.out.println(builder);
Output:
Java Programming
Common StringBuilder Methods
Method Purpose
append() Adds content to the end.
insert() Inserts content at a specified position.
delete() Removes characters from a specified range.
replace() Replaces characters within a specified range.
reverse() Reverses the character sequence.
toString() Converts the StringBuilder content into a String.

StringBuilder Example


                    StringBuilder builder = new StringBuilder("Java");
                    
                    builder.append(" Programming");
                    
                    builder.insert(5, "Full Stack ");
                    
                    System.out.println(builder);
                    
                    builder.reverse();
                    
                    System.out.println(builder);
Output:
Java Full Stack Programming
gnimmargorP kcatS lluF avaJ

StringBuilder vs StringBuffer

Java also provides StringBuffer, which is another mutable character sequence. The major difference is related to synchronization and thread safety.

Feature String StringBuilder StringBuffer
Mutable No Yes Yes
Best for Normal text values Frequent modifications Mutable text in synchronized contexts
Synchronization Not applicable Not synchronized Synchronized methods

Key Points to Remember

  • String is a class used to represent a sequence of characters.
  • Strings are immutable.
  • String indexing starts from 0.
  • Use length() to find the number of characters.
  • Use charAt() to access an individual character.
  • Use equals() to compare String contents.
  • == compares object references, not String content.
  • Methods such as substring(), replace(), split(), contains() and indexOf() are frequently used for text processing.
  • Use StringBuilder when frequent String modifications are required.
  • Use StringBuffer when its synchronization behavior is specifically required.

Methods in Java

Create reusable operations using parameters, return values and method overloading.

A method is a named block of code designed to perform a specific task. Instead of writing the same logic repeatedly, you can place it inside a method and call that method whenever the operation is required.

Methods help divide a large program into smaller, manageable units. This makes code easier to read, test, debug and maintain.

Example: Instead of writing the calculation for an employee's annual salary in multiple places, you can create a method such as calculateAnnualSalary() and reuse it wherever required.

Why Use Methods?

Code Reusability

Write a piece of logic once and call it multiple times.

Modularity

Divide a large program into smaller logical operations.

Easier Debugging

Individual methods can be tested and debugged separately.

Maintainability

Changes can be made in one method instead of modifying repeated code.

Method Syntax


                    accessModifier static returnType methodName(parameters) {
                        // method body
                    }
Part Description
accessModifier Controls where the method can be accessed, such as public, private or protected.
static Indicates that the method belongs to the class rather than an individual object.
returnType Specifies the type of value returned by the method.
methodName The name used to call the method.
parameters Input values received by the method.
Method body Contains the statements that perform the required operation.

Methods with void

A method declared with void does not return a value to the caller. It can perform an operation such as displaying information, updating an object or printing a message.


                    static void greet() {
                    
                        System.out.println("Welcome to Java");
                    }
                    
                    public static void main(String[] args) {
                    
                        greet();
                    }
Output:
Welcome to Java

Methods with Parameters

Parameters allow a method to receive data from the caller. They make methods more flexible because the same method can work with different input values.


                    static void greet(String name) {
              
                        System.out.println("Welcome " + name);
                    }
                    
                    public static void main(String[] args) {
                    
                        greet("Arun");
                        greet("Priya");
                    }
Output:
Welcome Arun
Welcome Priya
Parameter vs Argument:
String name in the method declaration is a parameter.
"Arun" passed during the method call is an argument.

Multiple Parameters

A method can accept multiple parameters. Each parameter must have a declared type.


                    static void displayStudent(String name, int age) {
                    
                        System.out.println("Name: " + name);
                        System.out.println("Age: " + age);
                    }
                    
                    public static void main(String[] args) {
                    
                        displayStudent("Arun", 22);
                    }

Methods Returning a Value

A method can return a value to the code that called it. The return type must specify the type of value the method returns.


                    static int add(int a, int b) {
                    
                        return a + b;
                    }
                    
                    public static void main(String[] args) {
                    
                        int result = add(10, 20);
                    
                        System.out.println(result);
                    }
Output:
30

The return statement terminates the method and sends the calculated value back to the caller.

Different Return Types

A method can return primitive values, objects, strings and other reference types.


                    static int getAge() {
                        return 22;
                    }
                    
                    static double getSalary() {
                        return 35000.50;
                    }
                    
                    static String getName() {
                        return "Arun";
                    }
                    
                    static boolean isEligible() {
                        return true;
                    }

void vs Returning Methods

void Method Returning Method
Does not return a value. Returns a value.
Uses void as the return type. Uses a specific return type.
Commonly performs an action. Commonly calculates or produces a result.
static void printName() static String getName()

Calling a Method

A method executes only when it is invoked. For a static method, it can be called directly from another static method in the same class.


                    static void showMessage() {
                    
                        System.out.println("Java Methods");
                    }
                    
                    public static void main(String[] args) {
                    
                        showMessage();
                        showMessage();
                    }

The same method is executed twice because it is called twice.

Instance Methods

A method that belongs to an object rather than the class is called an instance method. It is called using an object reference.


                    class Calculator {
                    
                        int add(int a, int b) {
                    
                            return a + b;
                        }
                    }
                    
                    public class Main {
                    
                        public static void main(String[] args) {
                    
                            Calculator calculator = new Calculator();
                    
                            int result = calculator.add(10, 20);
                    
                            System.out.println(result);
                        }
                    }
Remember:
Static methods are associated with the class.
Instance methods are associated with objects.

Static Method vs Instance Method

Static Method Instance Method
Declared using static. Does not use static.
Belongs to the class. Belongs to an object.
Can be called using the class name. Called using an object.
Math.max(10, 20) calculator.add(10, 20)

Local Variables Inside Methods

Variables declared inside a method are called local variables. They can only be accessed within the method or block where they are declared.


                    static void calculate() {
                    
                        int price = 100;
                        int quantity = 5;
                    
                        int total = price * quantity;
                    
                        System.out.println(total);
                    }
Important: Local variables must be initialized before they are used.

Method and Variable Scope

The scope of a variable determines where it can be accessed.


                    static void calculate() {
                    
                        int total = 500;
                    
                        System.out.println(total);
                    }
                    
                    public static void main(String[] args) {
                    
                        calculate();
                    
                        // total cannot be accessed here
                    }

Passing Values to Methods

Java uses pass-by-value when passing arguments to methods. For primitive values, the method receives a copy of the value.


                    static void changeValue(int number) {
                    
                        number = 100;
                    }
                    
                    public static void main(String[] args) {
                    
                        int value = 50;
                    
                        changeValue(value);
                    
                        System.out.println(value);
                    }
Output:
50

Changing the parameter inside the method does not change the original primitive variable because the method received a copy of its value.

Method Best Practices

  • Give methods meaningful names such as calculateTotal() or displayDetails().
  • Keep a method focused on one logical responsibility whenever possible.
  • Use parameters instead of duplicating similar methods for different values.
  • Use an appropriate return type when the caller needs the result of an operation.
  • Avoid making methods unnecessarily long.
  • Use access modifiers such as private when an operation should not be directly accessible from outside the class.

Key Points to Remember

  • A method is a reusable block of code that performs a specific task.
  • Parameters allow methods to receive input values.
  • Arguments are the actual values passed during a method call.
  • A void method does not return a value.
  • The return statement sends a value back to the caller.
  • Static methods belong to the class.
  • Instance methods belong to objects.
  • Method overloading means using the same method name with different parameter lists.
  • Changing only the return type does not create an overloaded method.
  • Java passes arguments by value.

Object-Oriented Programming (OOP)

Understand the object-oriented approach used to design Java applications.

Object-Oriented Programming (OOP) is a programming approach in which a program is designed using objects that represent entities, their data and the operations that can be performed on that data.

Java is primarily an object-oriented programming language. Instead of keeping all program logic in one large block, Java allows developers to organize applications into classes and objects.

Real-world example: Consider a bank application. A customer has information such as name, account number and balance, and can perform operations such as deposit, withdraw and checkBalance. In an object-oriented design, these related properties and behaviors can be represented using a class and its objects.

Why Object-Oriented Programming?

As applications become larger, managing all the data and operations in a single program becomes difficult. OOP provides a structured way to divide a complex application into smaller and reusable components.

Organize Complex Programs

Large applications can be divided into classes representing different entities and responsibilities.

Reusability

Classes and their functionality can be reused to reduce duplicate code.

Data Protection

Encapsulation allows data and the operations that control it to be managed together.

Easier Maintenance

Changes to one part of an application can often be made without modifying unrelated components.

Procedural Programming vs OOP

One way to understand OOP is to compare it with a procedural approach. Procedural programming generally organizes a program around functions and the sequence of operations, while OOP organizes related data and behavior around objects.

Procedural Approach Object-Oriented Approach
Focuses primarily on procedures and functions. Focuses on objects and their behavior.
Data and functions may be handled separately. Data and related methods can be grouped inside classes.
Programs are commonly organized around operations. Programs are organized around entities and responsibilities.
Can become harder to manage as application complexity increases. Provides mechanisms for organizing larger applications.

Four Pillars of OOP

Object-oriented programming is commonly explained through four major concepts. These concepts work together to create flexible and maintainable software designs.

Encapsulation

Encapsulation combines data and the methods that operate on that data inside a class and provides controlled access to the object's state.

Inheritance

Inheritance allows a class to acquire accessible properties and behavior from another class, helping create relationships between related classes.

Polymorphism

Polymorphism allows the same interface or method call to represent different behaviors depending on the object or implementation.

Abstraction

Abstraction focuses on exposing the essential functionality while hiding unnecessary implementation details.

Class and Object: The Foundation of OOP

Classes and objects are fundamental to Java's object-oriented programming model. A class defines the structure and behavior, while an object is an instance created from that class.


                    class Student {
                    
                        String name;
                        int age;
                    
                        void display() {
                            System.out.println(name);
                            System.out.println(age);
                        }
                    }
                    
                    public class Main {
                    
                        public static void main(String[] args) {
                    
                            Student student = new Student();
                    
                            student.name = "Arun";
                            student.age = 22;
                    
                            student.display();
                        }
                    }
Output:
Arun
22

Here, Student is the class and student is an object created from that class. The fields represent the object's state, while display() represents its behavior.

OOP Example: Banking System

A banking application is a good example of how OOP can model real-world entities. An account can have data such as an account number and balance, along with operations such as deposit and withdrawal.


                    class BankAccount {
                    
                        String accountNumber;
                        double balance;
                    
                        void deposit(double amount) {
                    
                            balance += amount;
                        }
                    
                        void displayBalance() {
                    
                            System.out.println("Balance: " + balance);
                        }
                    }

Instead of keeping account data and banking operations unrelated, the class groups them into a single logical unit.

How OOP Concepts Work Together

The four pillars are not completely independent. A real Java application can use several OOP concepts together.

Concept Purpose Example in an Application
Class & Object Model entities and create instances. Customer, Product, Employee
Encapsulation Control access to object data. Private account balance
Inheritance Represent relationships between related classes. Manager extends Employee
Polymorphism Allow different implementations through a common type. Different payment methods
Abstraction Expose essential operations while hiding implementation details. Payment interface

Benefits of OOP in Java

  • Modularity: Applications can be divided into classes with specific responsibilities.
  • Reusability: Existing classes and functionality can be reused in different parts of an application.
  • Maintainability: Well-designed classes make application changes easier to manage.
  • Extensibility: Concepts such as inheritance and polymorphism can make applications easier to extend.
  • Data Control: Encapsulation provides controlled access to an object's internal data.
  • Real-world Modeling: Objects can represent entities and concepts found in real applications.
OOP is More Than Just Creating Objects

Creating a class and an object is only the starting point. Effective object-oriented programming also involves deciding how classes should interact, which data should be exposed, which behavior should be shared, and how responsibilities should be divided between objects.

OOP Learning Flow in Java

1
Classes & Objects

Learn how Java models entities using classes and objects.

2
Methods & Constructors

Define object behavior and initialize object state.

3
Encapsulation

Control how object data is accessed and modified.

4
Inheritance

Build relationships between related classes.

5
Polymorphism

Work with different implementations through a common type.

6
Abstraction

Hide implementation details and expose essential functionality.

Key Points to Remember

  • OOP stands for Object-Oriented Programming.
  • Java uses classes and objects as fundamental building blocks of object-oriented applications.
  • A class defines the structure and behavior of objects.
  • An object is an instance of a class.
  • The four commonly discussed pillars of OOP are Encapsulation, Inheritance, Polymorphism and Abstraction.
  • Good OOP design focuses not only on objects, but also on clear responsibilities and relationships between classes.

Classes & Objects

Understand classes, objects, fields, methods and constructors in Java.

Java is an object-oriented programming language. In object-oriented programming, a program can be designed around objects that contain both data and the behavior that operates on that data.

A class defines the structure and behavior of objects, while an object is an actual instance created from that class.

Simple example: A class can be considered a blueprint for a car. The blueprint defines properties such as color, model and speed, along with behaviors such as start(), stop() and accelerate(). Individual cars created from that blueprint are objects.

What is a Class?

A class is a user-defined type that groups related fields and methods together. It describes what an object should contain and what it should be able to do.


                    class Student {
                    
                        String name;
                        int age;
                    
                        void display() {
                            System.out.println(name);
                            System.out.println(age);
                        }
                    }

In this example, name and age are fields, while display() is a method.

Fields in a Class

Fields are variables declared inside a class. They represent the state or data of an object.


                    class Student {
                    
                        String name;
                        int age;
                        String course;
                    }

Every Student object can have its own values for name, age and course.

What is an Object?

An object is an instance of a class. It is created using the new keyword.

Student student = new Student();

This statement involves two important parts:

  • Student student declares a reference variable named student.
  • new Student() creates a new Student object.
Important: The variable student stores a reference to the object; it is not the object itself.

Creating and Using an Object


                    class Student {
                    
                        String name;
                        int age;
                    
                        void display() {
                            System.out.println("Name: " + name);
                            System.out.println("Age: " + age);
                        }
                    }
                    
                    public class Main {
                    
                        public static void main(String[] args) {
                    
                            Student student = new Student();
                    
                            student.name = "Arun";
                            student.age = 22;
                    
                            student.display();
                        }
                    }
Output:
Name: Arun
Age: 22

Accessing Members Using the Dot Operator

The dot operator . is used to access fields and methods through an object reference.


                    student.name = "Arun";
                    student.age = 22;
                    
                    student.display();

Here, student.name accesses the object's field, while student.display() calls its method.

Methods in a Class

A method defines behavior that an object can perform. Methods can accept parameters and can return values.


                    class Calculator {
                    
                        int add(int a, int b) {
                            return a + b;
                        }
                    }

The method can be called using a Calculator object.


                    Calculator calculator = new Calculator();
                    
                    int result = calculator.add(10, 20);
                    
                    System.out.println(result);
Output:
30

Creating Multiple Objects

A single class can be used to create many objects. Each object can maintain its own state.


                    class Student {
                    
                        String name;
                        int age;
                    }
                    
                    public class Main {
                    
                        public static void main(String[] args) {
                    
                            Student student1 = new Student();
                            Student student2 = new Student();
                    
                            student1.name = "Arun";
                            student1.age = 22;
                    
                            student2.name = "Priya";
                            student2.age = 21;
                    
                            System.out.println(student1.name);
                            System.out.println(student2.name);
                        }
                    }
Output:
Arun
Priya

Although both objects were created from the same class, they contain separate instance data.

Instance Variables and Instance Methods

Fields and methods that belong to individual objects are called instance members.


                    class Employee {
                    
                        String name;
                        double salary;
                    
                        void display() {
                            System.out.println(name);
                            System.out.println(salary);
                        }
                    }

Every Employee object has its own name and salary.

Static Members

A static member belongs to the class rather than to a particular object. A static field is shared among all objects of that class.


                    class Employee {
                    
                        String name;
                        static String company = "ABC Technologies";
                    }
                    
                    public class Main {
                    
                        public static void main(String[] args) {
                    
                            Employee e1 = new Employee();
                            Employee e2 = new Employee();
                    
                            e1.name = "Arun";
                            e2.name = "Priya";
                    
                            System.out.println(Employee.company);
                        }
                    }
Key difference: Instance fields belong to individual objects, while static fields belong to the class and can be shared by all objects.

Object Components

Component Meaning Example
State Data maintained by an object. name, age
Behavior Actions performed by an object. display(), calculateSalary()
Identity Each object has its own identity. student1, student2

Class vs Object

Class Object
Blueprint or template. Instance created from the class.
Defines fields and methods. Contains actual values for instance fields.
Does not represent an individual entity. Represents an individual entity.
Declared using the class keyword. Commonly created using new.

Why Use Classes and Objects?

Organization

Related data and behavior can be grouped together inside a class.

Reusability

One class can be used to create many objects without rewriting the same structure.

Data Control

Access modifiers and encapsulation can control how object data is accessed.

Object-Oriented Design

Classes provide the foundation for inheritance, polymorphism and abstraction.

Key Points to Remember

  • A class is a blueprint that defines the structure and behavior of objects.
  • An object is an instance of a class.
  • Fields represent the object's state.
  • Methods represent the object's behavior.
  • The new keyword is commonly used to create objects.
  • Constructors are used to initialize objects.
  • The this keyword refers to the current object.
  • Instance members belong to individual objects.
  • Static members belong to the class and can be shared.
  • Classes and objects form the foundation for Java's object-oriented programming concepts.

Constructors in Java

Initialize objects and define their initial state when they are created.

A constructor is a special member of a class that is automatically invoked when an object is created using the new keyword.

The primary purpose of a constructor is to initialize the object's instance variables and place the object into a valid initial state.

Example: When a Student object is created, its constructor can initialize the student's name, age and course instead of requiring these values to be assigned separately after object creation.

Constructor vs Method

Constructor Method
Used to initialize objects. Used to perform an operation.
Must have the same name as the class. Can have any valid method name.
Does not have a return type. Can have a return type or void.
Called automatically when an object is created. Normally called explicitly.
Cannot be inherited like ordinary methods. Methods can participate in inheritance.

Basic Constructor

A constructor has the same name as its class and does not specify a return type.


                    class Student {
                    
                        String name;
                        int age;
                    
                        Student() {
                    
                            name = "Unknown";
                            age = 0;
                        }
                    }
                    
                    public class Main {
                    
                        public static void main(String[] args) {
                    
                            Student student = new Student();
                    
                            System.out.println(student.name);
                            System.out.println(student.age);
                        }
                    }
Output:
Unknown
0

When new Student() is executed, the Student() constructor runs automatically and initializes the object.

No-Argument Constructor

A constructor that does not receive any parameters is called a no-argument constructor.


                    class Employee {
                    
                        String name;
                        double salary;
                    
                        Employee() {
                    
                            name = "Not Assigned";
                            salary = 0.0;
                        }
                    }

This type of constructor is useful when every newly created object should begin with a predefined or default application-specific state.

Default Constructor

If a class does not declare any constructor, Java provides a default constructor automatically. It initializes instance variables with their default values.


                    class Student {
                    
                        String name;
                        int age;
                    }
                    
                    public class Main {
                    
                        public static void main(String[] args) {
                    
                            Student student = new Student();
                    
                            System.out.println(student.name);
                            System.out.println(student.age);
                        }
                    }
Output:
null
0
Important: Java provides a compiler-generated default constructor only when the class does not declare any constructor.

Parameterized Constructor

A parameterized constructor accepts values from the caller and uses them to initialize the newly created object.


                    class Student {
                    
                        String name;
                        int age;
                    
                        Student(String name, int age) {
                    
                            this.name = name;
                            this.age = age;
                        }
                    }
                    
                    public class Main {
                    
                        public static void main(String[] args) {
                    
                            Student s1 = new Student("Arun", 22);
                            Student s2 = new Student("Priya", 21);
                    
                            System.out.println(s1.name);
                            System.out.println(s2.name);
                        }
                    }
Output:
Arun
Priya

Using the this Keyword

The this keyword refers to the current object. It is commonly used when a constructor parameter has the same name as an instance variable.


                    class Employee {
                    
                        String name;
                        double salary;
                    
                        Employee(String name, double salary) {
                    
                            this.name = name;
                            this.salary = salary;
                        }
                    }
Understanding the assignment:
this.name → instance variable of the current object.
name → constructor parameter.
Therefore: this.name = name; assigns the parameter value to the object's instance variable.

Initializing Multiple Objects

A parameterized constructor allows the same class to create multiple objects with different initial values.


                    class Product {
                    
                        String name;
                        double price;
                    
                        Product(String name, double price) {
                    
                            this.name = name;
                            this.price = price;
                        }
                    }
                    
                    public class Main {
                    
                        public static void main(String[] args) {
                    
                            Product p1 = new Product("Laptop", 55000);
                            Product p2 = new Product("Mouse", 800);
                            Product p3 = new Product("Keyboard", 1500);
                    
                            System.out.println(p1.name);
                            System.out.println(p2.name);
                            System.out.println(p3.name);
                        }
                    }

Each object has its own copy of the instance variables and can therefore maintain different state.

Constructor Overloading

A class can have multiple constructors as long as their parameter lists are different. This is called constructor overloading.


                    class Student {
                    
                        String name;
                        int age;
                    
                        Student() {
                    
                            name = "Unknown";
                            age = 0;
                        }
                    
                        Student(String name) {
                    
                            this.name = name;
                            age = 0;
                        }
                    
                        Student(String name, int age) {
                    
                            this.name = name;
                            this.age = age;
                        }
                    }

Java selects the appropriate constructor based on the arguments supplied during object creation.


                    Student s1 = new Student();
                    
                    Student s2 = new Student("Arun");
                    
                    Student s3 = new Student("Priya", 21);

Constructor Chaining Using this()

A constructor can call another constructor in the same class using this(). This is called constructor chaining. It helps avoid repeating initialization logic.


                    class Student {
                    
                        String name;
                        int age;
                    
                        Student() {
                    
                            this("Unknown", 0);
                        }
                    
                        Student(String name, int age) {
                    
                            this.name = name;
                            this.age = age;
                        }
                    }
                    
                    public class Main {
                    
                        public static void main(String[] args) {
                    
                            Student student = new Student();
                    
                            System.out.println(student.name);
                            System.out.println(student.age);
                        }
                    }
Output:
Unknown
0
Important: A call to this() must be the first statement inside the constructor.

this() vs this

Syntax Purpose Example
this.variable Refers to the current object's instance variable. this.name = name;
this() Calls another constructor in the same class. this("Unknown", 0);

Important Rules of Constructors

  • A constructor must have the same name as the class.
  • A constructor does not have a return type, including void.
  • Constructors are invoked when objects are created.
  • A class can have multiple overloaded constructors.
  • Constructors can accept parameters.
  • If no constructor is declared, Java can provide a compiler-generated default constructor.
  • If a parameterized constructor is declared, Java does not automatically provide a no-argument constructor.
  • A constructor can call another constructor using this().
  • Constructors are not inherited by subclasses.

Key Points to Remember

  • A constructor initializes an object when it is created.
  • The constructor name must match the class name.
  • Constructors do not have return types.
  • A no-argument constructor does not accept parameters.
  • A parameterized constructor receives values during object creation.
  • Constructor overloading allows a class to provide different ways to initialize objects.
  • this refers to the current object.
  • this() calls another constructor in the same class.
  • If you declare your own constructor, Java does not automatically add a no-argument constructor for you.

Encapsulation

Protect object data and control how it is accessed or modified.

Encapsulation is one of the fundamental principles of Object-Oriented Programming (OOP). It means bundling data and the methods that operate on that data inside a class, while restricting direct access to the internal state of an object.

In Java, encapsulation is mainly achieved by declaring fields as private and providing controlled access through methods such as getters and setters. This prevents other classes from directly changing important object data.

Why is Encapsulation Needed?

Without encapsulation, any class that has access to an object could directly modify its fields. This can result in invalid or unexpected values. Encapsulation allows a class to decide how its data can be read, changed, or validated.


                    class BankAccount {

                      private double balance;
                      
                      public void deposit(double amount) {
                          if (amount > 0) {
                              balance += amount;
                          }
                      }
                      
                      public double getBalance() {
                          return balance;
                      }

                    }

The balance field is declared as private, so it cannot be accessed directly from outside the BankAccount class.

Direct access is not allowed:


                    BankAccount account = new BankAccount();
                  

Instead of allowing direct modification, the class provides the deposit() method. This method checks whether the amount is valid before modifying the balance.

Controlled access:


                    BankAccount account = new BankAccount();
                    
                    account.deposit(5000);
                    
                    System.out.println(account.getBalance());

This approach gives the BankAccount class complete control over how its internal data is modified. For example, the class can reject negative deposits without requiring the calling code to understand the internal validation rules.

Encapsulation Using Getters and Setters

A getter is a method used to read a private field, while a setter is used to modify it. A setter can also validate the value before assigning it.


                    class Student {

                      private int age;
                      
                      public int getAge() {
                          return age;
                      }
                      
                      public void setAge(int age) {
                          if (age >= 5 && age <= 100) {
                              this.age = age;
                          }
                      }
                      
                    }

Here, the age field cannot be changed directly. The setAge() method controls which values are accepted.


                    Student student = new Student();
                    
                    student.setAge(22);
                    
                    System.out.println(student.getAge());

If a program attempts to assign an invalid value such as -10, the setter can reject it. This is one of the major advantages of encapsulation: business rules and validation can be kept inside the class.

Access Control in Encapsulation

Encapsulation commonly works together with Java's access modifiers. Fields containing internal state are often made private, while selected methods are exposed as public.

Member Purpose
private field Protects internal object data from direct external access.
public getter Provides controlled read access to a value.
public setter Provides controlled modification and can perform validation.
public business method Allows specific operations without exposing internal implementation.
Key idea: Encapsulation does not simply mean making variables private. The important concept is controlling access to an object's internal state and allowing the class to enforce rules when that state changes.
Benefits of Encapsulation:
  • Protects data from unwanted direct modification.
  • Allows validation before changing object state.
  • Reduces dependencies between classes.
  • Makes classes easier to maintain and modify.
  • Hides internal implementation details.
  • Improves security and reliability of object-oriented programs.

Inheritance

Reuse and extend properties and behavior from an existing class.

Inheritance is an important concept in Object-Oriented Programming (OOP) that allows one class to acquire accessible fields and methods from another class. It promotes code reusability and allows developers to create new classes based on existing classes.

The class whose properties and methods are inherited is called the superclass, parent class, or base class. The class that inherits from it is called the subclass, child class, or derived class.

In Java, inheritance between classes is created using the extends keyword.

Basic Example of Inheritance

                    class Animal {
                    
                      void eat() {
                          System.out.println("Animal is eating");
                      }
                    
                    }
                    
                    class Dog extends Animal {
                    
                      void bark() {
                          System.out.println("Dog is barking");
                      }
                    
                    }
                    
                    public class Main {
                    
                      public static void main(String[] args) {
                      
                          Dog dog = new Dog();
                      
                          dog.eat();
                          dog.bark();
                      }
                    
                    }

Here, Animal is the parent class and Dog is the child class. Since Dog extends Animal, a Dog object can access the inherited eat() method as well as its own bark() method.

Key idea: Inheritance represents an "is-a" relationship. For example, a Dog is an Animal, so Dog can inherit common behavior from Animal.
What Does a Subclass Inherit?

A subclass can use accessible members of its superclass, but inheritance does not mean that every member becomes directly accessible. private members of the parent class cannot be accessed directly by the child class.

Parent Member Accessible in Child?
public Yes
protected Yes, subject to Java's access rules
default / package-private Yes, when the classes are in the same package
private No direct access

Types of Inheritance

Inheritance can be organized into different structures depending on how parent and child classes are related.

Type Description Java Support
Single One child class inherits from one parent class. Supported
Multilevel A class inherits from a class that itself inherits from another class. Supported
Hierarchical Multiple child classes inherit from the same parent class. Supported
Multiple using classes One class inherits from multiple classes. Not supported
Multiple through interfaces A class can implement multiple interfaces. Supported
Single Inheritance

In single inheritance, one subclass extends one superclass.


                    class Vehicle {
                    
                      void start() {
                          System.out.println("Vehicle starts");
                      }
                    
                    }
                    
                    class Car extends Vehicle {
                    
                      void drive() {
                          System.out.println("Car is driving");
                      }
                    
                    }
Multilevel Inheritance

In multilevel inheritance, inheritance occurs across multiple levels. A child class can inherit members through its parent and grandparent classes.


                    class Animal {
                    
                      void eat() {
                          System.out.println("Eating");
                      }
                    
                    }
                    
                    class Dog extends Animal {
                    
                      void bark() {
                          System.out.println("Barking");
                      }
                    
                    }
                    
                    class Puppy extends Dog {
                    
                      void play() {
                          System.out.println("Playing");
                      }
                    
                    }

A Puppy object can access its own play() method, the inherited bark() method from Dog, and the eat() method inherited from Animal.

Hierarchical Inheritance

Hierarchical inheritance occurs when multiple child classes inherit from the same parent class. Each child class can reuse the common properties and methods of the parent while also defining its own specific behavior.


                    class Animal {
                    
                      void eat() {
                        System.out.println("Animal is eating");
                      }
                    }
                    
                    class Dog extends Animal {
                    
                      void bark() {
                        System.out.println("Dog is barking");
                      }
                    }
                    
                    class Cat extends Animal {
                      
                      void meow() {
                        System.out.println("Cat is meowing");
                      }
                    }
                      
                    class Cow extends Animal {
                      
                      void moo() {
                        System.out.println("Cow is mooing");
                      }
                    }
                    
                    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();
                        
                        Cow cow = new Cow();
                        cow.eat();
                        cow.moo();
                      }
                    }

Here, Animal is the parent class, while Dog, Cat, and Cow are child classes. All three child classes inherit the eat() method from Animal, but each class also has its own specific method.

super Keyword

The super keyword refers to the immediate superclass. It is commonly used when a child class needs to access a parent class method, field, or constructor.

Calling a Parent Method

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

Here, super.sound() calls the implementation of sound() from the parent class before executing the Dog class implementation.

Why Java Does Not Support Multiple Inheritance with Classes

Java does not allow a class to extend multiple classes. This avoids ambiguity when two parent classes contain methods with the same name.


                    // Not allowed
                    
                    class A {
                      void show() { }
                    }
                    
                    class B {
                      void show() { }
                    }
                    
                    // class C extends A, B { }

However, Java supports multiple inheritance of type through interfaces. A class can implement multiple interfaces and provide the required implementations.

Benefits of Inheritance:
  • Promotes code reusability.
  • Reduces duplicate code between related classes.
  • Allows subclasses to extend existing functionality.
  • Supports method overriding and runtime polymorphism.
  • Creates a clear relationship between related classes.
Key idea: Inheritance allows a child class to reuse and specialize functionality from a parent class. Use inheritance when there is a genuine "is-a" relationship, such as Dog is an Animal or Car is a Vehicle.

Polymorphism

Allow the same method or operation to behave differently in different contexts.

Polymorphism means "many forms". It is one of the four fundamental principles of Object-Oriented Programming (OOP). Polymorphism allows the same method name or operation to represent different behaviors depending on how it is used.

In Java, polymorphism is mainly achieved through method overloading and method overriding. These are commonly categorized as compile-time polymorphism and runtime polymorphism.

Key idea: The same method call can produce different behavior depending on the parameters supplied or the actual object involved.

Types of Polymorphism

Type Commonly Achieved Using Decision Made
Compile-Time Polymorphism Method Overloading At compile time
Runtime Polymorphism Method Overriding At runtime

Compile-Time Polymorphism

Compile-time polymorphism occurs when the compiler determines which method should be called during compilation. In Java, this is commonly achieved using method overloading.

Method overloading means defining multiple methods with the same name but with different parameter lists. The methods may differ in the number, type, or order of parameters.


                    class Calculator {

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

                    }
                    
                    public class Main {

                      public static void main(String[] args) {
                      
                          Calculator calculator = new Calculator();
                      
                          System.out.println(calculator.add(10, 20));
                          System.out.println(calculator.add(10, 20, 30));
                          System.out.println(calculator.add(10.5, 20.5));
                      }

                    }

All three methods are named add(), but their parameter lists are different. The compiler determines which version should be called based on the arguments passed to the method.

Output:

                    30
                    60
                    31.0
Rules for Method Overloading
  • The methods must have the same name.
  • The parameter list must be different.
  • Changing only the return type is not sufficient for overloading.
  • Overloaded methods can have different access modifiers.

Invalid overloading:


                    int add(int a, int b) {
                      return a + b;

                    }
                    
                    // Not allowed: only return type is different
                    double add(int a, int b) {
                      return a + b;
                    }

Runtime Polymorphism

Runtime polymorphism occurs when a subclass overrides a method of its superclass and Java determines which implementation to execute at runtime.

Runtime polymorphism is achieved through method overriding. It is closely related to inheritance and allows a parent-class reference to refer to an object of a child class.


                    class Animal {

                      void sound() {
                          System.out.println("Animal 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

Notice the following statement:

Animal animal = new Dog();

The Animal reference points to a Dog object. Although the reference type is Animal, the actual object is Dog. Therefore, when animal.sound() is called, Java executes the overridden sound() method from Dog.

Reference Type vs Object Type

Understanding the difference between the reference type and the actual object type is important for runtime polymorphism.

Concept Example Meaning
Reference Type Animal Determines what members can be accessed through the reference.
Object Type Dog Determines which overridden method executes at runtime.
Runtime Polymorphism with Multiple Subclasses

Runtime polymorphism becomes especially useful when several subclasses provide different implementations of the same parent method.


                    class Animal {

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

                    }
                    
                    class Dog extends Animal {

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

                    }
                    
                    class Cat extends Animal {

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

                    }
                    
                    public class Main {

                      public static void main(String[] args) {
                      
                          Animal animal;
                      
                          animal = new Dog();
                          animal.sound();
                      
                          animal = new Cat();
                          animal.sound();
                      }

                    }

The same reference animal can refer to different objects. When the method is called, Java executes the implementation belonging to the actual object.

Output:

                    Dog barks
                    Cat meows

Method Overloading vs Method Overriding

Feature Overloading Overriding
Purpose Provide different versions of a method. Provide a specialized implementation.
Class relationship Can occur within the same class. Requires inheritance.
Parameters Must be different. Must match the overridden method.
Binding Compile-time Runtime
Common concept Compile-time polymorphism Runtime polymorphism
Important: A method cannot be overloaded by changing only its return type. For overriding, the subclass method must follow the overriding rules of Java, including compatible return types and valid access visibility.
Benefits of Polymorphism:
  • Allows one interface or method name to represent multiple behaviors.
  • Reduces the need for repetitive conditional logic.
  • Makes programs easier to extend with new subclasses.
  • Supports flexible and reusable object-oriented designs.
  • Runtime polymorphism enables dynamic method selection.
Key idea: Polymorphism allows the same method call to behave differently depending on the situation. Overloading is generally resolved at compile time, while overriding enables Java to select the appropriate implementation at runtime.

Abstraction

Hide implementation details and expose only essential behavior.

Abstraction is one of the four fundamental principles of Object-Oriented Programming (OOP). It focuses on what an object does rather than how it does it. Abstraction hides unnecessary implementation details and exposes only the functionality that is required by the user.

A simple real-world example is an ATM. A user can withdraw money, deposit money, or check a balance without knowing the internal implementation of how the ATM communicates with the bank server and processes the transaction. The complex implementation is hidden behind a simple interface.

Java supports abstraction primarily through abstract classes and interfaces.

Key idea: Abstraction hides implementation complexity and exposes only the essential operations that other parts of the program need to use.

Abstract Class

An abstract class is a class declared using the abstract keyword. It can contain both abstract methods, which do not have a body, and concrete methods, which contain an implementation.


                    abstract class Animal {

                      abstract void sound();
                    
                      void eat() {
                          System.out.println("Animal is eating");
                      }

                    }
                    
                    class Dog extends Animal {

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

                    }
                    
                    public class Main {

                      public static void main(String[] args) {
                      
                          Dog dog = new Dog();
                      
                          dog.sound();
                          dog.eat();
                      }

                    }

Here, Animal defines the general behavior of an animal. The sound() method is abstract because different animals can produce different sounds. The Dog class provides the specific implementation of that method.

Output:

                    Dog barks
                    Animal is eating
Abstract Method

An abstract method is declared without a method body. It defines what a subclass must do, while the subclass decides how the operation should be implemented.


                    abstract class Animal {

                      abstract void sound();

                    }

A concrete subclass must normally provide an implementation for the inherited abstract method.


                    class Dog extends Animal {

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

                    }
Concrete Methods in an Abstract Class

An abstract class is not limited to abstract methods. It can also contain normal methods with complete implementations. This allows a parent class to provide common functionality while leaving specialized behavior to subclasses.


                    abstract class Vehicle {

                      abstract void start();
                      
                      void stop() {
                          System.out.println("Vehicle stopped");
                      }

                    }
                    
                    class Car extends Vehicle {

                      @Override
                      void start() {
                          System.out.println("Car starts with a key");
                      }

                    }

In this example, start() must be implemented by the subclass, while stop() is already provided by the abstract class. This allows the parent class to combine common behavior with subclass-specific behavior.

Why Can't an Abstract Class Be Instantiated?

An abstract class may contain incomplete behavior through abstract methods. Therefore, Java does not allow an abstract class to be instantiated directly.


                    // Not allowed
              
                   // Animal animal = new Animal();

Instead, an object of a concrete subclass can be created.


                    Animal animal = new Dog();
              
                    animal.sound();

This also demonstrates how abstraction can work together with runtime polymorphism. The reference is of type Animal, while the actual object is a Dog.

Abstraction Using Interfaces

An interface is another major mechanism for achieving abstraction in Java. It defines a contract that implementing classes must follow.


                    interface Payment {

                      void pay(double amount);

                    }
                    
                    class UPI implements Payment {

                      @Override
                      public void pay(double amount) {
                          System.out.println("Paid ₹" + amount + " using UPI");
                      }

                    }
                    
                    public class Main {

                      public static void main(String[] args) {
                      
                          Payment payment = new UPI();
                      
                          payment.pay(1500);
                      }

                    }

The Payment interface defines the operation pay() without specifying the implementation. The UPI class provides the actual implementation.

Think of an interface as a contract: it specifies what functionality a class must provide, while the class decides how that functionality is implemented.

Abstract Class vs Interface

Feature Abstract Class Interface
Keyword abstract class interface
Methods Can contain abstract and concrete methods. Primarily defines a contract; can also contain default, static, and private methods.
Fields Can have instance and static fields. Fields are implicitly public static final.
Constructor Can have constructors. Cannot have constructors.
Inheritance A class can extend only one class. A class can implement multiple interfaces.
Best suited for Sharing common state and behavior among related classes. Defining a common contract that different classes can implement.

Abstraction vs Encapsulation

Abstraction and encapsulation are related, but they solve different problems.

Concept Main Purpose Example
Abstraction Hides implementation details and exposes essential behavior. Providing pay() without exposing payment processing details.
Encapsulation Protects internal data and controls how it is accessed or modified. Making balance private and accessing it through methods.
Benefits of Abstraction:
  • Hides unnecessary implementation details.
  • Reduces complexity for users of a class.
  • Creates clear contracts between classes.
  • Improves code maintainability and flexibility.
  • Allows different classes to provide different implementations of the same behavior.
  • Works effectively with inheritance and polymorphism.
When to use abstraction: Use abstraction when you know what functionality a group of classes should provide, but the exact implementation may differ between those classes.
Key idea: Abstraction focuses on what an object should do, while hiding the details of how it does it. In Java, abstract classes are useful when related classes need to share state or common implementation, while interfaces are useful for defining common contracts across different classes.

Interfaces

Define contracts that classes can implement for abstraction and flexible design.

An interface in Java defines a contract that specifies what a class should do without requiring the interface to provide the complete implementation of that behavior.

Interfaces are widely used to achieve abstraction, loose coupling, and polymorphism. A class uses the implements keyword to implement an interface.

Key idea: An interface describes a set of behaviors that a class agrees to provide. The implementing class is responsible for providing the required implementation.

Creating an Interface

An interface is declared using the interface keyword. Methods declared without a body are abstract methods by default, unless they are declared as default, static, or certain other supported interface method types.


                    interface Payment {

                      void pay(double amount);

                    }
                    
                    class UPI implements Payment {

                      @Override
                      public void pay(double amount) {
                          System.out.println("Paid using UPI: " + amount);
                      }

                    }

Here, Payment defines the pay() operation. The UPI class implements the interface and provides the actual behavior for that method.

Using an Interface Reference

An interface reference can refer to an object of any class that implements that interface. This allows interfaces to work naturally with runtime polymorphism.


                    interface Payment {

                      void pay(double amount);

                    }
                    
                    class UPI implements Payment {

                      @Override
                      public void pay(double amount) {
                          System.out.println("Paid using UPI: " + amount);
                      }

                    }
                    
                    class CreditCard implements Payment {

                      @Override
                      public void pay(double amount) {
                          System.out.println("Paid using Credit Card: " + amount);
                      }

                    }
                    
                    public class Main {

                      public static void main(String[] args) {
                      
                          Payment payment;
                      
                          payment = new UPI();
                          payment.pay(1500);
                      
                          payment = new CreditCard();
                          payment.pay(2500);
                      }

                    }

The same Payment reference can point to different objects. When pay() is called, Java executes the implementation provided by the actual object.

Output:

                    Paid using UPI: 1500.0
                    Paid using Credit Card: 2500.0

Implementing Multiple Interfaces

A Java class can implement multiple interfaces. This is an important feature because Java does not allow a class to extend multiple classes. Multiple interfaces allow a class to follow several independent contracts.


                    interface Printable {
                    
                      void print();

                    }
                    
                    interface Showable {

                      void show();

                    }
                    
                    class Demo implements Printable, Showable {

                      @Override
                      public void print() {
                          System.out.println("Printing document");
                      }
                    
                      @Override
                      public void show() {
                          System.out.println("Showing document");
                      }

                    }
                    
                    public class Main {

                      public static void main(String[] args) {
                      
                          Demo demo = new Demo();
                      
                          demo.print();
                          demo.show();
                      }

                    }

The Demo class implements both Printable and Showable. Therefore, it must provide implementations for both print() and show().

Important: A class can extend only one class but can implement multiple interfaces.

Interface Variables

Variables declared inside an interface are implicitly public, static, and final. They behave like constants and cannot be modified by implementing classes.


                    interface Payment {

                      double TAX = 0.18;
                      
                      void pay(double amount);

                    }
                    
                    public class Main {

                      public static void main(String[] args) {
                      
                          System.out.println(Payment.TAX);
                      }

                    }

The constant can be accessed using the interface name: Payment.TAX. It cannot be reassigned.

Default Methods

Since Java 8, interfaces can contain default methods. A default method has a body and provides a default implementation that implementing classes can use or override.


                    interface Vehicle {

                      void start();
                      
                        default void stop() {
                            System.out.println("Vehicle stopped");
                        }

                    }
                    
                    class Car implements Vehicle {

                      @Override
                      public void start() {
                          System.out.println("Car started");
                      }
                    
                    }
                    
                    public class Main {

                      public static void main(String[] args) {
                      
                          Car car = new Car();
                      
                          car.start();
                          car.stop();
                      }

                    }

The Car class only needs to implement start(). It can directly use the default implementation of stop().

Static Methods in Interfaces

Interfaces can also contain static methods. Static interface methods belong to the interface itself and are called using the interface name.


                    interface Calculator {

                      static int square(int number) {
                          return number * number;
                      }

                    }
                    
                    public class Main {

                      public static void main(String[] args) {
                      
                          System.out.println(Calculator.square(5));
                      }

                    }

The method is called using Calculator.square(). It is not called through an object of an implementing class.

Interface vs Class

Feature Interface Class
Declaration interface class
Object creation Cannot be instantiated directly. Can normally be instantiated.
Inheritance A class can implement multiple interfaces. A class can extend only one class.
Instance fields Does not have ordinary instance fields. Can contain instance fields.
Constructors Cannot have constructors. Can have constructors.
Purpose Defines a contract or capability. Defines state and behavior of objects.

When Should You Use an Interface?

Interfaces are particularly useful when different classes need to provide the same type of behavior even though they may have completely different implementations.

Example:

Different payment methods can follow the same Payment contract:

  • UPI implements Payment.
  • CreditCard implements Payment.
  • NetBanking implements Payment.

The application can work with the common Payment interface without depending directly on a particular payment implementation. This helps create loosely coupled and easily extensible applications.

Benefits of Interfaces:
  • Provides a clear contract for implementing classes.
  • Supports abstraction and runtime polymorphism.
  • Allows a class to implement multiple interfaces.
  • Promotes loose coupling between components.
  • Makes applications easier to extend and maintain.
  • Allows different classes to provide different implementations of the same behavior.
Remember: A class uses extends when inheriting from another class and implements when following an interface contract.
Key idea: An interface defines what a class must provide, while the implementing class defines how that behavior is performed. Interfaces are especially useful when unrelated classes need to follow a common contract or when a class needs to implement multiple capabilities.

Exception Handling

Handle errors and unexpected situations without terminating the application abruptly.

An exception is an event that occurs during program execution and disrupts the normal flow of instructions. Exceptions can occur because of invalid input, incorrect calculations, unavailable files, invalid array indexes, database failures, and many other situations.

Exception handling allows a Java program to detect these exceptional situations and respond to them appropriately instead of allowing the application to terminate unexpectedly.

Key idea: Exception handling separates normal program logic from error-handling logic, making applications more reliable and easier to maintain.

Exception Hierarchy

Java provides a hierarchy of classes for representing errors and exceptions. The main root class is Throwable, which has two important branches: Error and Exception.


                    Throwable
                    ├── Error
                    │   ├── OutOfMemoryError
                    │   └── StackOverflowError
                    │
                    └── Exception
                        ├── IOException
                        ├── SQLException
                        └── RuntimeException
                            ├── NullPointerException
                            ├── ArithmeticException
                            └── ArrayIndexOutOfBoundsException

Error generally represents serious problems that applications normally should not try to recover from, while Exception represents conditions that an application can often handle.

try-catch

The try block contains code that may produce an exception. If an exception occurs, Java transfers control to a matching catch block.


                    try {

                      int result = 10 / 0;
                      System.out.println(result);

                      } catch (ArithmeticException e) {

                      System.out.println("Cannot divide by zero");

                    }
Output:
Cannot divide by zero

Without the try-catch, the ArithmeticException would propagate through the program and could terminate the current execution flow.

Multiple catch Blocks

A single try block can be followed by multiple catch blocks when different types of exceptions need to be handled differently.


                    try {

                      int[] numbers = {10, 20, 30};
                    
                      System.out.println(numbers[5]);

                    } catch (ArithmeticException e) {

                      System.out.println("Arithmetic error");

                    } catch (ArrayIndexOutOfBoundsException e) {

                      System.out.println("Invalid array index");

                    }

Java checks the catch blocks in order and executes the first matching handler.

Important: When using multiple catch blocks, a more specific exception should be handled before a more general exception such as Exception.

finally

The finally block contains code that should normally execute after the try and catch processing. It is commonly used for cleanup operations such as closing resources.


                    try {

                      System.out.println("Try block");

                    } catch (Exception e) {

                      System.out.println("Exception");

                    } finally {

                      System.out.println("Finally block");

                    }
Output:

                    Try block
                    Finally block

The finally block is especially useful when a resource needs to be released regardless of whether an operation succeeds or fails.

throw

The throw statement is used when a program needs to explicitly create and throw an exception. This is useful when a business rule or validation condition is violated.


                    static void checkAge(int age) {

                      if (age < 18) {
                          throw new IllegalArgumentException(
                              "Age must be 18 or above"
                          );
                      }
                    
                      System.out.println("Eligible");

                    }
                    
                    public static void main(String[] args) {

                      checkAge(16);

                    }

Here, the method explicitly throws an IllegalArgumentException when the supplied age is invalid.

throws

The throws keyword is used in a method declaration to indicate that the method may pass one or more exceptions to its caller. It is particularly important when working with checked exceptions.


                    import java.io.IOException;
              
                    class FileManager {

                      static void readFile() throws IOException {
                      
                          // File operation
                      }

                    }

In this example, readFile() does not handle the IOException itself. Instead, it declares that the exception may be passed to the method that calls it.

throw vs throws

throw throws
Used to explicitly throw an exception. Used to declare possible exceptions in a method signature.
Used inside the method body. Used in the method declaration.
Throws a specific exception object. Can declare one or more exception types.
throw new Exception() method() throws IOException

Checked vs Unchecked Exceptions

Java broadly categorizes exceptions into checked and unchecked exceptions based on whether the compiler requires them to be handled or declared.

Feature Checked Exception Unchecked Exception
Compiler checking Compiler requires handling or declaration. Compiler does not require explicit handling or declaration.
Base category Exceptions other than RuntimeException and its subclasses. RuntimeException and its subclasses.
Common cause External conditions such as file or database operations. Programming mistakes or invalid runtime operations.
Examples IOException, SQLException NullPointerException, ArithmeticException

Custom Exceptions

Java also allows developers to create their own exception classes when standard exceptions do not clearly represent a particular application rule or business condition.


                    class InsufficientBalanceException extends Exception {

                      InsufficientBalanceException(String message) {
                          super(message);
                      }

                    }
                    
                    class BankAccount {

                      void withdraw(double balance, double amount) throws InsufficientBalanceException {
                      
                          if (amount > balance) {
                              throw new InsufficientBalanceException(
                                  "Insufficient balance"
                              );
                          }
                      
                          System.out.println("Withdrawal successful");
                      }

                    }

A custom exception makes the program's error more meaningful and allows application-specific conditions to be handled separately.

try-catch-finally Flow

The general flow of exception handling can be understood as follows:

  1. Java starts executing the try block.
  2. If no exception occurs, the catch block is skipped.
  3. If an exception occurs, Java searches for a matching catch block.
  4. The matching catch block handles the exception.
  5. The finally block is then normally executed.
  6. Program execution continues after the exception-handling structure.

Exception Handling Best Practices

  • Catch specific exceptions instead of unnecessarily catching Exception.
  • Do not use exceptions as a replacement for normal program flow.
  • Provide meaningful error messages.
  • Do not silently ignore exceptions.
  • Use finally or try-with-resources when cleanup is required.
  • Create custom exceptions for meaningful application-specific conditions.
Benefits of Exception Handling:
  • Prevents unexpected application termination.
  • Separates error-handling logic from normal application logic.
  • Allows meaningful error messages to be provided to users.
  • Helps applications recover from expected exceptional conditions.
  • Makes debugging and maintenance easier.
Key idea: Use try-catch to handle exceptions, finally for cleanup, throw to explicitly raise an exception, and throws to declare that a method may pass an exception to its caller.

Collections Framework

Store, organize and process groups of objects using flexible collection classes.

The Java Collections Framework is a set of interfaces, classes, and utility methods used to store and manipulate groups of objects. It provides ready-to-use data structures such as ArrayList, HashSet, PriorityQueue, and HashMap.

Collections are generally more flexible than arrays because their size can grow or shrink dynamically, and they provide many built-in methods for searching, adding, removing, sorting, and processing elements.

Key idea: Choose a collection based on how the data needs to be stored and accessed, rather than using the same collection for every situation.

Collections Framework Hierarchy

The main collection interfaces represent different ways of organizing data. Map is part of the Collections Framework but does not extend the Collection interface because it stores key-value pairs rather than individual elements.


                    Iterable
                      |
                    Collection
                      |
                      +-- List
                      |    +-- ArrayList
                      |    +-- LinkedList
                      |
                      +-- Set
                      |    +-- HashSet
                      |    +-- LinkedHashSet
                      |    +-- TreeSet
                      |
                      +-- Queue
                            +-- PriorityQueue
                            +-- Deque
                                +-- ArrayDeque
                    
                    Map
                    |
                    +-- HashMap
                    +-- LinkedHashMap
                    +-- TreeMap

Important Collection Types

Type Characteristics Common Implementations
List Ordered elements, duplicates allowed, index-based access. ArrayList, LinkedList
Set Does not allow duplicate elements. HashSet, TreeSet
Queue Designed for processing elements according to queue rules. PriorityQueue, ArrayDeque
Map Stores data using key-value associations. HashMap, TreeMap

Generics in Collections

Java collections commonly use generics to specify the type of elements that can be stored. This provides compile-time type checking and reduces the need for explicit type casting.


                    ArrayList<String> names = new ArrayList<>();
                    
                    names.add("Arun");
                    names.add("Priya");
                    
                    // names.add(100);  // Compile-time error

Since the list is declared as ArrayList<String>, only String values can be added to it.

ArrayList

ArrayList is one of the most commonly used List implementations. It stores elements in an ordered sequence and allows duplicate values. It also provides fast positional access using an index.


                    import java.util.ArrayList;
                    
                    ArrayList names = new ArrayList<>();
                    
                    names.add("Arun");
                    names.add("Kumar");
                    names.add("Priya");
                    
                    System.out.println(names.get(1));
                    
                    for (String name : names) {
                      System.out.println(name);
                    }

Common methods include add(), get(), set(), remove(), contains(), and size().


                    names.set(1, "Rahul");
                    names.remove("Arun");
                    
                    System.out.println(names.size());
                    System.out.println(names.contains("Priya"));

LinkedList

LinkedList is another implementation of the List interface. It is useful when frequent insertions or removals are required at known positions or at the ends of the list.


                    import java.util.LinkedList;
                    
                    LinkedList names = new LinkedList<>();
                    
                    names.add("Arun");
                    names.add("Priya");
                    
                    names.addFirst("Kumar");
                    names.addLast("Rahul");
                    
                    System.out.println(names);

HashSet

A HashSet stores unique elements. If the same value is added more than once, the duplicate value is not stored. It does not provide index-based access.


                    import java.util.HashSet;
                    
                    HashSet numbers = new HashSet<>();
                    
                    numbers.add(10);
                    numbers.add(20);
                    numbers.add(10);
                    
                    System.out.println(numbers);
Note: HashSet does not guarantee a predictable iteration order. If insertion order needs to be maintained, consider LinkedHashSet.

TreeSet

TreeSet stores unique elements and maintains them in their natural sorted order, or according to a supplied comparator.


                    import java.util.TreeSet;
                    
                    TreeSet numbers = new TreeSet<>();
                    
                    numbers.add(30);
                    numbers.add(10);
                    numbers.add(20);
                    
                    System.out.println(numbers);
Output:
[10, 20, 30]

Queue

A Queue is designed for holding elements before they are processed. Different queue implementations can use different ordering rules. For example, PriorityQueue processes elements based on priority rather than simply following insertion order.


                    import java.util.PriorityQueue;
                    
                    PriorityQueue numbers = new PriorityQueue<>();
                    
                    numbers.add(30);
                    numbers.add(10);
                    numbers.add(20);
                    
                    System.out.println(numbers.poll());

The poll() method retrieves and removes the head of the queue. For a natural-order PriorityQueue<Integer>, the smallest value has the highest priority.

HashMap

A HashMap stores data as key-value pairs. Each key is unique, while multiple keys can have the same value.


                    import java.util.HashMap;
                    
                    HashMap students = new HashMap<>();
                    
                    students.put(101, "Arun");
                    students.put(102, "Priya");
                    students.put(103, "Kumar");
                    
                    System.out.println(students.get(101));

In this example, the student ID acts as the key and the student name acts as the value.


                    System.out.println(students.containsKey(102));
                    
                    students.remove(103);
                    
                    System.out.println(students.size());

Iterating Through a Map

A map can be traversed using its entrySet(), which provides access to both the key and value of each entry.


                    for (Map.Entry<Integer, String> entry : students.entrySet()) {

                      System.out.println(
                          entry.getKey() + " : " + entry.getValue()
                      );

                    }

Common Collection Methods

Method Purpose
add() Adds an element to a collection.
remove() Removes an element.
contains() Checks whether an element exists.
size() Returns the number of elements.
isEmpty() Checks whether the collection contains no elements.
clear() Removes all elements.

Choosing the Right Collection

The appropriate collection depends on how the application needs to store and access data.

Requirement Recommended Collection
Ordered elements with index-based access ArrayList
Frequent insertion/removal at list ends LinkedList or ArrayDeque
Unique elements without requiring sorted order HashSet
Unique elements in sorted order TreeSet
Priority-based processing PriorityQueue
Key-value relationships HashMap

Collections vs Arrays

Feature Array Collection
Size Fixed after creation Usually dynamic
Data types Can store primitives and objects Stores objects; wrapper types are used for primitives
Built-in operations Limited Many methods for manipulating data
Data structures Basic indexed structure List, Set, Queue and other structures
Flexibility Less flexible More flexible for dynamic data management
Benefits of the Collections Framework:
  • Provides ready-to-use data structures.
  • Reduces the need to implement common data structures manually.
  • Supports dynamic storage of objects.
  • Provides standard methods for adding, removing, searching and processing data.
  • Improves code reusability and maintainability.
  • Provides different implementations for different performance and ordering requirements.
Key idea: Use a List when order and duplicates matter, a Set when uniqueness matters, a Queue when elements need to be processed according to queue rules, and a Map when data is represented as key-value relationships.

File Handling in Java

Create, read, write, update and manage files using Java APIs.

File handling allows a Java application to store and retrieve data from files on a storage device. Unlike variables and objects that normally exist only while a program is running, file data can be preserved and used later.

Java provides several APIs for file operations. The traditional java.io package provides classes such as File, FileReader, FileWriter, and BufferedReader. The modern java.nio.file package provides Path and Files, which offer a convenient API for many common file operations.

Key idea: File handling is commonly used for configuration files, reports, logs, text data, application data, and importing or exporting information.

File Paths

A file path tells Java where a file or directory is located. A path can refer to a file in the current working directory or specify a complete location.


                    String relativePath = "data.txt";
                    
                    String absolutePath = "C:/Users/Student/Documents/data.txt";

Using relative paths can make applications easier to move between environments, while absolute paths identify a specific location on the system.

Using the File Class

The File class from java.io represents a file or directory path. It provides methods for checking whether a file exists, obtaining file information, creating directories, and deleting files.


                    import java.io.File;
                    
                    File file = new File("data.txt");
                    
                    if (file.exists()) {

                      System.out.println("File exists");
                      System.out.println("File name: " + file.getName());
                      System.out.println("File size: " + file.length());

                    } else {

                      System.out.println("File does not exist");

                    }

Creating a File

A file can be created using the createNewFile() method. The method returns true when a new file is created and false if the file already exists.


                    import java.io.File;
                    import java.io.IOException;
                    
                    File file = new File("data.txt");
                    
                    try {

                      if (file.createNewFile()) {
                          System.out.println("File created");
                      } else {
                          System.out.println("File already exists");
                      }

                    } catch (IOException e) {

                      System.out.println("Unable to create file");

                    }

Creating Directories

Java can also create directories using the mkdir() and mkdirs() methods. The mkdirs() method can create multiple levels of directories when necessary.


                    import java.io.File;
                    
                    File directory = new File("data/reports");
                    
                    if (directory.mkdirs()) {
                      System.out.println("Directories created");
                    }

Writing to a File

FileWriter can be used to write character data to a text file. The following example creates the file if it does not exist and writes content to it.


                    import java.io.FileWriter;
                    import java.io.IOException;
                    
                    public class Main {

                      public static void main(String[] args) {
                      
                          try (FileWriter writer =
                                  new FileWriter("data.txt")) {
                      
                              writer.write("Welcome to Java File Handling");
                              writer.write("\nLearning file operations");
                      
                          } catch (IOException e) {
                      
                              System.out.println("Unable to write file");
                          }
                      }

                    }

By default, FileWriter writes from the beginning of the file, which can replace existing content. To append new content instead, use the append mode.


                    try (FileWriter writer = new FileWriter("data.txt", true)) {

                      writer.write("\nNew line added");

                    
                    } catch (IOException e) {

                      System.out.println("Unable to write file");
                    
                    }

Reading a File

BufferedReader can efficiently read text from a file one line at a time. It is commonly used together with FileReader.


                    import java.io.BufferedReader;
                    import java.io.FileReader;
                    import java.io.IOException;
                    
                    public class Main {

                    public static void main(String[] args) {
                    
                        try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
                    
                            String line;
                    
                            while ((line = reader.readLine()) != null) {
                                System.out.println(line);
                            }
                    
                        } catch (IOException e) {
                    
                            System.out.println("Unable to read file");
                        }
                    }

                    }

The readLine() method returns one line at a time. When there are no more lines to read, it returns null, which ends the loop.

Deleting a File

A file can be deleted using the delete() method of the File class.


                    import java.io.File;
                    
                    File file = new File("data.txt");
                    
                    if (file.delete()) {
                      System.out.println("File deleted");
                    } else {
                      System.out.println("File could not be deleted");
                    }

Modern File Handling with Path and Files

Java's java.nio.file package provides a modern API for working with files. The Path interface represents a file or directory location, while the Files class provides operations for reading, writing, copying, moving, and deleting files.


                    import java.nio.file.Files;
                    import java.nio.file.Path;
                    import java.io.IOException;
                    
                    public class Main {

                      public static void main(String[] args) {
                      
                          Path path = Path.of("data.txt");
                      
                          try {
                      
                              Files.writeString(
                                  path,
                                  "Welcome to Java"
                              );
                      
                              String content = Files.readString(path);
                      
                              System.out.println(content);
                      
                          } catch (IOException e) {
                      
                              System.out.println("File operation failed");
                          }
                      }

                    }

The Files API provides convenient methods such as writeString() and readString() for common text file operations.

Common File Operations

Operation Common API Purpose
Check existence File.exists() / Files.exists() Checks whether a file or directory exists.
Create file File.createNewFile() Creates a new file.
Write FileWriter / Files.writeString() Writes data to a file.
Read BufferedReader / Files.readString() Reads data from a file.
Delete File.delete() / Files.delete() Removes a file or directory.

Try-With-Resources

File streams and readers use system resources that should be closed after use. Java provides try-with-resources to automatically close resources that implement AutoCloseable.


                    try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {

                      String line = reader.readLine();
                      
                      System.out.println(line);

                    } catch (IOException e) {

                      System.out.println("Unable to read file");

                    }

When execution leaves the try block, Java automatically closes the reader. This reduces the possibility of resource leaks and eliminates the need to manually close the resource in a finally block.

Character Streams vs Byte Streams

Java provides different stream types depending on the kind of data being processed.

Type Used For Examples
Character Streams Text and character data FileReader, FileWriter
Byte Streams Binary data such as images, audio and PDFs FileInputStream, FileOutputStream

Common File Handling Exceptions

File operations can fail for several reasons, such as a missing file, insufficient permissions, an invalid path, or an unavailable resource. Java commonly represents these problems using IOException and its subclasses.


                    try {

                      String content = Files.readString(
                          Path.of("data.txt")
                      );

                    } catch (IOException e) {

                      System.out.println(
                          "Unable to access the file"
                      );

                    }
Best practices:
  • Use try-with-resources when working with closeable streams and readers.
  • Handle IOException appropriately instead of ignoring it.
  • Check whether a file exists when the application requires it.
  • Prefer java.nio.file APIs for many modern file operations.
  • Use relative paths when possible to make applications easier to move between environments.
Common File Handling Tasks:
  • Create files and directories.
  • Read text from files.
  • Write and append content.
  • Copy, move and delete files.
  • Check file properties and existence.
  • Process text and binary data using appropriate streams.
Key idea: Java provides both traditional java.io classes and the modern java.nio.file API for file handling. For resource-based operations such as readers and writers, use try-with-resources so resources are automatically closed after use.