Java
Java
Features of Java
1. Object-Oriented
2. Platform-Independent
Java programs are compiled into bytecode, which can run on any device
equipped with a Java Virtual Machine (JVM).
This "Write Once, Run Anywhere" (WORA) capability is one of Java's most
significant advantages.
3. Robust
Type-checking features.
Java code is first compiled into bytecode and then interpreted by the JVM
at runtime.
6. Multi-Threaded
JAVA 1
Java can execute multiple threads simultaneously, enabling efficient
multitasking.
8. JShell
JAVA 2
Hello World in java
Java Development Kit (JDK): Download and install the JDK from the official
Oracle website.
IDE or Text Editor: You can use an IDE like IntelliJ IDEA, Eclipse, or a simple
text editor like VS Code.
public class HelloWorld { // Class definition (class name must match file nam
e)
public static void main(String[] args) { // Main method - entry point
System.out.println("Hello, World!");
}
}
public static void main(String[] args) : This is the main method, which serves as the
entry point of the program. The program starts executing from here.
class.
javac HelloWorld.java
java HelloWorld
Using an IDE:
Most IDEs have a built-in button to compile and run the program. Simply
click "Run" after writing the code.
Expected Output:
When you run the program, you should see:
Hello, World!
This is your first Java program! It demonstrates how Java code is structured, how
to define a class and the main method, and how to print output to the console.
What is JVM?
JVM is the part of Java that runs your programs.
It takes the bytecode (a special code Java creates after compilation) and
converts it into instructions your computer can understand.
2. Compile Code
The Java compiler ( javac ) converts your code into bytecode (a .class file).
3. Run Code
The JVM reads the bytecode and converts it into instructions your
operating system understands.
Since every OS has its own version of JVM, it knows how to work on that
system.
Variables in Java
A variable is a container that stores data values. Every variable in Java must be
declared with a specific data type.
Types of Variables:
1. Local Variables
Example:
2. Instance Variables
Example:
class Person {
String name; // Instance variable
}
3. Global Variables
Example:
Key Notes:
Global variables are part of the class and have a single copy in
memory.
They can be accessed anywhere within the class or even outside the
class (if public) using the class name.
class Person {
String name;
}
Rule: The data types must be compatible, and the conversion will not lose
data.
Examples:
int i = 7;
long l = i; // int to long (Widening)
float f = l; // long to float (Widening)
double d = f; // float to double (Widening)
Syntax:
Type Conversions 1
int i = (int) 7.5; // double to int (Explicit)
Type Conversions 2
Operators in Java
Operators in Java are special symbols or keywords used to perform operations on
variables and values. Java has several types of operators:
1. Arithmetic Operators
Arithmetic operators are used to perform basic mathematical operations like
addition, subtraction, multiplication, division, and modulus.
- Subtraction a-b
* Multiplication a*b
/ Division a/b
!= Not equal to a != b
3. Logical Operators
Logical operators are used to combine multiple conditions (boolean expressions).
These return true or false .
Operators in Java 1
Operator Description Example
&& Logical AND a > b && b > 0
4. Assignment Operators
Assignment operators are used to assign values to variables. There are several
variations of the assignment operator.
5. Bitwise Operators
Bitwise operators are used to perform operations on the binary representations of
integers. These operators work on bits and perform bit-by-bit operations.
` ` OR
^ XOR (exclusive OR) a^b
Example:
Operators in Java 2
int a = 5, b = 3;
int andResult = a & b; // 1 (0101 & 0011 = 0001)
int orResult = a | b; // 7 (0101 | 0011 = 0111)
int xorResult = a ^ b; // 6 (0101 ^ 0011 = 0110)
int notResult = ~a; // -6 (~0101 = 1010 in two's complement)
int leftShift = a << 1; // 10 (0101 << 1 = 1010)
int rightShift = a >> 1; // 2 (0101 >> 1 = 0010)
Operators in Java 3
Control Statements
Control statements are used to control the flow of execution in a Java program.
They allow you to make decisions, repeat actions, or jump between different parts
of your program. Java provides several types of control statements:
if Statement
The if statement is used to test a condition and execute a block of code if the
condition is true .
Syntax:
if (condition) {
// Code to be executed if condition is true
}
if-else Statement
The if-else statement is used when you have two possible outcomes based on a
condition.
Syntax:
if (condition) {
// Code to be executed if condition is true
} else {
// Code to be executed if condition is false
}
Control Statements 1
This statement allows you to test multiple conditions.
Syntax:
if (condition1) {
// Code if condition1 is true
} else if (condition2) {
// Code if condition2 is true
} else {
// Code if neither condition1 nor condition2 is true
}
switch Statement
The switch statement is used when you have multiple possible conditions for a
variable, and you want to choose between them.
Syntax:
switch (expression) {
case value1:
// Code to be executed if expression equals value1
break;
case value2:
// Code to be executed if expression equals value2
break;
default:
// Code to be executed if expression doesn't match any value
}
for Loop
The for loop is used when the number of iterations is known in advance.
Control Statements 2
Syntax:
Example:
while Loop
The while loop is used when the number of iterations is not known in advance, and
the loop continues until the condition is false .
Syntax:
while (condition) {
// Code to be executed
}
Example:
int i = 1;
while (i <= 5) {
System.out.println(i);
i++;
}
do-while Loop
The do-while loop is similar to the while loop, but it guarantees that the block of
code is executed at least once, even if the condition is false .
Syntax:
Control Statements 3
do {
// Code to be executed
} while (condition);
Example:
int i = 1;
do {
System.out.println(i);
i++;
} while (i <= 5);
break Statement
The break statement is used to exit from a loop or a switch statement prematurely.
Example:
continue Statement
The continue statement is used to skip the current iteration of a loop and proceed
with the next iteration.
Example:
Control Statements 4
for (int i = 1; i <= 5; i++) {
if (i == 3) {
continue; // Skip this iteration when i is 3
}
System.out.println(i);
}
return Statement
The return statement is used to exit from a method and optionally return a value.
Example:
Ternary Operator
The ternary operator is a shorthand for an if-else statement. It allows you to assign
a value to a variable based on a condition in a more compact form.
Syntax:
Control Statements 5
expression2: The value that will be returned if the condition is false .
How It Works:
If the condition is true , the operator returns expression1.
Example:
Output:
Control Statements 6
Methods in java
In Java, the terms function and method are often used interchangeably. A
method is a block of code that performs a specific task and is usually part of a
class. A function is essentially the same as a method in Java, but in other
programming languages, it may not be part of a class.
Access Modifier: Defines the visibility of the method (e.g., public , private ).
Return Type: The type of value the method will return (e.g., int , void ).
Parameters (optional): The input values the method will use to perform its
task.
Syntax:
Here:
Methods in java 1
int a, int b : Parameters (inputs) of the method.
2. Calling a Method
To call (or invoke) a method, you need to:
Use the object to call the method (if the method is not static).
If the method is static, you can call it directly using the class name.
Pass by Value:
Changes made to the parameter inside the method do not affect the original
value.
Pass by Reference:
Changes made to the parameter inside the method affect the original object.
Example: In Java, objects (e.g., arrays, custom classes) behave like pass by
reference.
Methods in java 2
(Note: Java technically uses pass by value for both primitives and objects, but for
objects, the "value" is the reference itself.)
Pass-by-Value in Java
Java always uses pass-by-value. This means that when you pass a variable to a
method, a copy of the variable's value is passed. This applies to both primitive
types and object references.
Primitive Types
For primitive types (e.g., int , char , double ), the value itself is copied. Any changes
made to the parameter inside the method do not affect the original variable.
Example:
int original = 5;
modifyValue(original); // original remains 5
System.out.println(original); // Output: 5
Here, original remains 5 because modifyValue only modifies the copy of original .
Object References
For objects, Java passes a copy of the reference (memory address) to the object.
This means:
However, since the copied reference still points to the same object, changes to
the object's state (e.g., modifying fields or array elements) will affect the
original object.
Example:
Methods in java 3
public void modifyArray(int[] nums) {
nums[0] = 10; // Changes the first element of the original array
}
Here, originalArray is modified because the method receives a copy of the reference
to the same array object.
Changes to the state of an object (e.g., modifying fields or array elements) are
visible outside the method.
Reassigning the reference inside the method does not affect the original
reference outside the method.
Important Question
1. In Java, when I pass a primitive type (e.g., int ) to a method and modify it
inside the method, the changes do not reflect on the original variable. For
example:
int original = 5;
modifyValue(original); // original remains 5
System.out.println(original); // Output: 5
Methods in java 4
Why does this happen, and how can I modify the value of the original variable?
Answer:
In Java, primitive types (like int , float , boolean , etc.) are passed by value, meaning
that a copy of the variable is passed to the method. Changes made to the variable
inside the method only affect the copy, not the original value.
To modify the original value, you can use one of the following approaches:
1. Return the New Value
Modify the method to return the updated value and reassign it to the original
variable:
int original = 5;
original = modifyValue(original); // Update the original variable
System.out.println(original); // Output: 10
Wrap the primitive value in an object so that changes to the object persist outside
the method:
Using AtomicInteger :
import java.util.concurrent.atomic.AtomicInteger;
Methods in java 5
modifyValue(original); // Modify the wrapper's value
System.out.println(original.get()); // Output: 10
class Wrapper {
int value;
Wrapper(int value) {
this.value = value;
}
}
3. Use an Array
Since arrays in Java are passed by reference, modifying an array element inside
the method will affect the original array:
Methods in java 6
You can use a mutable data structure, such as a List , to hold the value and modify
it:
import java.util.ArrayList;
import java.util.List;
Methods in java 7
Memory Allocation in Java
In Java, memory is allocated to variables and objects in two main areas:
1. Stack Memory
2. Heap Memory
1. Stack Memory
Definition: Stack memory is used for the execution of threads and stores
primitive types (like int , char , boolean , etc.) and references to objects (not the
objects themselves).
Characteristics:
LIFO (Last In First Out): Stack works on the principle of Last In First Out.
When a method is called, a block (frame) is pushed onto the stack, and
when the method finishes execution, that block is popped off.
How it works:
When the method finishes execution, the stack frame is removed, and the
space is freed.
Example:
2. Heap Memory
Characteristics:
How it works:
References to these objects are stored in the stack, and the actual object
data is stored in the heap.
In this example:
The garbage collector checks for objects that are no longer referenced
and frees up memory from the heap.
class Person {
String name;
int age;
void displayInfo() {
System.out.println("Name: " + name + ", Age: " + age);
}
}
Memory Breakdown:
The reference person , which holds the memory address of the Person
object.
Heap:
The Person object containing name ("John") and age (25) is allocated in the
heap.
This diagram shows the relationship between the stack and heap:
The heap contains the actual object ( Person ) that the reference points to.
Important Questions
1. What is stored in the stack memory?
Arrays.
An object is created using the new keyword and stored in the heap
memory.
7. Can you control the size of stack and heap memory in Java?
Stack Memory: Use Xss to set the stack size (e.g., Xss1m for 1 MB).
Heap Memory: Use Xms to set the initial heap size and Xmx to set the
maximum heap size (e.g., Xms256m -Xmx1024m ).
The new keyword allocates memory in the heap for the object and returns
a reference to that memory location. The reference is stored in the stack
memory.
10. Can you explain the concept of memory fragmentation in the heap?
1. Shallow Copy
Definition: A shallow copy creates a new object but does not duplicate the
internal objects (like fields, arrays, or collections). Instead, the new object and
the original share references to the internal objects.
Memory: Only the top-level object is new; the internal objects remain shared
between the original and the copy.
Impact: Changes made to shared internal objects in the copy will reflect in the
original and vice versa.
import java.util.ArrayList;
import java.util.List;
class Connection {
private String url;
Connection(String url) {
this.url = url;
}
@Override
public String toString() {
return "Connection{url='" + url + "'}";
}
}
ConnectionPool(List<Connection> connections) {
this.connections = connections; // Shallow copy
}
@Override
public String toString() {
return "ConnectionPool{connections=" + connections + "}";
}
}
// Output
System.out.println("Original Connections: " + connections);
System.out.println("Connection Pool: " + pool);
}
}
3. When a new object is added to the connections list, it also affects the
Connection
2. Deep Copy
Definition: A deep copy creates a new object and also recursively duplicates
all internal objects. Both the top-level object and the internal objects are
independent of the original.
Memory: The new object and all its internal objects are stored in separate
memory locations.
Impact: Changes made to the copy do not affect the original object and vice
versa.
import java.util.ArrayList;
import java.util.List;
class Player {
String name;
Player(String name) {
@Override
public String toString() {
return "Player{name='" + name + "'}";
}
}
class GameState {
int level;
List<Player> players;
@Override
public String toString() {
return "GameState{level=" + level + ", players=" + players + "}";
}
}
// Output
System.out.println("Original: " + original); // Players remain unchanged
System.out.println("Deep Copy: " + deepCopy); // Modified copy
}
}
2. A GameState object ( original ) is created, containing a new list where new Player
Memory Usage Less memory (shared references). More memory (separate objects).
Changes Affects the original object. Does not affect the original object.
When to Use
Shallow Copy:
Deep Copy:
1. Arrays:
We can use arrays to store and access multiple values of the same data
type under one identifier.
Data Structures 1
Arrays make it easier to perform operations on an entire dataset.
2. Stack:
Stack follows the principle of LIFO (Last in First out) i.e. element which is
inserted at last will be removed first.
3. Queue:
Queue follows the principle of FIFO (First in First out) i.e. element which is
inserted first will be removed first.
Data Structures 2
4. Linked List:
A linked list is a linear data structure, in which the elements are not stored
at contiguous memory locations.
The elements in a linked list are linked using pointers as shown in the
below image:
5. Binary Tree: A binary tree is a tree data structure in which each parent node
can have at most two children. Each node of a binary tree consists of three
items:
data item
Data Structures 3
6. Graph:
The nodes are sometimes also referred to as vertices and the edges are
lines or arcs that connect any two nodes in the graph.
More formally a Graph can be defined as, A Graph consists of a finite set
of vertices(or nodes) and a set of Edges that connect a pair of nodes.
Data Structures 4
Arrays
An array is a collection of similar types of data stored in contiguous memory
locations. It allows us to store multiple values under one name and access them
using their index.
2. Index-based Access: You can easily access any element using its index.(0
based indexing)
3. Static Size: Arrays have a fixed size, which helps in memory allocation and
management.
To create an array:
Syntax:
Example:
Arrays 1
int[] numbers = new int[5]; // Creates an array of size 5
numbers[0] = 10; // Initialize the first element
numbers[1] = 20;
numbers[2] = 30;
System.out.println("First Element: " + numbers[0]);
System.out.println("Array Length: " + numbers.length);
}
}
2. Default Initialization:
Numeric types: 0
Boolean: false
Objects: null
Multi-Dimensional Arrays
Arrays 2
dataType[][] arrayName = new dataType[rows][columns];
Example:
Jagged Arrays
A jagged array is an array of arrays where each sub-array can have a different
length.
Syntax:
Example:
// Initialize elements
Arrays 3
jaggedArray[0][0] = 10;
jaggedArray[0][1] = 20;
jaggedArray[1][0] = 30;
Operations on Arrays
System.out.println(numbers.length);
import java.util.Arrays;
Arrays 4
import java.util.Arrays;
Limitations of Arrays
Important Questions
1. What Happens if You Try to Access an Array Index Out of Bounds?
No, the size of an array is fixed after creation. To resize an array, you need
to create a new array and copy the elements from the old array to the new
one.
Arrays 5
Object Array: Stores objects (e.g., String , custom objects).
System.arraycopy() :
Example:
Arrays.copyOf() :
Creates a new array and copies elements from the source array.
Example:
Arrays.sort() :
Arrays 6
Collections.sort() :
The Arrays class provides utility methods for working with arrays, such as:
Sorting ( Arrays.sort() )
Searching ( Arrays.binarySearch() )
Copying ( Arrays.copyOf() )
Comparing ( Arrays.equals() )
Filling ( Arrays.fill() )
8. When you execute System.out.println(arr); in Java, where arr is an array, the output
will be something like this:
[I@1b6d3586
1. [I :
The I indicates that the array is of type int (the letter corresponds to
the internal JVM representation of the data type).
2. @ :
The @ symbol separates the type information from the hash code.
3. 1b6d3586 :
Arrays 7
In Java, arrays are objects. When you pass an object to System.out.println() ,
the toString() method of that object is called.
ClassName@HashCode
Arrays 8
Strings
A String in Java is a sequence of characters. Strings are widely used in Java
programming and are immutable, meaning they cannot be changed after creation.
2. Immutability: Ensures security and thread safety, making it ideal for tasks like
password handling.
3. Rich Library Support: Java provides the java.lang.String class with numerous
methods for string manipulation.
4. Memory Efficiency: String literals are stored in the String pool, avoiding
unnecessary memory allocation
String s = "Hello";
Strings 1
3. Using Character Arrays
You can create a string by passing a character array to the String constructor.
Example:
5. Using String.valueOf()
1. String Pool: The String Pool is a special area of memory in the Java Heap.
Specifically, it resides in the Heap Memory section known as the Method
Area (or Metaspace in Java 8 and later).
2. Immutability:
In Java, String is not just a type but a class. Its immutability plays a significant role
in making it efficient and secure.
Strings 2
System.out.println(name);
1. Initially, the string name points to "Abhiram" in the String Pool (e.g., memory
location 101 ).
2. When concatenation ( "Sai" + name ) occurs, Java does not modify the original
string. Instead:
A new string "Sai Abhiram" is created in the String Pool (e.g., memory
location 105 ).
The reference variable name is updated to point to the new string ( 105 ).
3. The old string "Abhiram" at memory location 101 remains in the pool and may
eventually be cleaned up by the garbage collector if not referenced.
Thus, the original string is never modified, and a new string is always created for
changes. This behavior demonstrates immutability.
String s1 = "Abhiram";
String s2 = "Abhiram";
Strings 3
Key Takeaways
Memory Efficiency: String literals with the same value share the same location
in the String Pool.
String s1 = "Hello";
String s2 = "World";
String result = s1 + " " + s2; // Outputs: Hello World
String s = "Java";
System.out.println(s.length()); // Outputs: 4
Strings 4
String s = "Programming";
System.out.println(s.substring(0, 6)); // Outputs: Progra
4. Comparison
String s1 = "Java";
String s2 = "Java";
System.out.println(s1.equals(s2)); // Outputs: true
String s1 = "Java";
String s2 = "java";
System.out.println(s1.equalsIgnoreCase(s2)); // Outputs: true
String s1 = "Apple";
String s2 = "Banana";
System.out.println(s1.compareTo(s2)); // Outputs: -1 (negative if s1 < s2)
5. Search
a. Index Of: Finds the first occurrence of a character or substring.
String s = "Programming";
System.out.println(s.lastIndexOf('g')); // Outputs: 10
Strings 5
6. Replace: Replaces characters or substrings.
String s = "apple";
System.out.println(s.replace('a', 'A')); // Outputs: Apple
String s = "hello";
System.out.println(s.toUpperCase()); // Outputs: HELLO
String s = "HELLO";
System.out.println(s.toLowerCase()); // Outputs: hello
12. Split: Splits the string into an array of substrings based on a delimiter.
Strings 6
String s = "Java,Python,C++";
String[] languages = s.split(",");
for (String lang : languages) {
System.out.println(lang);
}
// Outputs:
// Java
// Python
// C++
String s = "Hello";
System.out.println(s.charAt(1)); // Outputs: e
String s = "";
System.out.println(s.isEmpty()); // Outputs: true
Strings 7
int num = 42;
String s = String.valueOf(num);
System.out.println(s); // Outputs: 42
String s = "12345";
System.out.println(s.matches("\\d+")); // Outputs: true (only digits)
Returns:
Example:
Important Questions
1. What is the Difference Between == and .equals() for Strings?
== :
.equals() :
Strings 8
Returns true if the content of the strings is the same.
Example:
2. Difference Between String str = "Hello" and String str = new String("Hello") ?
If the string already exists in the pool, the existing reference is reused.
Creates a new string object in the heap memory, even if the same
string exists in the pool.
This creates a new reference, which is different from the one in the
pool.
and
StringBuilder StringBuffer are mutable and designed for efficient string
manipulation.
Example:
Strings 9
String str = String.format("Name: %s, Age: %d", "Alice", 25);
System.out.println(str); // Output: "Name: Alice, Age: 25"
Strings 10
String vs String Builder vs String
Buffer
In Java, both StringBuilder and StringBuffer are classes used to create mutable
(modifiable) strings. Unlike the immutable String class, these allow for efficient
string manipulation, especially when performing operations like appending,
inserting, or deleting characters repeatedly.
StringBuilder
Features:
1. Mutable class, meaning you can modify the object without creating a new one.
StringBuffer
Features:
Common Methods:
// Append
sb.append(" World");
System.out.println(sb); // Outputs: Hello World
// Insert
sb.insert(6, "Java ");
System.out.println(sb); // Outputs: Hello Java World
// Replace
sb.replace(6, 10, "Beautiful");
System.out.println(sb); // Outputs: Hello Beautiful World
// Delete
// Reverse
sb.reverse();
System.out.println(sb); // Outputs: dlroW olleH
}
}
Important Questions
1. Why is StringBuilder Faster than StringBuffer ?
Immutability of String means that once a String is created, its value cannot
be changed. You cannot modify the characters in a string. If you want to
You can't change the string once it is created. For example, when you
create the string "Hello" , it will stay "Hello" forever. If you try to change it,
you actually create a new string, not modify the original one.
Another thread can also safely use the same string, because no thread
can change "Hello" . If you try to modify it, you will get a new string, and the
old one is untouched.
t1.start();
t2.start();
// Thread 1
Thread t1 = new Thread(() -> {
buffer.append("Thread 1 ");
});
// Thread 2
Thread t2 = new Thread(() -> {
buffer.append("Thread 2 ");
});
t1.start();
t2.start();
In this case, StringBuffer ensures that the threads safely append their data
without interference, which is crucial when multiple threads are involved.
Types of Collections
There are two main categories of collections in Java:
Collection Interface: The root interface for all collection classes, such as List,
Set, Queue.
Map Interface: Used to store key-value pairs (like a dictionary or hash map).
Summary
Each collection type has its specific use cases based on whether you need order,
uniqueness, or key-value pairing. The Collections Framework allows you to easily
work with these data structures in a consistent and efficient manner.
Collection Frameworks 1
Comparison of ArrayList, LinkedList, Vector, and
Stack
Synchronized
Doubly-linked Synchronized
Implementation Resizable array resizable array
list resizable array
(LIFO)
Not Not
Synchronization Synchronized Synchronized
synchronized synchronized
Collection Frameworks 2
Thread Safety No No Yes Yes
Iterating Over
Fast Moderate Fast Fast
List
Frequent Frequent
access, modifications, Multi-threaded
Usage Scenario LIFO operations
infrequent infrequent environment
modifications access
List Methods
Collection Frameworks 3
for (int ele : array) { list.add(ele); }
9. for-each loop: Iterates over the list using an enhanced for loop.
10. for loop: Iterates over the list using a traditional for loop.
11. toArray(): Converts the list to an array (but doesn't store the result).
list.toArray();
Thread Safety No No No
Slow compared to
Iterating Over
Fast Moderate HashSet and
Set
LinkedHashSet
Collection Frameworks 4
Allows one null Allows one null Does not allow null
Null Elements
element element elements
new LinkedHashSet<>
Example new HashSet<>() new TreeSet<>()
()
Set Methods
7. for-each loop: Iterates over the set using an enhanced for loop.
Collection Frameworks 5
Access Time O(1) O(1) O(log n)
Thread Safety No No No
Iterating Over
Fast Moderate Moderate to slow
Map
Allows one null key Allows one null key Does not allow null
Null
and multiple null and multiple null keys, allows null
Keys/Values
values values values
Map Methods
2. putIfAbsent(): Adds a key-value pair only if the key does not exist.
map.putIfAbsent(key, value);
Collection Frameworks 6
map.isEmpty();
9. for-each loop: Iterates over the map using an enhanced for loop.
Collection Frameworks 7
Comparator and Comparable
1. Comparator:
It is useful when you want to sort objects in different ways (not necessarily
the natural order defined by Comparable ).
Example:
@Override
public int compare(Student s1, Student s2) {
if (s1.age == s2.age) {
return s1.name.compareTo(s2.name);
}
return Integer.compare(s1.age, s2.age);
}
You can also use anonymous Comparator objects or lambda expressions for
different sorting logic, like sorting by weight in the examples.
2. Comparable:
It is used when you want to define the default sorting order for the objects
of a class.
Example:
@Override
public int compareTo(Student other) {
if (this.age == other.age) {
return this.name.compareTo(other.name);
}
return this.age - other.age;
}
The above code sorts students first by age and then by name if ages are
equal.
Implemented The class whose objects need to An external class that compares
by be compared. objects.
Comparator Implementation:
import java.util.*;
class Student {
String name;
int age;
int weight;
@Override
public String toString() {
return "Student {" +
"name='" + name + '\'' +
", age=" + age +
", weight=" + weight +
'}';
}
}
Comparable Implementation:
@Override
public String toString() {
return "Student {" +
"name='" + name + '\'' +
", age=" + age +
", weight=" + weight +
'}';
}
@Override
public int compareTo(Student other) {
if (this.age == other.age) {
return this.name.compareTo(other.name);
}
return this.age - other.age;
}
}
They allow operations like filtering, sorting, mapping, and reducing data in a
more readable and efficient manner.
Advantages of Streams:
2. Readability: They avoid the need for boilerplate code (like for-loops or
iterators) and are easier to understand.
Streams 1
Key Operations on Streams:
7. Aggregating ( average() , sum() , etc.): Calculates values like the average or sum
of elements.
This stream filters students who are 18 or younger and have a weight of 50
or less. Then, it increases their weight by 5 and prints it.
students.stream()
.filter(s -> s.age <= 18 && s.weight <= 50)
.sorted()
.distinct()
.map(s -> s.weight + 5)
.forEach(s -> System.out.println(s));
2. Collecting to a List:
This stream filters and sorts students and collects them into a new list.
students.stream()
.filter(s -> s.age <= 18 && s.weight <= 50)
.sorted()
.map(s -> s.weight + 5)
.collect(Collectors.toList());
Streams 2
3. Finding Max/Min:
students.stream()
.max(Comparator.comparing(s -> s.age))
.ifPresent(s -> System.out.println(s));
4. Aggregating Data:
students.stream()
.mapToInt(s -> s.age)
.average()
.orElse(0);
5. Group By:
You can group elements based on a condition. Here, students are grouped
by age greater than 17.
students.stream()
.collect(Collectors.groupingBy(student -> student.age > 17))
.forEach((age, studentList) -> {
System.out.println("Age: " + age);
studentList.forEach(System.out::println);
});
Streams 3
Exceptions
An exception is an unexpected event that occurs during program execution, which
can affect the program's flow and may cause it to terminate abnormally.
Device failure
Code errors
Errors: Represent serious problems (e.g., JVM running out of memory or stack
overflow). Errors are usually not recoverable by the program.
Exceptions 1
Types of Exceptions
Examples:
Examples:
Exceptions 2
public class ExceptionWithoutHandling {
(division by zero).
3. The exception causes the program to stop, and the remaining lines (such as
printing the result) are never executed.
Start
Exception in thread "main" java.lang.ArithmeticException: / by zero
Errors: Prevent the program from even compiling. The bytecode file is not
generated.
Exceptions 3
Exceptions: Allow the program to compile, but can cause the program to stop
during execution unless handled.
Exceptions 4
Exception Handling
Java exceptions, which occur when something goes wrong during the execution
of a program, causing it to terminate abnormally. To ensure that the program
doesn’t crash abruptly, we use exception handling mechanisms.
It helps maintain smooth execution and prevents the program from terminating
unexpectedly.
By handling exceptions, we can give clear error messages, log issues, or even
recover from certain types of errors.
1. try...catch Block
The try...catch block is the most commonly used method for handling exceptions in
Java. It allows you to try running a block of code that may throw an exception,
and if an exception occurs, you can catch it and handle it properly.
try {
// code that might generate an exception
}
catch(ExceptionType e) {
// handle the exception
}
The try block contains the code that might throw an exception.
Example:
Exception Handling 1
class Main {
public static void main(String[] args) {
try {
// code that generates exception
int divideByZero = 5 / 0;
}
catch (ArithmeticException e) {
// handling the exception
System.out.println("Exception: " + e.getMessage());
}
}
}
Output:
In this example:
The catch block catches this exception and prints the message: "Error: / by
zero".
2. finally Block
The finally block is used to ensure that specific code gets executed regardless of
whether an exception occurs or not. It is commonly used for cleanup tasks, like
closing files, releasing resources, etc.
try {
// code
}
catch (ExceptionType e) {
// handle exception
Exception Handling 2
}
finally {
// always execute code (cleanup tasks)
}
Example:
class Main {
public static void main(String[] args) {
try {
int divideByZero = 5 / 0;
}
catch (ArithmeticException e) {
System.out.println("Caught Exception: " + e.getMessage());
}
finally {
System.out.println("This is the finally block.");
}
}
}
Output:
Here, the finally block ensures that the message "This is the finally block." is always
printed, even if an exception is thrown.
3. throw Keyword
Exception Handling 3
The throw keyword is used when we want to manually throw an exception in our
code. This is useful when something goes wrong, and we want to stop the normal
flow of the program and signal an error.
When to Use throw :
If something in the code doesn't follow the rules, we can use throw to generate
an exception (error).
Let's say we have a method that checks if someone's age is below 18. If the age is
under 18, we want to throw an exception saying "Underage."
class Main {
public static void checkAge(int age) {
if (age < 18) {
// Throw an exception if age is less than 18
throw new IllegalArgumentException("Underage! You must be 18 or olde
r.");
} else {
System.out.println("You are allowed.");
}
}
Output:
Exception Handling 4
Underage! You must be 18 or older.
In this example:
The catch block catches this exception and prints the message to the user.
4. throws Keyword
The throws keyword is used in a method's declaration to tell the calling method
that this method might throw an exception. So, the calling method needs to be
prepared to handle that exception.
The calling method can then decide how to handle the exception.
Let's say we have a method that reads a file, and reading a file might cause an
exception (like the file not existing). We use throws to declare this possibility.
import java.io.*;
class Main {
// This method declares that it might throw an IOException (if the file doesn't
exist)
public static void readFile(String filename) throws IOException {
FileInputStream file = new FileInputStream(filename);
System.out.println("File opened successfully!");
}
Exception Handling 5
found)
}
}
}
Output:
In this example:
The method readFile() declares with throws IOException that it might throw an
IOException (this can happen if the file is not found).
In the main() method, we call readFile() . Since readFile() might throw an exception,
we have to handle it with a try...catch block.
If the file doesn't exist, it will throw a FileNotFoundException , and the catch block will
handle it.
In Java, we can create our own exceptions (custom exceptions) by extending the
built-in exception classes. This allows us to create meaningful error messages
that are specific to our application's needs.
2. Add Constructors:
Exception Handling 6
We can create custom constructors to pass specific messages or cause
information when the exception is thrown.
Once the custom exception is created, we can use throw to raise it in the
code.
Let's create a custom exception that handles a situation where a user's age is
invalid (e.g., less than 0 or greater than 150).
Here, InvalidAgeException is our custom exception class, which extends the Exception
class.
class Main {
public static void validateAge(int age) throws InvalidAgeException {
// If the age is invalid (less than 0 or greater than 150), throw custom exce
ption
if (age < 0 || age > 150) {
throw new InvalidAgeException("Invalid age: " + age + ". Age must be
between 0 and 150.");
} else {
Exception Handling 7
System.out.println("Age is valid: " + age);
}
}
try {
validateAge(25); // Trying with a valid age
} catch (InvalidAgeException e) {
System.out.println("Caught an exception: " + e.getMessage());
}
}
}
Output:
Caught an exception: Invalid age: 200. Age must be between 0 and 150.
Age is valid: 25
Explanation:
The validateAge() method checks if the age is valid. If the age is less than 0 or
greater than 150, it throws the custom InvalidAgeException with a relevant
message.
The catch block catches the custom exception and prints the exception
message.
Exception Handling 8