๐Ÿ“˜ Dept. of Computer Science, Vidyasagar University

Java Programming Notes

(Agile Software Development)

โ˜•

Java Features & JVM

01 ยท Features ยท Bytecode ยท JVM Architecture
โ–ผ
Key Features of Java

๐Ÿ”„ Platform Independent

Java code is compiled into bytecode which runs on any JVM, regardless of the underlying OS โ€” "Write Once, Run Anywhere."

๐Ÿ”’ Object-Oriented

Everything in Java revolves around classes and objects. Supports encapsulation, inheritance, polymorphism, and abstraction.

๐Ÿ›ก๏ธ Secure & Robust

  • No explicit pointers
  • Strong type checking
  • Exception handling
  • Automatic garbage collection

๐ŸŒ Distributed & Multithreaded

Built-in support for network programming (TCP/IP) and concurrent programming with multiple threads of execution.

JVM Architecture
โ„น๏ธ
JVM (Java Virtual Machine) is an abstract machine that provides a runtime environment to execute Java bytecode. It is platform-specific but the bytecode it runs is platform-independent.
Component Role
Class Loader Loads .class files into memory; performs loading, linking, and initialization
Method Area Stores class metadata, static variables, method bytecode
Heap Runtime data area where objects are allocated
Stack Each thread has its own stack storing frames (local vars, operand stack)
Execution Engine Executes bytecode using Interpreter + JIT Compiler
Garbage Collector Automatically reclaims unused heap memory
Java Compilation & Bytecode Flow
// Java Source Code (.java)
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

// Compilation: javac HelloWorld.java โ†’ HelloWorld.class (Bytecode)
// Execution:   java HelloWorld  โ†’ JVM interprets/compiles bytecode
      Java
๐Ÿ’ก
JIT (Just-In-Time) Compiler converts frequently used bytecode into native machine code at runtime, dramatically improving performance beyond pure interpretation.
๐Ÿ“ฆ

Class & Object Fundamentals

02 ยท Objects ยท References ยท Garbage Collection
โ–ผ
Class vs Object

๐Ÿ“‹ Class

A blueprint/template that defines attributes (fields) and behaviors (methods). It is a logical entity โ€” no memory allocated at class declaration.

๐Ÿ”ท Object

An instance of a class. Memory is allocated on the heap when an object is created using the new keyword.

class Car {
    // Fields (attributes)
    String model;
    int speed;

    // Method (behavior)
    void accelerate() {
        speed += 10;
        System.out.println("Speed: " + speed);
    }
}

// Object creation
Car myCar = new Car();   // myCar is a reference variable
myCar.model = "Tesla";
myCar.accelerate();

Car anotherRef = myCar;   // both point to same object!Java
Object References
โš ๏ธ
In Java, variables of class types hold references (memory addresses), not the actual objects. Primitive types (int, char, double) hold actual values.
Garbage Collection

โ™ป๏ธ How GC Works

  • JVM automatically reclaims heap memory
  • Objects with no references are eligible
  • Cannot force GC โ€” only System.gc() suggests it
  • GC runs in a low-priority daemon thread

๐Ÿ” GC Algorithms

  • Mark & Sweep โ€” mark live, sweep dead
  • Generational GC โ€” Young/Old/PermGen
  • G1 GC โ€” default in Java 9+
  • ZGC / Shenandoah โ€” low-latency
Car c = new Car();  // Object created on heap
c = null;             // Object now eligible for GC

// Requesting GC (not guaranteed)
System.gc();
Runtime.getRuntime().gc();Java
๐Ÿ—๏ธ

Constructors & Initialization Code Blocks

03 ยท Constructors ยท Instance Initializers ยท Static Blocks
โ–ผ
Constructors

A constructor is a special method called when an object is created. It has the same name as the class and no return type.

Type Description Example
Default Constructor No-arg, auto-provided if none defined Car()
Parameterized Constructor Accepts arguments to initialize fields Car(String m, int s)
Copy Constructor Creates new object from existing one Car(Car c)
class Student {
    String name;
    int age;

    // Static initializer block โ€” runs once when class is loaded
    static {
        System.out.println("Class loaded!");
    }

    // Instance initializer block โ€” runs before every constructor
    {
        age = 18;  // default age
    }

    // Default constructor
    Student() { name = "Unknown"; }

    // Parameterized constructor
    Student(String n, int a) {
        name = n;
        age = a;
    }

    // Constructor chaining with this()
    Student(String n) {
        this(n, 18);  // calls parameterized constructor
    }
}Java
Initialization Order
๐Ÿ“‹
Execution order: 1. Static blocks/fields (class loading, once) โ†’ 2. Instance initializer blocks (each instantiation) โ†’ 3. Constructor body
๐Ÿ”

Access Control & Modifiers

04 ยท Access Specifiers ยท Non-Access Modifiers
โ–ผ
Access Modifiers
Modifier Same Class Same Package Subclass Other Package
public โœ… โœ… โœ… โœ…
protected โœ… โœ… โœ… โŒ
default โœ… โœ… โŒ โŒ
private โœ… โŒ โŒ โŒ
Non-Access Modifiers

๐Ÿ”’ final

  • Variable: constant, can't reassign
  • Method: can't be overridden
  • Class: can't be extended (e.g., String)

๐Ÿ“Œ static

  • Belongs to class, not instance
  • Shared across all objects
  • Called via class name
  • Cannot access instance members directly

๐Ÿ”„ abstract

  • Class: cannot be instantiated
  • Method: no body, must be overridden
  • Requires at least one abstract method to declare class abstract

๐Ÿ” synchronized / volatile

  • synchronized: thread-safe method/block
  • volatile: variable always read from main memory
  • transient: skip during serialization
final class Constants {
    public static final double PI = 3.14159;  // constant
    private transient int tempData;           // not serialized
    private volatile boolean flag = true;     // thread-visible
}Java
๐Ÿช†

Nested, Inner & Anonymous Classes

05 ยท Inner Class ยท Static Nested ยท Anonymous ยท Local
โ–ผ
Type Access to Outer Use Case
Static Nested Static members only Grouping classes logically
Inner (Non-static) All members incl. private Accessing outer class state
Local Class final/effectively final vars Defined inside a method
Anonymous final/effectively final vars One-off interface implementation
class Outer {
    private int x = 10;

    // Inner class
    class Inner {
        void show() { System.out.println(x); } // accesses outer x
    }

    // Static nested class
    static class Nested {
        void display() { System.out.println("Static nested"); }
    }

    void method() {
        // Local class
        class Local { void run() {} }

        // Anonymous class implementing Runnable
        Runnable r = new Runnable() {
            public void run() { System.out.println("Running!"); }
        };
        r.run();
    }
}

// Creating inner class instance (requires outer instance)
Outer o = new Outer();
Outer.Inner i = o.new Inner();
Outer.Nested n = new Outer.Nested();  // no outer instance neededJava
๐Ÿงฉ

Abstract Classes & Interfaces

06 ยท Abstract ยท Interface ยท Default Methods ยท Functional Interface
โ–ผ

๐Ÿ”ท Abstract Class

  • Can have abstract + concrete methods
  • Can have constructors & fields
  • Single inheritance only
  • Use when classes share implementation

๐Ÿ”ถ Interface

  • All methods implicitly public abstract
  • Fields are public static final
  • Multiple implementation (Java 8+: default/static methods)
  • Use for defining contracts/capabilities
// Abstract class
abstract class Shape {
    String color;
    Shape(String c) { color = c; }

    abstract double area();          // must override
    void describe() {                 // concrete method
        System.out.println("Color: " + color);
    }
}

// Interface with default method (Java 8+)
interface Drawable {
    void draw();                      // abstract
    default void render() {          // default (Java 8+)
        System.out.println("Rendering...");
    }
    static void info() {              // static method
        System.out.println("Drawable interface");
    }
}

class Circle extends Shape implements Drawable {
    double radius;
    Circle(String c, double r) { super(c); radius = r; }
    public double area() { return Math.PI * radius * radius; }
    public void draw() { System.out.println("Drawing circle"); }
}Java
๐Ÿ’ก
Functional Interface (Java 8+): An interface with exactly one abstract method. Used with lambda expressions. Annotated with @FunctionalInterface. Examples: Runnable, Comparator, Predicate.
โšก

Defining Methods & Method Overloading

07 ยท Method Syntax ยท Overloading ยท varargs
โ–ผ
Method Anatomy
// modifier returnType methodName(params) throws Exception
public static int add(int a, int b) {
    return a + b;
}

// Varargs - variable number of arguments
public int sum(int... nums) {
    int total = 0;
    for (int n : nums) total += n;
    return total;
}Java
Method Overloading
โ„น๏ธ
Method overloading = same method name, different parameter list (type/number/order). Return type alone cannot differentiate overloaded methods. Resolved at compile time (static polymorphism).
class Calculator {
    int    add(int a, int b)          { return a + b; }
    double add(double a, double b)  { return a + b; }
    int    add(int a, int b, int c)  { return a + b + c; }
    String add(String a, String b)  { return a + b; }
}
// Each call is resolved by the compiler based on argument typesJava
โš ๏ธ
Overloading vs Overriding: Overloading is compile-time, same class, different params. Overriding is runtime, subclass, same signature โ€” that's dynamic polymorphism.
๐Ÿ”

Recursion

08 ยท Base Case ยท Recursive Case ยท Call Stack
โ–ผ

Recursion occurs when a method calls itself. Every recursive solution needs a base case (stops recursion) and a recursive case (moves toward base case).

// Factorial: n! = n ร— (n-1)!
int factorial(int n) {
    if (n <= 1) return 1;       // Base case
    return n * factorial(n - 1); // Recursive case
}

// Fibonacci: fib(n) = fib(n-1) + fib(n-2)
int fib(int n) {
    if (n <= 1) return n;
    return fib(n-1) + fib(n-2);
}

// Stack trace for factorial(3):
// factorial(3) โ†’ 3 ร— factorial(2)
//   factorial(2) โ†’ 2 ร— factorial(1)
//     factorial(1) โ†’ 1 (base case)
//   returns 2 ร— 1 = 2
// returns 3 ร— 2 = 6Java
โš ๏ธ
Without a proper base case, recursion leads to StackOverflowError. Deep recursion also consumes significant stack memory โ€” consider iterative or tail-recursive approaches for large inputs.
๐Ÿ“Œ

Dealing with Static Members

09 ยท Static Variables ยท Static Methods ยท Static Blocks
โ–ผ

๐Ÿ“Š Static Variables

  • Belong to class, not objects
  • Shared across all instances
  • One copy in memory (Method Area)
  • Used for: counters, constants

โš™๏ธ Static Methods

  • Called on class, not instance
  • Cannot use this keyword
  • Cannot access non-static members directly
  • Used for: utility/factory methods
class BankAccount {
    private static int totalAccounts = 0; // class-level counter
    private int balance;

    static {
        System.out.println("BankAccount class loaded");
    }

    BankAccount() { totalAccounts++; }

    public static int getTotalAccounts() {
        return totalAccounts; // OK: accessing static from static
        // return balance;   // ERROR: cannot access instance var
    }
}

// Calling static method
System.out.println(BankAccount.getTotalAccounts());Java
๐Ÿ’ก
Singleton Pattern uses static: a private static instance + private constructor + public static getInstance() ensures only one object of the class is ever created.
๐Ÿ”ง

finalize(), Native Methods & 'this' Reference

10 ยท finalize ยท native ยท this keyword ยท Use of Modifiers
โ–ผ
finalize() Method

Called by the GC before reclaiming an object's memory. Used for cleanup (closing resources). Deprecated since Java 9 โ€” prefer try-with-resources or Cleaner.

class Resource {
    protected void finalize() throws Throwable {
        try {
            System.out.println("Cleaning up resource...");
            // close file handles, DB connections, etc.
        } finally {
            super.finalize(); // always call super
        }
    }
}Java
Native Methods
๐ŸŒ
Native methods are declared in Java but implemented in C/C++ via JNI (Java Native Interface). Declared with the native keyword and no body. Used for OS-level operations, performance-critical code.
class NativeDemo {
    public native void nativeMethod(); // no body!

    static {
        System.loadLibrary("nativeLib"); // load .dll/.so file
    }
}Java
The 'this' Reference

๐Ÿ”ต Uses of this

  • Refer to current object's fields/methods
  • Disambiguate when param names clash with fields
  • Call another constructor: this(args)
  • Pass current object as argument
  • Return current object from method

๐Ÿ”ต this in Builder Pattern

class Builder {
  String name;
  Builder setName(String n) {
    this.name = n;
    return this; // method chaining
  }
}
class Person {
    String name;
    int age;

    Person(String name, int age) {
        this.name = name; // this.name = field, name = param
        this.age = age;
    }

    Person(String name) {
        this(name, 0); // constructor chaining โ€” must be first statement
    }

    void greet(Greeter g) {
        g.greetPerson(this); // passing current object
    }
}Java
๐ŸŽ›๏ธ

Design of Accessor & Mutator Methods

11 ยท Getters ยท Setters ยท Encapsulation ยท Design Principles
โ–ผ

๐Ÿ“– Accessor (Getter)

Returns the value of a private field. Named getFieldName() or isFieldName() for booleans. Makes fields readable without direct access.

โœ๏ธ Mutator (Setter)

Sets/modifies a private field value. Named setFieldName(value). Enables validation logic before changing state.

class Employee {
    private String name;
    private double salary;
    private boolean active;

    // Accessor (getter)
    public String getName() { return name; }
    public double getSalary() { return salary; }
    public boolean isActive() { return active; } // boolean: 'is'

    // Mutator (setter) with validation
    public void setName(String name) {
        if (name == null || name.isBlank())
            throw new IllegalArgumentException("Name cannot be empty");
        this.name = name;
    }

    public void setSalary(double salary) {
        if (salary < 0)
            throw new IllegalArgumentException("Salary cannot be negative");
        this.salary = salary;
    }
}

// Usage: JavaBeans convention
Employee e = new Employee();
e.setName("Alice");
System.out.println(e.getName());Java
๐Ÿ’ก
JavaBeans convention: private fields + public getters/setters. This pattern enables frameworks (Spring, Hibernate, JavaFX) to reflectively access properties. It's the foundation of encapsulation.
๐Ÿงฌ

Cloning Objects, Shallow/Deep Copy & Generics

12 ยท clone() ยท Shallow Copy ยท Deep Copy ยท Generic Types
โ–ผ
Cloning Objects

๐Ÿชž Shallow Copy

Copies field values directly. Primitive fields are copied by value. Reference fields point to the same object โ€” not duplicated.

Default Object.clone() performs shallow copy.

๐Ÿ“‹ Deep Copy

Recursively copies all objects referenced. Creates completely independent copies of all nested objects. Must be manually implemented or via serialization.

// Shallow Copy
class Address {
    String city;
    Address(String c) { city = c; }
}

class Person implements Cloneable {
    String name;
    Address address;  // reference type

    // SHALLOW CLONE โ€” address is NOT duplicated
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    // DEEP CLONE โ€” address IS duplicated
    Person deepClone() {
        Person copy = new Person();
        copy.name = this.name;
        copy.address = new Address(this.address.city); // new object!
        return copy;
    }
}

Person p1 = new Person(); p1.name = "Alice"; p1.address = new Address("NY");
Person p2 = p1.deepClone();
p2.address.city = "LA";  // p1.address.city still "NY"Java
Generic Class Types

Generics enable writing type-safe code that works with any data type, catching type errors at compile time rather than runtime.

// Generic class
class Box<T> {
    private T item;
    public void set(T item) { this.item = item; }
    public T get() { return item; }
}

// Generic method
public static <T extends Comparable<T>> T max(T a, T b) {
    return a.compareTo(b) >= 0 ? a : b;
}

// Wildcards
void printList(List<?> list) { ... }               // unknown type
void addNums(List<? super Integer> l) { ... }      // lower bound
void readNums(List<? extends Number> l) { ... }    // upper bound

// Usage
Box<String> strBox = new Box<>();
strBox.set("Hello");
String s = strBox.get(); // no cast needed!Java
โš ๏ธ
Type Erasure: Generic type information is removed at runtime by the compiler. Box<String> and Box<Integer> are both just Box at runtime. Cannot use new T() or T.class directly.
๐Ÿ“

Array and String

13 ยท Arrays ยท Multi-dim ยท String Ops ยท StringBuffer ยท StringBuilder
โ–ผ
Defining & Initializing Arrays

๐Ÿ“ฆ Array Declaration

  • Fixed-size, homogeneous data structure
  • Zero-indexed, stored in contiguous memory
  • Default values: 0, false, null
  • array.length gives size (not a method)

๐Ÿ”ข Multi-Dimensional Arrays

  • 2D: int[][] mat = new int[3][4]
  • Jagged arrays: rows of different lengths
  • Accessed via mat[row][col]
  • Enhanced for-loop for iteration
// Array declaration and initialization
int[] nums = new int[5];                  // default zeros
int[] primes = {2, 3, 5, 7, 11};           // array literal
String[] names = new String[]{"Alice", "Bob"};

// Accessing
System.out.println(primes[0]);              // 2
System.out.println(primes.length);          // 5

// Enhanced for-loop
for (int p : primes) System.out.print(p + " ");

// 2D Array (Matrix)
int[][] matrix = {{1,2},{3,4},{5,6}};
for (int[] row : matrix)
    for (int val : row) System.out.print(val + "\t");

// Jagged array
int[][] jagged = new int[3][];
jagged[0] = new int[2]; jagged[1] = new int[4]; jagged[2] = new int[1];Java
String Operations
โ„น๏ธ
String in Java is an immutable object stored in the String Pool (inside heap). Two string literals with the same value share a single pool entry.
Method Description Example
length() Returns character count "hello".length() โ†’ 5
charAt(i) Char at index i "abc".charAt(1) โ†’ 'b'
substring(s,e) Extracts substring "hello".substring(1,3) โ†’ "el"
indexOf(s) First occurrence index "hello".indexOf('l') โ†’ 2
toUpperCase() Convert to uppercase "hi".toUpperCase() โ†’ "HI"
trim() Strip leading/trailing spaces " hi ".trim() โ†’ "hi"
replace(a,b) Replace characters/substrings "cat".replace('c','b') โ†’ "bat"
split(regex) Split into String array "a,b,c".split(",") โ†’ ["a","b","c"]
equals(s) Content equality (use over ==) "abc".equals("abc") โ†’ true
compareTo(s) Lexicographic comparison Returns 0 if equal
Mutable vs Immutable Strings

๐Ÿ”’ String (Immutable)

  • Cannot be modified after creation
  • Every change creates a new object
  • Thread-safe, can be shared
  • Cached in String Pool

๐Ÿ”“ StringBuilder / StringBuffer (Mutable)

  • StringBuilder: fast, not thread-safe
  • StringBuffer: synchronized, thread-safe
  • Modify in-place (same object)
  • Use for heavy string concatenation
// Immutable String โ€” creates new objects
String s = "Hello";
s = s + " World";    // old "Hello" object abandoned

// StringBuilder โ€” modifies in-place
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");   // same object
sb.insert(5, ",");     // "Hello, World"
sb.delete(0, 5);      // ", World"
sb.reverse();          // "dlroW ,"
sb.replace(0, 2, "XX"); // replace range
String result = sb.toString();

// StringBuffer (thread-safe, same API as StringBuilder)
StringBuffer buf = new StringBuffer();
buf.append("safe"); // synchronized

// Tokenizing with StringTokenizer
import java.util.StringTokenizer;
StringTokenizer st = new StringTokenizer("one two three", " ");
while (st.hasMoreTokens())
    System.out.println(st.nextToken());

// Loop over characters
for (int i = 0; i < s.length(); i++)
    System.out.print(s.charAt(i));

// Collection classes for strings
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));Java
๐Ÿงฌ

Inheritance

14 ยท Types ยท Constructors ยท Overriding ยท super ยท Polymorphism
โ–ผ
Benefits of Inheritance

โœ… Why Inherit?

  • Code reuse โ€” avoid duplication
  • Extensibility โ€” add new behavior
  • Polymorphism โ€” unified interface
  • Maintainability โ€” change in one place

๐Ÿ”— Types of Inheritance

  • Single โ€” A extends B
  • Multilevel โ€” A โ†’ B โ†’ C
  • Hierarchical โ€” B, C both extend A
  • Multiple โ€” via interfaces only in Java
Constructors in Inheritance
โ„น๏ธ
Constructors are not inherited, but the parent constructor is always called first. super() must be the first statement. If omitted, the compiler inserts a no-arg super() call automatically.
class Animal {
    String name;
    Animal(String name) { this.name = name; }
    void speak() { System.out.println("..."); }
    void breathe() { System.out.println("Breathing"); }
}

class Dog extends Animal {
    String breed;

    // Constructor chaining with super()
    Dog(String name, String breed) {
        super(name);         // must be first!
        this.breed = breed;
    }

    // Method overriding
    @Override
    void speak() {
        super.speak();        // call parent version
        System.out.println("Woof!");
    }
}

class GoldenRetriever extends Dog {   // Multilevel
    GoldenRetriever(String name) { super(name, "Golden"); }
}Java
Overriding Rules
Rule Detail
Same signature Same name, same parameter list
Return type Same or covariant (subtype) return type
Access modifier Cannot be more restrictive than parent
Cannot override static, final, private methods
@Override Annotation recommended โ€” compile-time check
Polymorphism & Type Conversion
// Runtime Polymorphism (dynamic dispatch)
Animal a = new Dog("Rex", "Labrador"); // upcasting (implicit)
a.speak();   // calls Dog's speak() at runtime

// Downcasting (explicit, may throw ClassCastException)
if (a instanceof Dog) {
    Dog d = (Dog) a;       // safe downcast
    System.out.println(d.breed);
}

// Implementing an interface
interface Swimmable { void swim(); }
class Duck extends Animal implements Swimmable {
    Duck(String n) { super(n); }
    public void swim() { System.out.println("Splashing!"); }
    public void speak() { System.out.println("Quack!"); }
}Java
๐Ÿ’ก
Type Compatibility: Java uses IS-A relationship for type compatibility. A Dog IS-A Animal, so it can be assigned to an Animal reference. Use instanceof to check before downcasting.
๐Ÿ“ฆ

Packages

15 ยท Concept ยท Organizing Classes ยท Access Protection ยท Import
โ–ผ
Concept of Package

A package is a namespace that organizes related classes and interfaces into groups โ€” similar to folders in a file system. Packages prevent naming conflicts and control access.

๐Ÿ“ Built-in Packages

  • java.lang โ€” auto-imported (String, Math, Object)
  • java.util โ€” Collections, Scanner, Date
  • java.io โ€” File I/O streams
  • java.net โ€” Networking
  • java.awt / javax.swing โ€” GUI

๐Ÿ” Package as Access Protection

  • default (no modifier): accessible within same package only
  • public: accessible from any package
  • protected: package + subclasses in other packages
  • Package encapsulates implementation details
// File: com/myapp/utils/MathHelper.java
package com.myapp.utils;   // must be first statement

public class MathHelper {
    public static int square(int n) { return n * n; }
    static void helper() {}   // package-private (default)
}

// File: com/myapp/Main.java
package com.myapp;

import com.myapp.utils.MathHelper;       // specific class
import com.myapp.utils.*;                 // all public classes
import static java.lang.Math.PI;         // static import

public class Main {
    public static void main(String[] args) {
        System.out.println(MathHelper.square(5)); // 25
        System.out.println(PI);               // static import usage
    }
}

// Directory structure mirrors package name:
// src/
//   com/
//     myapp/
//       Main.java
//       utils/
//         MathHelper.javaJava
โš ๏ธ
Wildcard import (import java.util.*) imports all public types but not sub-packages. It does NOT affect performance โ€” the compiler resolves needed classes only.
๐Ÿšจ

Exception Handling

16 ยท try-catch-finally ยท throw ยท throws ยท Checked vs Unchecked
โ–ผ
Idea Behind Exceptions

An exception is an abnormal condition that disrupts normal program flow. Java separates error-handling code from regular code using a structured mechanism, making programs more robust and readable.

โš ๏ธ Errors vs Exceptions

  • Error: JVM-level, unrecoverable (OutOfMemoryError, StackOverflowError)
  • Exception: Application-level, recoverable
  • Both extend Throwable

๐Ÿ—‚๏ธ Types of Exceptions

  • Checked: must be handled or declared (IOException, SQLException)
  • Unchecked: RuntimeExceptions (NullPointerException, ArrayIndexOutOfBoundsException)
Exception Hierarchy
Class Category Example
Throwable Root All exceptions/errors
Error JVM / System OutOfMemoryError, StackOverflowError
Exception Application All checked exceptions
RuntimeException Unchecked NullPointerException, ClassCastException
IOException Checked FileNotFoundException
try, catch, finally, throw, throws
// Basic try-catch-finally
try {
    int[] arr = new int[3];
    arr[5] = 10;                // throws ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("Index error: " + e.getMessage());
} catch (Exception e) {        // catch-all (more specific first!)
    e.printStackTrace();
} finally {
    System.out.println("Always runs โ€” close resources here");
}

// Multi-catch (Java 7+)
try { ... }
catch (IOException | SQLException e) { e.printStackTrace(); }

// throw โ€” manually throw an exception
void setAge(int age) {
    if (age < 0) throw new IllegalArgumentException("Age cannot be negative");
    this.age = age;
}

// throws โ€” declare checked exceptions in method signature
void readFile(String path) throws IOException {
    new FileReader(path);     // checked exception
}

// try-with-resources (Java 7+) โ€” auto-closes resources
try (FileReader fr = new FileReader("file.txt")) {
    // fr.close() is called automatically
}

// User-defined exception
class InsufficientFundsException extends Exception {
    double amount;
    InsufficientFundsException(double amt) {
        super("Insufficient funds: " + amt);
        amount = amt;
    }
}Java
๐Ÿ’ก
Control Flow: When an exception is thrown, the JVM searches for a matching catch block up the call stack. If none is found, the thread terminates. finally always executes unless System.exit() is called.
โš™๏ธ

Multithreading

17 ยท Thread Lifecycle ยท Priorities ยท Synchronization ยท Deadlock
โ–ผ
Understanding Threads

A thread is the smallest unit of execution within a process. Java supports multithreading natively, enabling concurrent tasks to run within a single program.

๐Ÿงต Thread Lifecycle

  • NEW โ€” created, not started
  • RUNNABLE โ€” running or ready
  • BLOCKED โ€” waiting for monitor lock
  • WAITING โ€” waiting indefinitely
  • TIMED_WAITING โ€” waiting with timeout
  • TERMINATED โ€” execution complete

๐Ÿ† Thread Priorities

  • Range: 1 (MIN) to 10 (MAX), default 5 (NORM)
  • Set with t.setPriority(n)
  • Higher priority โ‰  guaranteed first execution
  • JVM / OS schedules threads โ€” priority is a hint
// Method 1: Extending Thread class
class MyThread extends Thread {
    public void run() { System.out.println("Thread running: " + getName()); }
}

// Method 2: Implementing Runnable (preferred)
class Task implements Runnable {
    public void run() { System.out.println("Task running"); }
}

// Creating and starting threads
Thread t1 = new MyThread();
Thread t2 = new Thread(new Task());
Thread t3 = new Thread(() -> System.out.println("Lambda thread"));

t1.setPriority(Thread.MAX_PRIORITY); // priority 10
t1.start(); t2.start(); t3.start();

t1.join(); // wait for t1 to complete before continuing
Thread.sleep(1000); // pause current thread 1 secondJava
Synchronization
โš ๏ธ
Without synchronization, concurrent access to shared data causes race conditions โ€” unpredictable results when multiple threads read/write simultaneously.
class Counter {
    private int count = 0;

    // Synchronized method โ€” only one thread at a time
    public synchronized void increment() { count++; }

    // Synchronized block โ€” finer control
    public void add(int n) {
        synchronized (this) { count += n; }
    }

    public int getCount() { return count; }
}

// Inter-thread communication (Producer-Consumer pattern)
class SharedBuffer {
    private int data; private boolean ready = false;

    public synchronized void produce(int v) throws InterruptedException {
        while (ready) wait();   // release lock, wait
        data = v; ready = true;
        notifyAll();              // wake waiting threads
    }

    public synchronized int consume() throws InterruptedException {
        while (!ready) wait();
        ready = false;
        notifyAll();
        return data;
    }
}Java
Deadlock
๐Ÿ”’
Deadlock occurs when two or more threads are waiting for each other to release locks, creating a circular dependency. Prevention: always acquire locks in the same order, use tryLock() with timeouts, or use higher-level concurrency utilities (java.util.concurrent).
// Deadlock scenario
Object lockA = new Object(), lockB = new Object();

Thread t1 = new Thread(() -> {
    synchronized (lockA) {
        synchronized (lockB) { System.out.println("T1 done"); }
    }
});

Thread t2 = new Thread(() -> {
    synchronized (lockB) {   // DEADLOCK: opposite lock order!
        synchronized (lockA) { System.out.println("T2 done"); }
    }
});
// Fix: both threads acquire lockA THEN lockBJava
๐Ÿ’พ

Input / Output (I/O)

18 ยท Streams ยท File I/O ยท Buffers ยท Channels ยท Serialization
โ–ผ
Understanding Streams

A stream is a sequence of data flowing from a source to a destination. Java I/O is built around two stream hierarchies:

๐Ÿ”ต Byte Streams

  • Handle raw binary data (8-bit bytes)
  • InputStream / OutputStream (abstract roots)
  • Subclasses: FileInputStream, BufferedInputStream, DataInputStream
  • Used for images, audio, binary files

๐ŸŸข Character Streams

  • Handle Unicode text (16-bit chars)
  • Reader / Writer (abstract roots)
  • Subclasses: FileReader, BufferedReader, PrintWriter
  • Used for text files, console
Standard Streams
Stream Object Default Source/Dest
Standard Input System.in Keyboard
Standard Output System.out Console (stdout)
Standard Error System.err Console (stderr)
File I/O Basics
import java.io.*;
import java.nio.file.*;
import java.nio.channels.*;

// Working with File object
File f = new File("data.txt");
System.out.println(f.exists());      // true/false
System.out.println(f.length());     // bytes
System.out.println(f.getName());    // "data.txt"
f.createNewFile();                   // create if not exists
f.delete();                          // delete file

// Writing to a file (BufferedWriter)
try (BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"))) {
    bw.write("Hello, File!");
    bw.newLine();
    bw.write("Second line");
}

// Reading from a file (BufferedReader)
try (BufferedReader br = new BufferedReader(new FileReader("out.txt"))) {
    String line;
    while ((line = br.readLine()) != null)
        System.out.println(line);
}

// PrintWriter for formatted output
try (PrintWriter pw = new PrintWriter(new FileWriter("log.txt", true))) {
    pw.printf("Value: %d%n", 42); // append mode
}Java
NIO โ€” File Channel & Buffers
// NIO FileChannel Read/Write with Buffer
try (FileChannel fc = FileChannel.open(Paths.get("data.bin"),
        StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {

    // Write
    ByteBuffer wBuf = ByteBuffer.allocate(64);
    wBuf.put("NIO Channel Write".getBytes());
    wBuf.flip();       // switch to read mode
    fc.write(wBuf);

    // Read
    fc.position(0);    // rewind
    ByteBuffer rBuf = ByteBuffer.allocate(64);
    fc.read(rBuf);
    rBuf.flip();
    System.out.println(new String(rBuf.array(), 0, rBuf.limit()));
}Java
Serialization
๐Ÿ’พ
Serialization converts an object into a byte stream for storage or transmission. Deserialization reconstructs the object. The class must implement Serializable. Fields marked transient are skipped.
import java.io.*;

class Student implements Serializable {
    private static final long serialVersionUID = 1L;
    String name;
    int roll;
    transient String password;  // NOT serialized
}

// Serialize (write object to file)
try (ObjectOutputStream oos = new ObjectOutputStream(
        new FileOutputStream("student.ser"))) {
    Student s = new Student(); s.name = "Alice"; s.roll = 101;
    oos.writeObject(s);
}

// Deserialize (read object from file)
try (ObjectInputStream ois = new ObjectInputStream(
        new FileInputStream("student.ser"))) {
    Student s = (Student) ois.readObject();
    System.out.println(s.name + " " + s.roll);
    System.out.println(s.password); // null โ€” was transient
}Java
๐Ÿ’ก
Buffer Management: Buffers (BufferedReader, BufferedWriter) reduce I/O calls by caching data in memory. Always use buffers for file I/O โ€” raw unbuffered access makes one system call per byte, which is extremely slow for large files.