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.
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!");
}
}
Understanding the Program
-
public class Maindefines a class namedMain. -
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
;.
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
How Java Code Executes
- Developer writes Java source code in a
.javafile. - The Java compiler
javaccompiles the source code. - The compiler produces a
.classfile containing bytecode. - The JVM loads the bytecode.
- 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.
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.
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.
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.
java is commonly used to launch Java applications,
while javac is the Java compiler used to compile
.java source files into bytecode.
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 |
java -version and javac -version
already work from Command Prompt, you normally do not need to
manually modify PATH just to start learning Java.
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.
Install Java Extension Pack in VS Code
Java support in VS Code is provided through extensions.
- Open Visual Studio Code.
- Select the Extensions icon from the left sidebar.
- Search for Extension Pack for Java.
- Select the Java Extension Pack published by Microsoft.
- Click Install.
It provides Java language support, code completion, debugging, testing support, project management features, and other tools needed for Java development inside VS Code.
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
Hello, Java!
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.
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";
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);
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.
studentNametotalMarksemployeeSalarymaximumValue
student_nameStudentNamestudent name123student
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
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
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.
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);
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);
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);
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);
Decrement Operator
int count = 5;
count--;
System.out.println(count);
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);
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);
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);
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);
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);
Logical NOT
The ! operator reverses a boolean expression.
boolean active = true;
System.out.println(!active);
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.
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);
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);
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);
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);
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);
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);
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.
-
finalis 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
trueorfalse. - 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");
}
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");
}
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");
}
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");
}
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");
}
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");
}
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
- The initialization runs once.
- The condition is checked.
- If the condition is true, the loop body executes.
- The update expression executes.
- The condition is checked again.
for (int i = 1; i <= 5; i++) {
System.out.println(i);
}
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++;
}
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);
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...");
}
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);
}
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);
}
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);
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
-
ifexecutes code only when a condition is true. -
if-elseprovides two execution paths. -
else-ifis useful when multiple conditions must be evaluated. -
Nested
ifstatements allow one decision to depend on another. -
switchis useful for comparing one expression against multiple fixed cases. -
for,while, anddo-whileare used for repetition. -
A
whileloop may execute zero times, while ado-whileloop executes at least once. - Nested loops are useful for patterns, tables, and multidimensional data.
-
breakterminates the nearest loop or switch. -
continueskips the current iteration and proceeds to the next one. -
returnexits 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};
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 |
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 |
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]);
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]);
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);
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);
}
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");
}
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]);
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();
}
80 75 90
70 85 88
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));
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));
ArrayIndexOutOfBoundsException
Trying to access an index outside the valid range causes an
ArrayIndexOutOfBoundsException.
int[] numbers = {10, 20, 30};
System.out.println(numbers[3]);
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.lengthto determine the number of elements. -
Use a traditional
forloop when the index is required. -
Use an enhanced
forloop when only the values are required. - Two-dimensional arrays can represent row-and-column data.
-
Arraysprovides utility methods for sorting, copying, searching, and comparing arrays. -
Use collections such as
ArrayListwhen 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);
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);
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);
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.
StringBuilder.
Finding String Length
The length() method returns the number of characters in a
String.
String message = "Hello Java";
System.out.println(message.length());
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));
v
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));
}
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));
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);
== 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"));
Changing Letter Case
String text = "Java Programming";
System.out.println(text.toUpperCase());
System.out.println(text.toLowerCase());
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"));
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"));
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));
Java
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"));
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);
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);
Checking Empty Strings
The isEmpty() method returns true when the
String contains zero characters.
String username = "";
System.out.println(username.isEmpty());
Java also provides isBlank(), which is useful when a String
contains only whitespace characters.
String value = " ";
System.out.println(value.isBlank());
String Concatenation
Strings can be joined using the + operator.
String firstName = "Arun";
String lastName = "Kumar";
String fullName = firstName + " " + lastName;
System.out.println(fullName);
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:
0when 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);
}
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);
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);
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
-
Stringis 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()andindexOf()are frequently used for text processing. -
Use
StringBuilderwhen frequent String modifications are required. -
Use
StringBufferwhen 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.
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();
}
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");
}
Welcome Priya
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);
}
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);
}
}
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);
}
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);
}
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()ordisplayDetails(). - 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
privatewhen 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
voidmethod does not return a value. -
The
returnstatement 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.
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();
}
}
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
Classes & Objects
Learn how Java models entities using classes and objects.
Methods & Constructors
Define object behavior and initialize object state.
Encapsulation
Control how object data is accessed and modified.
Inheritance
Build relationships between related classes.
Polymorphism
Work with different implementations through a common type.
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.
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 studentdeclares a reference variable namedstudent. -
new Student()creates a new Student object.
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();
}
}
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);
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);
}
}
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);
}
}
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
newkeyword is commonly used to create objects. - Constructors are used to initialize objects.
-
The
thiskeyword 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.
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);
}
}
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);
}
}
0
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);
}
}
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;
}
}
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);
}
}
0
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.
-
thisrefers 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. |
private. The important concept is
controlling access to an object's internal state and
allowing the class to enforce rules when that state changes.
- 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.
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.
- 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.
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.
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.
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();
}
}
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.
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 |
- 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.
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.
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.
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.
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. |
- 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.
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.
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.
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().
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:
UPIimplementsPayment.CreditCardimplementsPayment.NetBankingimplementsPayment.
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.
- 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.
extends when inheriting from another class and
implements when following an interface contract.
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.
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");
}
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.
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");
}
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:
- Java starts executing the
tryblock. - If no exception occurs, the
catchblock is skipped. - If an exception occurs, Java searches for a matching
catchblock. - The matching
catchblock handles the exception. - The
finallyblock is then normally executed. - 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
finallyor try-with-resources when cleanup is required. - Create custom exceptions for meaningful application-specific conditions.
- 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.
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.
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);
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);
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 |
- 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.
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.
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"
);
}
- Use try-with-resources when working with closeable streams and readers.
- Handle
IOExceptionappropriately instead of ignoring it. - Check whether a file exists when the application requires it.
- Prefer
java.nio.fileAPIs for many modern file operations. - Use relative paths when possible to make applications easier to move between environments.
- 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.
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.