Codcups
Progress: 0%

01. πŸš€ Java Fundamentals

Java is a high-level, class-based, object-oriented programming language designed to have as few implementation dependencies as possible. It was created by James Gosling at Sun Microsystems (now owned by Oracle) in 1995.

Why Learn Java?

Java remains one of the most popular programming languages because:
1. Platform Independence: Write Once, Run Anywhere (WORA)
2. Object-Oriented: Pure OOP language
3. Robust & Secure: Memory management, exception handling
4. Multithreading: Built-in support for concurrent programming
5. Vast Ecosystem: Android apps, web apps, enterprise systems

Java Architecture

  • Java Source Code: .java files
  • Java Compiler (javac): Compiles to bytecode
  • Bytecode: .class files (platform-independent)
  • JVM (Java Virtual Machine): Executes bytecode
  • JRE (Java Runtime Environment): JVM + libraries
  • JDK (Java Development Kit): JRE + development tools
Key Concept: Java is both compiled and interpreted. Source code is compiled to bytecode, which is then interpreted by JVM. This enables platform independence through the "Write Once, Run Anywhere" principle.

Example: Your First Java Program

// Simple Java program to demonstrate basic structure
public class HelloWorld {
    
    // Main method - entry point of Java program
    public static void main(String[] args) {
        
        // Print "Hello, World!" to console
        System.out.println("Hello, World!");
        
        // Basic variables
        int number = 42;
        String message = "Learning Java";
        
        // Using variables
        System.out.println(message);
        System.out.println("The answer is: " + number);
        
        // Simple calculation
        int sum = 5 + 3;
        System.out.println("5 + 3 = " + sum);
        
        // Conditional statement
        if (number > 10) {
            System.out.println("Number is greater than 10");
        } else {
            System.out.println("Number is 10 or less");
        }
        
        // Loop example
        System.out.println("Counting from 1 to 3:");
        for (int i = 1; i <= 3; i++) {
            System.out.println("Count: " + i);
        }
    }
}

Note: Java is case-sensitive. Every Java program must have a class with a main method. The class name should match the filename (HelloWorld.java). Use proper indentation for readability.

Java Program Structure
Click buttons to understand Java compilation and execution process

02. πŸ“ Java Syntax Basics

Java syntax is similar to C and C++, but with stricter rules and fewer low-level features. Understanding Java syntax is crucial for writing correct and efficient code.

Basic Syntax Rules

  • Case Sensitivity: Java is case-sensitive
  • Class Names: Should start with uppercase letter (PascalCase)
  • Method Names: Should start with lowercase letter (camelCase)
  • Program File Name: Must match class name exactly
  • Main Method: Required for program execution
  • Statements: Must end with semicolon (;)
  • Blocks: Enclosed in curly braces {}

Java Comments

Comments are essential for documentation and code readability:
1. Single-line: // This is a comment
2. Multi-line: /* This is a multi-line comment */
3. Documentation: /** This is a Javadoc comment */

Example: Java Syntax in Action

// File: SyntaxDemo.java
// This class demonstrates Java syntax rules

// Class declaration (PascalCase)
public class SyntaxDemo {
    
    // Constant (UPPER_SNAKE_CASE)
    public static final double PI = 3.14159;
    
    // Instance variable (camelCase)
    private int instanceCount;
    
    // Constructor (same name as class)
    public SyntaxDemo() {
        this.instanceCount = 0;
    }
    
    // Method declaration (camelCase)
    public void demonstrateSyntax() {
        
        // Local variable (camelCase)
        int localVariable = 10;
        
        // If statement
        if (localVariable > 5) {
            System.out.println("Variable is greater than 5");
        }
        
        // For loop
        for (int i = 0; i < 3; i++) {
            System.out.println("Loop iteration: " + i);
        }
        
        // While loop
        int counter = 0;
        while (counter < 2) {
            System.out.println("While loop count: " + counter);
            counter++;
        }
        
        // Do-while loop
        int number = 5;
        do {
            System.out.println("Do-while executes at least once");
            number--;
        } while (number > 0);
        
        // Switch statement
        int day = 3;
        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("Other day");
        }
    }
    
    // Main method - program entry point
    public static void main(String[] args) {
        
        // Creating object
        SyntaxDemo demo = new SyntaxDemo();
        
        // Calling method
        demo.demonstrateSyntax();
        
        // Array declaration and initialization
        int[] numbers = {1, 2, 3, 4, 5};
        
        // Enhanced for loop (for-each)
        System.out.println("Array elements:");
        for (int num : numbers) {
            System.out.print(num + " ");
        }
        System.out.println();
        
        // Using constants
        double radius = 5.0;
        double area = PI * radius * radius;
        System.out.println("Area of circle: " + area);
    }
}
Syntax Rules Checker
Click buttons to learn about Java syntax rules

03. πŸ“¦ Data Types & Variables

Java is a strongly typed language, meaning every variable must be declared with a specific data type. Java has two categories of data types: primitive and reference types.

Primitive Data Types

  • byte: 8-bit integer (-128 to 127)
  • short: 16-bit integer (-32,768 to 32,767)
  • int: 32-bit integer (most common)
  • long: 64-bit integer
  • float: 32-bit floating point
  • double: 64-bit floating point (default)
  • char: 16-bit Unicode character
  • boolean: true or false

Reference Data Types

Reference types store references (addresses) to objects:
1. Classes: String, Scanner, etc.
2. Arrays: int[], String[], etc.
3. Interfaces: Runnable, List, etc.
4. Enums: Special class types

Example: Data Types and Variables

public class DataTypesDemo {
    public static void main(String[] args) {
        
        // Primitive data types
        byte smallNumber = 100;           // 8-bit integer
        short mediumNumber = 30000;       // 16-bit integer
        int regularNumber = 1000000;      // 32-bit integer (most common)
        long largeNumber = 10000000000L;  // 64-bit integer (note L suffix)
        
        float singlePrecision = 3.14f;    // 32-bit float (note f suffix)
        double doublePrecision = 3.141592653589793; // 64-bit double (default)
        
        char letter = 'A';                // Single character
        char unicodeChar = '\u0041';      // Unicode character 'A'
        
        boolean isJavaFun = true;         // true or false
        
        // Display primitive types
        System.out.println("=== Primitive Data Types ===");
        System.out.println("byte: " + smallNumber);
        System.out.println("short: " + mediumNumber);
        System.out.println("int: " + regularNumber);
        System.out.println("long: " + largeNumber);
        System.out.println("float: " + singlePrecision);
        System.out.println("double: " + doublePrecision);
        System.out.println("char: " + letter);
        System.out.println("boolean: " + isJavaFun);
        
        // Reference data types
        String message = "Hello, Java!";  // String object
        int[] numbers = {1, 2, 3, 4, 5};  // Array object
        
        // Display reference types
        System.out.println("\n=== Reference Data Types ===");
        System.out.println("String: " + message);
        System.out.print("Array: ");
        for (int num : numbers) {
            System.out.print(num + " ");
        }
        System.out.println();
        
        // Type casting
        System.out.println("\n=== Type Casting ===");
        
        // Widening casting (automatic)
        int intValue = 100;
        double doubleValue = intValue;    // Automatic: int to double
        System.out.println("int to double: " + intValue + " -> " + doubleValue);
        
        // Narrowing casting (manual)
        double price = 19.99;
        int intPrice = (int) price;       // Manual: double to int
        System.out.println("double to int: " + price + " -> " + intPrice);
        
        // Overflow example
        byte maxByte = 127;
        byte overflow = (byte) (maxByte + 1);
        System.out.println("Byte overflow: 127 + 1 = " + overflow);
        
        // Type promotion in expressions
        System.out.println("\n=== Type Promotion ===");
        byte b = 10;
        short s = 20;
        int i = 30;
        long l = 40L;
        float f = 50.0f;
        double d = 60.0;
        
        // Result is promoted to the largest type in expression
        double result = b + s + i + l + f + d;
        System.out.println("Promoted result: " + result);
        
        // Wrapper classes (autoboxing/unboxing)
        System.out.println("\n=== Wrapper Classes ===");
        Integer wrappedInt = 42;          // Autoboxing: int to Integer
        int primitiveInt = wrappedInt;    // Unboxing: Integer to int
        
        System.out.println("Wrapped Integer: " + wrappedInt);
        System.out.println("Primitive int: " + primitiveInt);
        
        // Useful wrapper class methods
        System.out.println("Max int value: " + Integer.MAX_VALUE);
        System.out.println("Min int value: " + Integer.MIN_VALUE);
        System.out.println("Integer to binary: " + Integer.toBinaryString(42));
        System.out.println("Parse string to int: " + Integer.parseInt("123"));
        
        // Constants
        final double PI = 3.14159;
        final int DAYS_IN_WEEK = 7;
        
        System.out.println("\n=== Constants ===");
        System.out.println("PI: " + PI);
        System.out.println("Days in week: " + DAYS_IN_WEEK);
        
        // PI = 3.14; // This would cause error - cannot reassign final variable
    }
}
Data Type Explorer
Click buttons to explore Java data types

04. βž• Operators & Control Flow

Java provides a rich set of operators for performing operations on variables and values. Control flow statements determine the execution order of program statements.

Java Operator Categories

  • Arithmetic: +, -, *, /, %, ++, --
  • Assignment: =, +=, -=, *=, /=, %=
  • Comparison: ==, !=, >, <, >=, <=
  • Logical: &&, ||, !
  • Bitwise: &, |, ^, ~, <<, >>, >>>
  • Ternary: ? : (conditional operator)
  • Instanceof: Type comparison operator

Control Flow Statements

Java provides several control flow mechanisms:
1. Conditional: if, if-else, switch
2. Looping: for, while, do-while, for-each
3. Branching: break, continue, return
4. Exception: try, catch, finally, throw

Example: Operators and Control Flow

public class OperatorsControlFlow {
    public static void main(String[] args) {
        
        System.out.println("=== ARITHMETIC OPERATORS ===");
        int a = 10, b = 3;
        System.out.println("a = " + a + ", b = " + b);
        System.out.println("Addition (a + b): " + (a + b));
        System.out.println("Subtraction (a - b): " + (a - b));
        System.out.println("Multiplication (a * b): " + (a * b));
        System.out.println("Division (a / b): " + (a / b));
        System.out.println("Modulus (a % b): " + (a % b));
        
        // Increment/Decrement
        int x = 5;
        System.out.println("\nPost-increment (x++): " + x++); // Prints 5, then x becomes 6
        System.out.println("After post-increment: " + x);
        System.out.println("Pre-increment (++x): " + ++x);   // x becomes 7, then prints 7
        
        System.out.println("\n=== ASSIGNMENT OPERATORS ===");
        int num = 10;
        num += 5;  // num = num + 5
        System.out.println("num += 5: " + num);
        num *= 2;  // num = num * 2
        System.out.println("num *= 2: " + num);
        
        System.out.println("\n=== COMPARISON OPERATORS ===");
        int p = 10, q = 20;
        System.out.println("p == q: " + (p == q));
        System.out.println("p != q: " + (p != q));
        System.out.println("p > q: " + (p > q));
        System.out.println("p < q: " + (p < q));
        System.out.println("p >= 10: " + (p >= 10));
        
        System.out.println("\n=== LOGICAL OPERATORS ===");
        boolean isJavaFun = true;
        boolean isProgrammingHard = false;
        System.out.println("isJavaFun && isProgrammingHard: " + 
                          (isJavaFun && isProgrammingHard));
        System.out.println("isJavaFun || isProgrammingHard: " + 
                          (isJavaFun || isProgrammingHard));
        System.out.println("!isProgrammingHard: " + (!isProgrammingHard));
        
        // Short-circuit evaluation
        System.out.println("\n=== SHORT-CIRCUIT EVALUATION ===");
        int count = 0;
        if (count != 0 && 10 / count > 2) {
            System.out.println("This won't execute (short-circuit)");
        }
        
        System.out.println("\n=== TERNARY OPERATOR ===");
        int age = 18;
        String canVote = (age >= 18) ? "Yes" : "No";
        System.out.println("Age " + age + " can vote? " + canVote);
        
        System.out.println("\n=== CONTROL FLOW: IF-ELSE ===");
        int score = 85;
        if (score >= 90) {
            System.out.println("Grade: A");
        } else if (score >= 80) {
            System.out.println("Grade: B");
        } else if (score >= 70) {
            System.out.println("Grade: C");
        } else {
            System.out.println("Grade: F");
        }
        
        System.out.println("\n=== CONTROL FLOW: SWITCH ===");
        int day = 3;
        switch (day) {
            case 1:
                System.out.println("Monday");
                break;
            case 2:
                System.out.println("Tuesday");
                break;
            case 3:
                System.out.println("Wednesday");
                break;
            case 4:
                System.out.println("Thursday");
                break;
            case 5:
                System.out.println("Friday");
                break;
            default:
                System.out.println("Weekend");
        }
        
        // Switch expression (Java 14+)
        String dayType = switch (day) {
            case 1, 2, 3, 4, 5 -> "Weekday";
            case 6, 7 -> "Weekend";
            default -> "Invalid day";
        };
        System.out.println("Day type: " + dayType);
        
        System.out.println("\n=== CONTROL FLOW: LOOPS ===");
        
        // For loop
        System.out.print("For loop (1-5): ");
        for (int i = 1; i <= 5; i++) {
            System.out.print(i + " ");
        }
        System.out.println();
        
        // While loop
        System.out.print("While loop (5-1): ");
        int j = 5;
        while (j >= 1) {
            System.out.print(j + " ");
            j--;
        }
        System.out.println();
        
        // Do-while loop
        System.out.print("Do-while loop (executes at least once): ");
        int k = 0;
        do {
            System.out.print(k + " ");
            k++;
        } while (k < 3);
        System.out.println();
        
        // For-each loop (enhanced for loop)
        System.out.print("For-each loop (array elements): ");
        int[] numbers = {10, 20, 30, 40, 50};
        for (int number : numbers) {
            System.out.print(number + " ");
        }
        System.out.println();
        
        System.out.println("\n=== CONTROL FLOW: BREAK & CONTINUE ===");
        
        // Break example
        System.out.print("Break at 3: ");
        for (int i = 1; i <= 5; i++) {
            if (i == 3) {
                break; // Exit loop
            }
            System.out.print(i + " ");
        }
        System.out.println();
        
        // Continue example
        System.out.print("Continue (skip 3): ");
        for (int i = 1; i <= 5; i++) {
            if (i == 3) {
                continue; // Skip iteration
            }
            System.out.print(i + " ");
        }
        System.out.println();
        
        // Labeled break (rarely used)
        System.out.println("\n=== LABELED BREAK ===");
        outerLoop:
        for (int i = 1; i <= 3; i++) {
            for (int m = 1; m <= 3; m++) {
                System.out.println("i=" + i + ", m=" + m);
                if (i == 2 && m == 2) {
                    break outerLoop; // Break out of both loops
                }
            }
        }
        
        System.out.println("\n=== BITWISE OPERATORS ===");
        int bitA = 5;  // Binary: 0101
        int bitB = 3;  // Binary: 0011
        
        System.out.println("bitA & bitB (AND): " + (bitA & bitB));  // 0001 = 1
        System.out.println("bitA | bitB (OR): " + (bitA | bitB));   // 0111 = 7
        System.out.println("bitA ^ bitB (XOR): " + (bitA ^ bitB));  // 0110 = 6
        System.out.println("~bitA (NOT): " + (~bitA));              // ...11111010 = -6
        System.out.println("bitA << 1 (Left shift): " + (bitA << 1)); // 1010 = 10
        System.out.println("bitA >> 1 (Right shift): " + (bitA >> 1)); // 0010 = 2
        
        System.out.println("\n=== INSTANCEOF OPERATOR ===");
        String text = "Hello";
        System.out.println("text instanceof String: " + (text instanceof String));
        System.out.println("text instanceof Object: " + (text instanceof Object));
    }
}
Operator Calculator
Enter numbers and select operator

05. πŸ—ƒοΈ Arrays & Strings

Arrays are collections of elements of the same type, while Strings are sequences of characters. Both are fundamental data structures in Java.

Array Characteristics

  • Fixed Size: Length cannot be changed after creation
  • Indexed Access: Elements accessed via index (0 to length-1)
  • Contiguous Memory: Elements stored sequentially
  • Type Safety: All elements must be same type
  • Multi-dimensional: Arrays of arrays (2D, 3D, etc.)

String Features

Strings in Java are:
1. Immutable: Cannot be changed after creation
2. Interned: String pool for memory efficiency
3. Unicode Support: Full UTF-16 character set
4. Rich API: Many built-in methods for manipulation

Example: Arrays and Strings

import java.util.Arrays;

public class ArraysStringsDemo {
    public static void main(String[] args) {
        
        System.out.println("=== ARRAY DECLARATION & INITIALIZATION ===");
        
        // Different ways to declare and initialize arrays
        int[] numbers1 = new int[5];               // Declaration with size
        int[] numbers2 = {1, 2, 3, 4, 5};          // Declaration with initialization
        int[] numbers3 = new int[]{10, 20, 30};    // Full syntax
        
        // Default values
        System.out.println("Default int array values:");
        for (int i = 0; i < numbers1.length; i++) {
            System.out.print(numbers1[i] + " ");   // All zeros
        }
        System.out.println();
        
        // Accessing and modifying
        numbers1[0] = 100;
        numbers1[1] = 200;
        System.out.println("After modification: " + numbers1[0] + ", " + numbers1[1]);
        
        System.out.println("\n=== ARRAY TRAVERSAL ===");
        
        // Traditional for loop
        System.out.print("Traditional for loop: ");
        for (int i = 0; i < numbers2.length; i++) {
            System.out.print(numbers2[i] + " ");
        }
        System.out.println();
        
        // Enhanced for loop (for-each)
        System.out.print("Enhanced for loop: ");
        for (int num : numbers2) {
            System.out.print(num + " ");
        }
        System.out.println();
        
        // Using Arrays.toString()
        System.out.println("Arrays.toString(): " + Arrays.toString(numbers2));
        
        System.out.println("\n=== MULTI-DIMENSIONAL ARRAYS ===");
        
        // 2D array declaration
        int[][] matrix = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        };
        
        // Traversing 2D array
        System.out.println("2D Array:");
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                System.out.print(matrix[i][j] + " ");
            }
            System.out.println();
        }
        
        // Jagged array (arrays of different lengths)
        int[][] jagged = {
            {1, 2},
            {3, 4, 5},
            {6, 7, 8, 9}
        };
        
        System.out.println("\nJagged Array:");
        for (int[] row : jagged) {
            for (int val : row) {
                System.out.print(val + " ");
            }
            System.out.println();
        }
        
        System.out.println("\n=== ARRAY METHODS ===");
        
        // Arrays class utility methods
        int[] arr = {5, 2, 8, 1, 9};
        
        // Sorting
        Arrays.sort(arr);
        System.out.println("Sorted array: " + Arrays.toString(arr));
        
        // Searching
        int index = Arrays.binarySearch(arr, 8);
        System.out.println("Index of 8: " + index);
        
        // Copying
        int[] copy = Arrays.copyOf(arr, 3);
        System.out.println("Copy of first 3 elements: " + Arrays.toString(copy));
        
        // Filling
        int[] filled = new int[5];
        Arrays.fill(filled, 7);
        System.out.println("Filled array: " + Arrays.toString(filled));
        
        // Comparing
        int[] arr1 = {1, 2, 3};
        int[] arr2 = {1, 2, 3};
        System.out.println("Arrays equal? " + Arrays.equals(arr1, arr2));
        
        System.out.println("\n=== STRING BASICS ===");
        
        // String creation
        String str1 = "Hello";                    // String literal (goes to string pool)
        String str2 = new String("Hello");        // String object (heap)
        String str3 = "Hello";
        
        System.out.println("str1 == str2: " + (str1 == str2));     // false (different references)
        System.out.println("str1 == str3: " + (str1 == str3));     // true (same reference in pool)
        System.out.println("str1.equals(str2): " + str1.equals(str2)); // true (same content)
        
        System.out.println("\n=== STRING METHODS ===");
        
        String text = "  Java Programming  ";
        
        // Length and character access
        System.out.println("Original: '" + text + "'");
        System.out.println("Length: " + text.length());
        System.out.println("Char at index 5: " + text.charAt(5));
        
        // Trimming and case
        System.out.println("Trimmed: '" + text.trim() + "'");
        System.out.println("Uppercase: " + text.toUpperCase());
        System.out.println("Lowercase: " + text.toLowerCase());
        
        // Substring
        System.out.println("Substring(2, 6): " + text.substring(2, 6));
        
        // Searching
        System.out.println("Contains 'Java': " + text.contains("Java"));
        System.out.println("Index of 'Pro': " + text.indexOf("Pro"));
        System.out.println("Starts with 'Java': " + text.startsWith("Java"));
        System.out.println("Ends with 'ing': " + text.endsWith("ing"));
        
        // Replacement
        System.out.println("Replace 'Java' with 'Python': " + text.replace("Java", "Python"));
        
        // Splitting
        String csv = "Apple,Banana,Cherry";
        String[] fruits = csv.split(",");
        System.out.println("Split fruits: " + Arrays.toString(fruits));
        
        // Joining (Java 8+)
        String joined = String.join(" - ", fruits);
        System.out.println("Joined: " + joined);
        
        // Comparison
        String s1 = "apple";
        String s2 = "banana";
        System.out.println("Compare 'apple' to 'banana': " + s1.compareTo(s2));
        System.out.println("Compare ignoring case: " + s1.compareToIgnoreCase("APPLE"));
        
        System.out.println("\n=== STRING BUILDER (MUTABLE) ===");
        
        // StringBuilder - mutable, efficient for concatenation
        StringBuilder sb = new StringBuilder();
        
        sb.append("Hello");
        sb.append(" ");
        sb.append("World");
        sb.insert(5, ",");
        sb.replace(0, 5, "Hi");
        sb.delete(2, 4);
        sb.reverse();
        
        System.out.println("StringBuilder operations: " + sb.toString());
        System.out.println("Capacity: " + sb.capacity());
        System.out.println("Length: " + sb.length());
        
        // Convert back to String
        String result = sb.toString();
        
        System.out.println("\n=== STRING FORMATTING ===");
        
        // String formatting
        String name = "Alice";
        int age = 25;
        double salary = 50000.50;
        
        String formatted = String.format("Name: %s, Age: %d, Salary: $%,.2f", name, age, salary);
        System.out.println(formatted);
        
        // printf style
        System.out.printf("Name: %s, Age: %d, Salary: $%,.2f%n", name, age, salary);
        
        System.out.println("\n=== STRING POOL DEMONSTRATION ===");
        
        // String pool demonstration
        String a = "hello";
        String b = "hello";
        String c = new String("hello");
        String d = c.intern();
        
        System.out.println("a == b: " + (a == b));         // true (both in pool)
        System.out.println("a == c: " + (a == c));         // false (c is new object)
        System.out.println("a == d: " + (a == d));         // true (d is interned)
        
        System.out.println("\n=== CHARACTER ARRAY CONVERSION ===");
        
        // String to char array and vice versa
        String word = "Java";
        char[] chars = word.toCharArray();
        
        System.out.print("Character array: ");
        for (char ch : chars) {
            System.out.print(ch + " ");
        }
        System.out.println();
        
        String fromChars = new String(chars);
        System.out.println("Back to string: " + fromChars);
    }
}
Array & String Operations
Click buttons to see array and string operations

06. πŸ—οΈ Object-Oriented Programming

Java is a pure object-oriented programming language where everything is an object (except primitive types). OOP organizes software design around objects rather than functions and logic.

Four Pillars of OOP

  • Encapsulation: Bundling data with methods that operate on that data
  • Abstraction: Hiding complex implementation details
  • Inheritance: Creating new classes from existing ones
  • Polymorphism: Objects of different types can be accessed through the same interface

Classes and Objects

Class: Blueprint/template for creating objects
Object: Instance of a class with state and behavior
Constructor: Special method to initialize objects
Methods: Functions that define object behavior
Fields: Variables that define object state

Example: OOP Concepts in Java

// ========== ENCAPSULATION ==========
class BankAccount {
    // Private fields (data hiding)
    private String accountNumber;
    private String accountHolder;
    private double balance;
    
    // Constructor
    public BankAccount(String accountNumber, String accountHolder, double initialBalance) {
        this.accountNumber = accountNumber;
        this.accountHolder = accountHolder;
        this.balance = initialBalance;
    }
    
    // Getter methods (accessors)
    public String getAccountNumber() {
        return accountNumber;
    }
    
    public String getAccountHolder() {
        return accountHolder;
    }
    
    public double getBalance() {
        return balance;
    }
    
    // Setter methods (mutators) with validation
    public void setAccountHolder(String accountHolder) {
        if (accountHolder != null && !accountHolder.isEmpty()) {
            this.accountHolder = accountHolder;
        }
    }
    
    // Business logic methods
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("Deposited: $" + amount);
        }
    }
    
    public void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            System.out.println("Withdrawn: $" + amount);
        } else {
            System.out.println("Insufficient funds or invalid amount");
        }
    }
    
    // Display account info
    public void displayAccountInfo() {
        System.out.println("Account: " + accountNumber);
        System.out.println("Holder: " + accountHolder);
        System.out.println("Balance: $" + balance);
    }
}

// ========== ABSTRACTION ==========
abstract class Vehicle {
    // Abstract method (no implementation)
    public abstract void start();
    public abstract void stop();
    
    // Concrete method
    public void honk() {
        System.out.println("Beep beep!");
    }
}

class Car extends Vehicle {
    @Override
    public void start() {
        System.out.println("Car starting... Vroom!");
    }
    
    @Override
    public void stop() {
        System.out.println("Car stopping... Screech!");
    }
}

// Interface (pure abstraction)
interface ElectricVehicle {
    void charge();
    int getBatteryLevel();
}

// ========== INHERITANCE ==========
// Base class
class Employee {
    protected String name;
    protected int id;
    protected double salary;
    
    public Employee(String name, int id, double salary) {
        this.name = name;
        this.id = id;
        this.salary = salary;
    }
    
    public void work() {
        System.out.println(name + " is working");
    }
    
    public void displayInfo() {
        System.out.println("ID: " + id + ", Name: " + name + ", Salary: $" + salary);
    }
}

// Derived class
class Manager extends Employee {
    private String department;
    private int teamSize;
    
    public Manager(String name, int id, double salary, String department, int teamSize) {
        super(name, id, salary); // Call parent constructor
        this.department = department;
        this.teamSize = teamSize;
    }
    
    // Additional method
    public void conductMeeting() {
        System.out.println(name + " is conducting a meeting with " + teamSize + " team members");
    }
    
    // Method overriding
    @Override
    public void displayInfo() {
        super.displayInfo();
        System.out.println("Department: " + department + ", Team Size: " + teamSize);
    }
}

// ========== POLYMORPHISM ==========
interface Shape {
    double calculateArea();
    double calculatePerimeter();
}

class Circle implements Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
    
    @Override
    public double calculatePerimeter() {
        return 2 * Math.PI * radius;
    }
}

class Rectangle implements Shape {
    private double width;
    private double height;
    
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    @Override
    public double calculateArea() {
        return width * height;
    }
    
    @Override
    public double calculatePerimeter() {
        return 2 * (width + height);
    }
}

// ========== MAIN CLASS ==========
public class OOPDemo {
    public static void main(String[] args) {
        System.out.println("=== ENCAPSULATION DEMO ===");
        BankAccount account = new BankAccount("123456", "John Doe", 1000.0);
        account.displayAccountInfo();
        account.deposit(500);
        account.withdraw(200);
        account.displayAccountInfo();
        
        System.out.println("\n=== ABSTRACTION DEMO ===");
        Vehicle myCar = new Car();
        myCar.start();
        myCar.honk();
        myCar.stop();
        
        System.out.println("\n=== INHERITANCE DEMO ===");
        Employee emp = new Employee("Alice", 101, 50000);
        emp.work();
        emp.displayInfo();
        
        Manager mgr = new Manager("Bob", 102, 75000, "Engineering", 5);
        mgr.work();
        mgr.conductMeeting();
        mgr.displayInfo();
        
        // Polymorphism through inheritance
        Employee emp2 = new Manager("Charlie", 103, 80000, "Sales", 3);
        emp2.displayInfo(); // Calls Manager's overridden method
        
        System.out.println("\n=== POLYMORPHISM DEMO ===");
        Shape[] shapes = new Shape[2];
        shapes[0] = new Circle(5.0);
        shapes[1] = new Rectangle(4.0, 6.0);
        
        for (Shape shape : shapes) {
            System.out.println("Area: " + shape.calculateArea());
            System.out.println("Perimeter: " + shape.calculatePerimeter());
            System.out.println();
        }
        
        System.out.println("=== STATIC VS INSTANCE ===");
        Counter c1 = new Counter();
        Counter c2 = new Counter();
        
        c1.increment();
        c2.increment();
        c2.increment();
        
        System.out.println("c1 count: " + c1.getCount());
        System.out.println("c2 count: " + c2.getCount());
        System.out.println("Total count (static): " + Counter.getTotalCount());
        
        System.out.println("\n=== THIS KEYWORD DEMO ===");
        Person person = new Person("John", 25);
        person.display();
        
        System.out.println("\n=== FINAL KEYWORD ===");
        final int MAX_VALUE = 100;
        System.out.println("Final constant: " + MAX_VALUE);
        
        final Person finalPerson = new Person("Jane", 30);
        // finalPerson = new Person("New", 40); // Error: cannot reassign
        finalPerson.setAge(31); // OK: can modify object state
        
        System.out.println("\n=== ACCESS MODIFIERS ===");
        AccessDemo demo = new AccessDemo();
        // demo.privateVar = 10; // Error: private
        demo.publicVar = 20;    // OK: public
        demo.protectedVar = 30; // OK: same package
        demo.defaultVar = 40;   // OK: same package
        
        System.out.println("\n=== METHOD OVERLOADING ===");
        Calculator calc = new Calculator();
        System.out.println("Add ints: " + calc.add(5, 3));
        System.out.println("Add doubles: " + calc.add(5.5, 3.3));
        System.out.println("Add three: " + calc.add(1, 2, 3));
        System.out.println("Add strings: " + calc.add("Hello", "World"));
    }
}

// ========== ADDITIONAL DEMO CLASSES ==========

// Static vs instance demonstration
class Counter {
    private int count = 0;          // Instance variable
    private static int totalCount = 0; // Class variable
    
    public void increment() {
        count++;
        totalCount++;
    }
    
    public int getCount() {
        return count;
    }
    
    public static int getTotalCount() {
        return totalCount;
    }
}

// This keyword demonstration
class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;  // 'this' refers to current instance
        this.age = age;
    }
    
    public void setAge(int age) {
        this.age = age;    // Parameter shadows instance variable
    }
    
    public void display() {
        System.out.println("Name: " + this.name + ", Age: " + this.age);
    }
}

// Access modifiers
class AccessDemo {
    private int privateVar = 1;     // Only within class
    public int publicVar = 2;       // Accessible anywhere
    protected int protectedVar = 3; // Within package + subclasses
    int defaultVar = 4;             // Within package only (default/package-private)
}

// Method overloading
class Calculator {
    // Same method name, different parameters
    public int add(int a, int b) {
        return a + b;
    }
    
    public double add(double a, double b) {
        return a + b;
    }
    
    public int add(int a, int b, int c) {
        return a + b + c;
    }
    
    public String add(String a, String b) {
        return a + " " + b;
    }
}
OOP Concepts Demo
Click buttons to explore OOP concepts

07. 🌳 Inheritance & Polymorphism

Inheritance allows a class to inherit properties and methods from another class. Polymorphism enables objects of different classes to be treated as objects of a common super class.

Types of Inheritance in Java

  • Single Inheritance: One class extends another
  • Multilevel Inheritance: Chain of inheritance
  • Hierarchical Inheritance: Multiple classes extend one class
  • Multiple Inheritance: Not supported (use interfaces)
  • Hybrid Inheritance: Combination (via interfaces)

Polymorphism Types

1. Compile-time Polymorphism: Method overloading
2. Runtime Polymorphism: Method overriding
3. Interface Polymorphism: Implementing interfaces
4. Abstract Class Polymorphism: Extending abstract classes

Example: Inheritance and Polymorphism

// ========== INHERITANCE HIERARCHY ==========

// Base class
class Animal {
    protected String name;
    protected int age;
    
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public void eat() {
        System.out.println(name + " is eating");
    }
    
    public void sleep() {
        System.out.println(name + " is sleeping");
    }
    
    public void makeSound() {
        System.out.println(name + " makes a generic sound");
    }
    
    public void displayInfo() {
        System.out.println("Animal: " + name + ", Age: " + age);
    }
}

// Single inheritance
class Mammal extends Animal {
    protected boolean hasFur;
    protected String habitat;
    
    public Mammal(String name, int age, boolean hasFur, String habitat) {
        super(name, age); // Call parent constructor
        this.hasFur = hasFur;
        this.habitat = habitat;
    }
    
    // Additional method
    public void giveBirth() {
        System.out.println(name + " gives birth to live young");
    }
    
    // Method overriding
    @Override
    public void makeSound() {
        System.out.println(name + " makes mammal sound");
    }
    
    @Override
    public void displayInfo() {
        super.displayInfo();
        System.out.println("Type: Mammal, Has Fur: " + hasFur + ", Habitat: " + habitat);
    }
}

// Multilevel inheritance
class Dog extends Mammal {
    private String breed;
    private boolean isTrained;
    
    public Dog(String name, int age, String breed, boolean isTrained) {
        super(name, age, true, "Domestic"); // Call Mammal constructor
        this.breed = breed;
        this.isTrained = isTrained;
    }
    
    // Additional methods
    public void bark() {
        System.out.println(name + " barks: Woof! Woof!");
    }
    
    public void fetch() {
        System.out.println(name + " fetches the ball");
    }
    
    // Override parent method
    @Override
    public void makeSound() {
        bark();
    }
    
    @Override
    public void displayInfo() {
        super.displayInfo();
        System.out.println("Breed: " + breed + ", Trained: " + isTrained);
    }
}

// Hierarchical inheritance
class Cat extends Mammal {
    private String color;
    private boolean isIndoor;
    
    public Cat(String name, int age, String color, boolean isIndoor) {
        super(name, age, true, "Domestic");
        this.color = color;
        this.isIndoor = isIndoor;
    }
    
    public void meow() {
        System.out.println(name + " meows: Meow!");
    }
    
    public void purr() {
        System.out.println(name + " purrs: Purrr...");
    }
    
    @Override
    public void makeSound() {
        meow();
    }
    
    @Override
    public void displayInfo() {
        super.displayInfo();
        System.out.println("Color: " + color + ", Indoor: " + isIndoor);
    }
}

// Bird class (another hierarchical example)
class Bird extends Animal {
    protected boolean canFly;
    protected double wingspan;
    
    public Bird(String name, int age, boolean canFly, double wingspan) {
        super(name, age);
        this.canFly = canFly;
        this.wingspan = wingspan;
    }
    
    public void fly() {
        if (canFly) {
            System.out.println(name + " is flying");
        } else {
            System.out.println(name + " cannot fly");
        }
    }
    
    @Override
    public void makeSound() {
        System.out.println(name + " chirps");
    }
    
    @Override
    public void displayInfo() {
        super.displayInfo();
        System.out.println("Type: Bird, Can Fly: " + canFly + ", Wingspan: " + wingspan + "m");
    }
}

// ========== POLYMORPHISM DEMONSTRATION ==========

// Abstract class for polymorphism
abstract class Employee {
    protected String name;
    protected double salary;
    
    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
    
    // Abstract method (must be implemented by subclasses)
    public abstract void work();
    
    // Concrete method
    public void takeBreak() {
        System.out.println(name + " is taking a break");
    }
    
    // Can be overridden
    public void displayInfo() {
        System.out.println("Name: " + name + ", Salary: $" + salary);
    }
}

class Developer extends Employee {
    private String programmingLanguage;
    
    public Developer(String name, double salary, String programmingLanguage) {
        super(name, salary);
        this.programmingLanguage = programmingLanguage;
    }
    
    @Override
    public void work() {
        System.out.println(name + " is writing code in " + programmingLanguage);
    }
    
    @Override
    public void displayInfo() {
        super.displayInfo();
        System.out.println("Role: Developer, Language: " + programmingLanguage);
    }
}

class Manager extends Employee {
    private int teamSize;
    
    public Manager(String name, double salary, int teamSize) {
        super(name, salary);
        this.teamSize = teamSize;
    }
    
    @Override
    public void work() {
        System.out.println(name + " is managing a team of " + teamSize + " people");
    }
    
    @Override
    public void displayInfo() {
        super.displayInfo();
        System.out.println("Role: Manager, Team Size: " + teamSize);
    }
}

// Interface for multiple inheritance simulation
interface Swimmable {
    void swim();
    int getSwimSpeed();
}

interface Flyable {
    void fly();
    int getFlightSpeed();
}

// Class implementing multiple interfaces
class Duck extends Animal implements Swimmable, Flyable {
    public Duck(String name, int age) {
        super(name, age);
    }
    
    @Override
    public void swim() {
        System.out.println(name + " is swimming");
    }
    
    @Override
    public int getSwimSpeed() {
        return 5; // km/h
    }
    
    @Override
    public void fly() {
        System.out.println(name + " is flying");
    }
    
    @Override
    public int getFlightSpeed() {
        return 80; // km/h
    }
    
    @Override
    public void makeSound() {
        System.out.println(name + " quacks");
    }
    
    @Override
    public void displayInfo() {
        super.displayInfo();
        System.out.println("Type: Duck, Swim Speed: " + getSwimSpeed() + " km/h, Flight Speed: " + getFlightSpeed() + " km/h");
    }
}

// ========== MAIN CLASS ==========
public class InheritancePolymorphismDemo {
    public static void main(String[] args) {
        System.out.println("=== SINGLE INHERITANCE ===");
        Mammal mammal = new Mammal("Bear", 5, true, "Forest");
        mammal.displayInfo();
        mammal.makeSound();
        mammal.giveBirth();
        
        System.out.println("\n=== MULTILEVEL INHERITANCE ===");
        Dog dog = new Dog("Buddy", 3, "Golden Retriever", true);
        dog.displayInfo();
        dog.makeSound();
        dog.fetch();
        
        System.out.println("\n=== HIERARCHICAL INHERITANCE ===");
        Cat cat = new Cat("Whiskers", 2, "Tabby", true);
        Bird bird = new Bird("Tweety", 1, true, 0.5);
        
        cat.displayInfo();
        cat.makeSound();
        cat.purr();
        
        System.out.println();
        bird.displayInfo();
        bird.makeSound();
        bird.fly();
        
        System.out.println("\n=== RUNTIME POLYMORPHISM ===");
        // Upcasting
        Animal myPet1 = new Dog("Max", 4, "German Shepherd", false);
        Animal myPet2 = new Cat("Luna", 1, "Black", true);
        Animal myPet3 = new Bird("Polly", 2, true, 0.3);
        
        // All treated as Animals, but execute their own methods
        Animal[] animals = {myPet1, myPet2, myPet3};
        
        for (Animal animal : animals) {
            animal.makeSound(); // Polymorphic call
            System.out.println();
        }
        
        System.out.println("=== ABSTRACT CLASS POLYMORPHISM ===");
        Employee emp1 = new Developer("Alice", 75000, "Java");
        Employee emp2 = new Manager("Bob", 90000, 5);
        
        Employee[] employees = {emp1, emp2};
        
        for (Employee emp : employees) {
            emp.displayInfo();
            emp.work();
            emp.takeBreak();
            System.out.println();
        }
        
        System.out.println("=== INTERFACE POLYMORPHISM ===");
        Duck duck = new Duck("Donald", 3);
        duck.displayInfo();
        duck.swim();
        duck.fly();
        duck.makeSound();
        
        // Treating as interfaces
        Swimmable swimmer = duck;
        Flyable flyer = duck;
        
        swimmer.swim();
        System.out.println("Swim speed: " + swimmer.getSwimSpeed() + " km/h");
        flyer.fly();
        System.out.println("Flight speed: " + flyer.getFlightSpeed() + " km/h");
        
        System.out.println("\n=== METHOD OVERLOADING (COMPILE-TIME POLYMORPHISM) ===");
        MathOperations math = new MathOperations();
        
        System.out.println("Add ints: " + math.add(5, 3));
        System.out.println("Add doubles: " + math.add(5.5, 3.3));
        System.out.println("Add three: " + math.add(1, 2, 3));
        System.out.println("Add array: " + math.add(new int[]{1, 2, 3, 4, 5}));
        
        System.out.println("\n=== SUPER KEYWORD USAGE ===");
        Child child = new Child();
        child.display();
        
        System.out.println("\n=== FINAL CLASS AND METHODS ===");
        FinalDemo demo = new FinalDemo();
        demo.display();
        // Cannot extend FinalClass
    }
}

// ========== ADDITIONAL CLASSES ==========

// Method overloading example
class MathOperations {
    // Overloaded methods
    public int add(int a, int b) {
        return a + b;
    }
    
    public double add(double a, double b) {
        return a + b;
    }
    
    public int add(int a, int b, int c) {
        return a + b + c;
    }
    
    public int add(int[] numbers) {
        int sum = 0;
        for (int num : numbers) {
            sum += num;
        }
        return sum;
    }
}

// Super keyword demonstration
class Parent {
    protected String message = "Parent message";
    
    public void show() {
        System.out.println("Parent show() method");
    }
}

class Child extends Parent {
    private String message = "Child message";
    
    public void display() {
        System.out.println("Child message: " + message);
        System.out.println("Parent message: " + super.message); // Access parent field
        
        show(); // Calls Child's show() if overridden
        super.show(); // Always calls Parent's show()
    }
    
    @Override
    public void show() {
        System.out.println("Child show() method");
    }
}

// Final keyword
final class FinalClass {
    public void display() {
        System.out.println("This is a final class");
    }
}

class FinalDemo extends FinalClass { // Error: cannot extend final class
    // Cannot override final methods
    @Override
    public void display() {
        System.out.println("Trying to override");
    }
}
Inheritance & Polymorphism Demo
Click buttons to explore inheritance and polymorphism

08. ⚠️ Exception Handling

Exception handling is a mechanism to handle runtime errors so that normal flow of the application can be maintained. Java provides a robust exception handling framework.

Exception Hierarchy

  • Throwable: Root class of exception hierarchy
  • Error: Serious problems (out of memory, stack overflow)
  • Exception: Problems that can be handled
  • RuntimeException: Unchecked exceptions (NullPointerException)
  • Checked Exceptions: Must be handled or declared (IOException)

Exception Handling Keywords

1. try: Block of code to monitor for exceptions
2. catch: Block to handle specific exceptions
3. finally: Block that always executes (cleanup)
4. throw: To explicitly throw an exception
5. throws: Declares exceptions a method might throw

Example: Exception Handling in Java

import java.io.*;
import java.util.*;

public class ExceptionHandlingDemo {
    
    // ========== BASIC EXCEPTION HANDLING ==========
    public static void basicExceptionDemo() {
        System.out.println("=== BASIC EXCEPTION HANDLING ===");
        
        try {
            // Code that might throw an exception
            int[] numbers = {1, 2, 3};
            System.out.println("Accessing element at index 5: " + numbers[5]); // ArrayIndexOutOfBoundsException
            
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Exception caught: " + e.getMessage());
            System.out.println("Stack trace:");
            e.printStackTrace();
        }
        
        System.out.println("Program continues after exception handling");
    }
    
    // ========== MULTIPLE CATCH BLOCKS ==========
    public static void multipleCatchDemo() {
        System.out.println("\n=== MULTIPLE CATCH BLOCKS ===");
        
        Scanner scanner = new Scanner(System.in);
        
        try {
            System.out.print("Enter numerator: ");
            int numerator = scanner.nextInt();
            
            System.out.print("Enter denominator: ");
            int denominator = scanner.nextInt();
            
            int result = numerator / denominator; // Might throw ArithmeticException
            System.out.println("Result: " + result);
            
            String text = null;
            System.out.println("Text length: " + text.length()); // Might throw NullPointerException
            
        } catch (ArithmeticException e) {
            System.out.println("Arithmetic error: Cannot divide by zero!");
            
        } catch (NullPointerException e) {
            System.out.println("Null pointer error: Object is null!");
            
        } catch (InputMismatchException e) {
            System.out.println("Input error: Please enter valid integers!");
            scanner.nextLine(); // Clear invalid input
            
        } catch (Exception e) {
            System.out.println("Generic exception caught: " + e.getClass().getName());
            
        } finally {
            System.out.println("Finally block executed - cleanup code");
            scanner.close();
        }
    }
    
    // ========== CHECKED EXCEPTIONS ==========
    public static void checkedExceptionDemo() throws IOException {
        System.out.println("\n=== CHECKED EXCEPTIONS ===");
        
        BufferedReader reader = null;
        
        try {
            reader = new BufferedReader(new FileReader("test.txt"));
            String line;
            
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            
        } catch (FileNotFoundException e) {
            System.out.println("File not found: " + e.getMessage());
            throw new IOException("Custom error: File operation failed", e); // Re-throw
            
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    System.out.println("Error closing file: " + e.getMessage());
                }
            }
        }
    }
    
    // ========== CUSTOM EXCEPTIONS ==========
    static class InsufficientFundsException extends Exception {
        private double amount;
        private double balance;
        
        public InsufficientFundsException(double amount, double balance) {
            super("Insufficient funds: Attempted to withdraw $" + amount + " but balance is $" + balance);
            this.amount = amount;
            this.balance = balance;
        }
        
        public double getAmount() {
            return amount;
        }
        
        public double getBalance() {
            return balance;
        }
    }
    
    static class InvalidAccountException extends RuntimeException {
        public InvalidAccountException(String message) {
            super(message);
        }
    }
    
    static class BankAccount {
        private double balance;
        private String accountNumber;
        
        public BankAccount(String accountNumber, double initialBalance) {
            this.accountNumber = accountNumber;
            this.balance = initialBalance;
        }
        
        public void withdraw(double amount) throws InsufficientFundsException {
            if (amount > balance) {
                throw new InsufficientFundsException(amount, balance);
            }
            balance -= amount;
            System.out.println("Withdrawn: $" + amount + ", New balance: $" + balance);
        }
        
        public void transfer(BankAccount other, double amount) throws InsufficientFundsException {
            if (other == null) {
                throw new InvalidAccountException("Cannot transfer to null account");
            }
            
            this.withdraw(amount);
            other.deposit(amount);
            System.out.println("Transferred $" + amount + " to account " + other.accountNumber);
        }
        
        public void deposit(double amount) {
            if (amount <= 0) {
                throw new IllegalArgumentException("Deposit amount must be positive");
            }
            balance += amount;
        }
    }
    
    public static void customExceptionDemo() {
        System.out.println("\n=== CUSTOM EXCEPTIONS ===");
        
        BankAccount account1 = new BankAccount("12345", 1000);
        BankAccount account2 = new BankAccount("67890", 500);
        
        try {
            account1.withdraw(200);
            account1.withdraw(1000); // This will throw exception
            
        } catch (InsufficientFundsException e) {
            System.out.println("Custom exception caught: " + e.getMessage());
            System.out.println("Shortfall: $" + (e.getAmount() - e.getBalance()));
            
        }
        
        try {
            account1.transfer(null, 100); // Will throw runtime exception
            
        } catch (InvalidAccountException e) {
            System.out.println("Runtime exception caught: " + e.getMessage());
        }
        
        try {
            account2.deposit(-100); // Will throw IllegalArgumentException
            
        } catch (IllegalArgumentException e) {
            System.out.println("Illegal argument: " + e.getMessage());
        }
    }
    
    // ========== TRY-WITH-RESOURCES ==========
    public static void tryWithResourcesDemo() {
        System.out.println("\n=== TRY-WITH-RESOURCES (Java 7+) ===");
        
        // Auto-closable resources
        try (BufferedReader reader = new BufferedReader(new StringReader("Line 1\nLine 2\nLine 3"));
             BufferedWriter writer = new BufferedWriter(new StringWriter())) {
            
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println("Read: " + line);
                writer.write("Processed: " + line);
                writer.newLine();
            }
            
            System.out.println("Resources automatically closed");
            
        } catch (IOException e) {
            System.out.println("IO error: " + e.getMessage());
        }
    }
    
    // ========== EXCEPTION PROPAGATION ==========
    public static void methodA() throws IOException {
        System.out.println("In methodA");
        methodB();
    }
    
    public static void methodB() throws IOException {
        System.out.println("In methodB");
        throw new IOException("Error from methodB");
    }
    
    public static void exceptionPropagationDemo() {
        System.out.println("\n=== EXCEPTION PROPAGATION ===");
        
        try {
            methodA();
        } catch (IOException e) {
            System.out.println("Caught in main: " + e.getMessage());
        }
    }
    
    // ========== STACK TRACE MANIPULATION ==========
    public static void stackTraceDemo() {
        System.out.println("\n=== STACK TRACE MANIPULATION ===");
        
        try {
            throw new Exception("Test exception");
            
        } catch (Exception e) {
            System.out.println("Original stack trace:");
            e.printStackTrace();
            
            // Get stack trace elements
            StackTraceElement[] stackTrace = e.getStackTrace();
            System.out.println("\nStack trace elements:");
            for (StackTraceElement element : stackTrace) {
                System.out.println("  " + element.getClassName() + "." + element.getMethodName() + 
                                 " (Line " + element.getLineNumber() + ")");
            }
            
            // Create new exception with cause
            Exception wrapped = new Exception("Wrapped exception", e);
            System.out.println("\nWrapped exception cause: " + wrapped.getCause().getMessage());
        }
    }
    
    // ========== MAIN METHOD ==========
    public static void main(String[] args) {
        
        // Basic exception handling
        basicExceptionDemo();
        
        // Multiple catch blocks
        multipleCatchDemo();
        
        // Custom exceptions
        customExceptionDemo();
        
        // Try-with-resources
        tryWithResourcesDemo();
        
        // Exception propagation
        exceptionPropagationDemo();
        
        // Stack trace manipulation
        stackTraceDemo();
        
        // Checked exceptions (handled in main)
        try {
            checkedExceptionDemo();
        } catch (IOException e) {
            System.out.println("Main caught: " + e.getMessage());
        }
        
        System.out.println("\n=== EXCEPTION BEST PRACTICES ===");
        System.out.println("1. Catch specific exceptions first");
        System.out.println("2. Don't catch Throwable or Exception unless necessary");
        System.out.println("3. Clean up resources in finally blocks");
        System.out.println("4. Use try-with-resources for AutoCloseable objects");
        System.out.println("5. Don't suppress exceptions");
        System.out.println("6. Include meaningful messages in custom exceptions");
        System.out.println("7. Consider checked vs unchecked exceptions carefully");
    }
}

// ========== ADDITIONAL EXCEPTION CLASSES ==========

// AutoCloseable resource
class DatabaseConnection implements AutoCloseable {
    private String connectionId;
    private boolean isOpen = true;
    
    public DatabaseConnection(String connectionId) {
        this.connectionId = connectionId;
        System.out.println("Database connection " + connectionId + " opened");
    }
    
    public void executeQuery(String query) throws SQLException {
        if (!isOpen) {
            throw new SQLException("Connection closed");
        }
        System.out.println("Executing query: " + query);
        
        // Simulate error
        if (query.contains("DROP")) {
            throw new SQLException("DROP not allowed");
        }
    }
    
    @Override
    public void close() {
        isOpen = false;
        System.out.println("Database connection " + connectionId + " closed");
    }
}

// Simulated SQLException for demonstration
class SQLException extends Exception {
    public SQLException(String message) {
        super(message);
    }
}

// Exception chaining example
class DataProcessor {
    public void processData(String data) throws ProcessingException {
        try {
            // Some processing that might fail
            if (data == null) {
                throw new IllegalArgumentException("Data cannot be null");
            }
            
            int result = Integer.parseInt(data); // Might throw NumberFormatException
            
        } catch (IllegalArgumentException | NumberFormatException e) {
            // Chain exceptions
            throw new ProcessingException("Failed to process data", e);
        }
    }
}

class ProcessingException extends Exception {
    public ProcessingException(String message, Throwable cause) {
        super(message, cause);
    }
}
Exception Handling Demo
Click buttons to learn about exception handling

09. πŸ“š Collections Framework

The Java Collections Framework provides a set of interfaces and classes for storing and manipulating groups of data as a single unit. It's one of the most important parts of Java.

Collection Interfaces

  • Collection: Root interface (List, Set, Queue)
  • List: Ordered collection with duplicates (ArrayList, LinkedList)
  • Set: No duplicates (HashSet, TreeSet)
  • Queue: FIFO order (PriorityQueue, LinkedList)
  • Map: Key-value pairs (HashMap, TreeMap)
  • SortedSet: Sorted set (TreeSet)
  • SortedMap: Sorted map (TreeMap)

Key Collection Classes

Common implementations:
1. ArrayList: Resizable array, fast random access
2. LinkedList: Doubly-linked list, fast insert/delete
3. HashSet: Hash table implementation of Set
4. TreeSet: Red-black tree, sorted order
5. HashMap: Hash table implementation of Map
6. TreeMap: Red-black tree, sorted by keys

Example: Collections Framework

import java.util.*;
import java.util.stream.Collectors;

public class CollectionsFrameworkDemo {
    
    public static void main(String[] args) {
        
        System.out.println("=== LIST INTERFACE ===");
        listDemo();
        
        System.out.println("\n=== SET INTERFACE ===");
        setDemo();
        
        System.out.println("\n=== MAP INTERFACE ===");
        mapDemo();
        
        System.out.println("\n=== QUEUE INTERFACE ===");
        queueDemo();
        
        System.out.println("\n=== ITERATORS ===");
        iteratorDemo();
        
        System.out.println("\n=== COLLECTIONS UTILITY CLASS ===");
        collectionsUtilityDemo();
        
        System.out.println("\n=== COMPARABLE & COMPARATOR ===");
        comparatorDemo();
        
        System.out.println("\n=== JAVA 8+ STREAMS WITH COLLECTIONS ===");
        streamsDemo();
    }
    
    // ========== LIST DEMONSTRATION ==========
    static void listDemo() {
        // ArrayList - most commonly used
        List arrayList = new ArrayList<>();
        arrayList.add("Apple");
        arrayList.add("Banana");
        arrayList.add("Cherry");
        arrayList.add("Banana"); // Duplicates allowed
        
        System.out.println("ArrayList: " + arrayList);
        System.out.println("Size: " + arrayList.size());
        System.out.println("Contains 'Apple': " + arrayList.contains("Apple"));
        System.out.println("Element at index 1: " + arrayList.get(1));
        
        // Modify
        arrayList.set(1, "Blueberry");
        arrayList.remove(2);
        System.out.println("After modifications: " + arrayList);
        
        // LinkedList - good for frequent insertions/deletions
        List linkedList = new LinkedList<>();
        linkedList.add(10);
        linkedList.add(20);
        linkedList.add(30);
        linkedList.addFirst(5);
        linkedList.addLast(40);
        
        System.out.println("\nLinkedList: " + linkedList);
        
        // Vector (thread-safe but slower)
        List vector = new Vector<>();
        vector.add("One");
        vector.add("Two");
        System.out.println("Vector: " + vector);
        
        // Stack (LIFO)
        Stack stack = new Stack<>();
        stack.push("First");
        stack.push("Second");
        stack.push("Third");
        System.out.println("Stack: " + stack);
        System.out.println("Popped: " + stack.pop());
        System.out.println("Peek: " + stack.peek());
        System.out.println("Stack after pop: " + stack);
    }
    
    // ========== SET DEMONSTRATION ==========
    static void setDemo() {
        // HashSet - no order, no duplicates
        Set hashSet = new HashSet<>();
        hashSet.add("Apple");
        hashSet.add("Banana");
        hashSet.add("Cherry");
        hashSet.add("Apple"); // Duplicate ignored
        
        System.out.println("HashSet: " + hashSet);
        System.out.println("Size: " + hashSet.size());
        
        // LinkedHashSet - maintains insertion order
        Set linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add("Zebra");
        linkedHashSet.add("Apple");
        linkedHashSet.add("Banana");
        
        System.out.println("LinkedHashSet (insertion order): " + linkedHashSet);
        
        // TreeSet - sorted order
        Set treeSet = new TreeSet<>();
        treeSet.add("Zebra");
        treeSet.add("Apple");
        treeSet.add("Banana");
        
        System.out.println("TreeSet (sorted): " + treeSet);
        
        // TreeSet with custom comparator
        Set treeSetReverse = new TreeSet<>(Collections.reverseOrder());
        treeSetReverse.addAll(treeSet);
        System.out.println("TreeSet (reverse order): " + treeSetReverse);
        
        // Set operations
        Set set1 = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
        Set set2 = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8));
        
        // Union
        Set union = new HashSet<>(set1);
        union.addAll(set2);
        System.out.println("Set1 βˆͺ Set2: " + union);
        
        // Intersection
        Set intersection = new HashSet<>(set1);
        intersection.retainAll(set2);
        System.out.println("Set1 ∩ Set2: " + intersection);
        
        // Difference
        Set difference = new HashSet<>(set1);
        difference.removeAll(set2);
        System.out.println("Set1 - Set2: " + difference);
    }
    
    // ========== MAP DEMONSTRATION ==========
    static void mapDemo() {
        // HashMap - most commonly used
        Map hashMap = new HashMap<>();
        hashMap.put("John", 25);
        hashMap.put("Alice", 30);
        hashMap.put("Bob", 28);
        hashMap.put("John", 26); // Overwrites previous value
        
        System.out.println("HashMap: " + hashMap);
        System.out.println("Size: " + hashMap.size());
        System.out.println("Get Alice's age: " + hashMap.get("Alice"));
        System.out.println("Contains key 'Bob': " + hashMap.containsKey("Bob"));
        System.out.println("Contains value 30: " + hashMap.containsValue(30));
        
        // Iterating through map
        System.out.println("\nIterating HashMap:");
        for (Map.Entry entry : hashMap.entrySet()) {
            System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
        }
        
        // LinkedHashMap - maintains insertion order
        Map linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put("Zebra", 5);
        linkedHashMap.put("Apple", 3);
        linkedHashMap.put("Banana", 2);
        
        System.out.println("\nLinkedHashMap (insertion order): " + linkedHashMap);
        
        // TreeMap - sorted by keys
        Map treeMap = new TreeMap<>();
        treeMap.put("Zebra", 5);
        treeMap.put("Apple", 3);
        treeMap.put("Banana", 2);
        
        System.out.println("TreeMap (sorted keys): " + treeMap);
        
        // TreeMap with custom comparator
        Map treeMapReverse = new TreeMap<>(Collections.reverseOrder());
        treeMapReverse.putAll(treeMap);
        System.out.println("TreeMap (reverse order): " + treeMapReverse);
        
        // Map operations
        Map map1 = new HashMap<>();
        map1.put("A", 1);
        map1.put("B", 2);
        
        Map map2 = new HashMap<>();
        map2.put("B", 3);
        map2.put("C", 4);
        
        // Merge maps
        Map merged = new HashMap<>(map1);
        map2.forEach((key, value) -> 
            merged.merge(key, value, (v1, v2) -> v1 + v2));
        
        System.out.println("\nMerged map (sum values on conflict): " + merged);
    }
    
    // ========== QUEUE DEMONSTRATION ==========
    static void queueDemo() {
        // LinkedList as Queue (FIFO)
        Queue queue = new LinkedList<>();
        queue.add("First");
        queue.add("Second");
        queue.add("Third");
        
        System.out.println("Queue: " + queue);
        System.out.println("Peek: " + queue.peek());
        System.out.println("Poll: " + queue.poll());
        System.out.println("Queue after poll: " + queue);
        
        // PriorityQueue - natural ordering or custom comparator
        Queue priorityQueue = new PriorityQueue<>();
        priorityQueue.add(30);
        priorityQueue.add(10);
        priorityQueue.add(20);
        
        System.out.println("\nPriorityQueue (natural order):");
        while (!priorityQueue.isEmpty()) {
            System.out.print(priorityQueue.poll() + " ");
        }
        System.out.println();
        
        // PriorityQueue with custom comparator (max heap)
        Queue maxHeap = new PriorityQueue<>(Collections.reverseOrder());
        maxHeap.add(30);
        maxHeap.add(10);
        maxHeap.add(20);
        
        System.out.println("PriorityQueue (max heap):");
        while (!maxHeap.isEmpty()) {
            System.out.print(maxHeap.poll() + " ");
        }
        System.out.println();
        
        // Deque (Double-ended queue)
        Deque deque = new ArrayDeque<>();
        deque.addFirst("Front");
        deque.addLast("Middle");
        deque.addLast("Back");
        
        System.out.println("\nDeque: " + deque);
        System.out.println("Remove first: " + deque.removeFirst());
        System.out.println("Remove last: " + deque.removeLast());
        System.out.println("Deque after removal: " + deque);
    }
    
    // ========== ITERATOR DEMONSTRATION ==========
    static void iteratorDemo() {
        List fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Cherry", "Date"));
        
        // Traditional iterator
        System.out.println("Using Iterator:");
        Iterator iterator = fruits.iterator();
        while (iterator.hasNext()) {
            String fruit = iterator.next();
            if (fruit.equals("Banana")) {
                iterator.remove(); // Safe removal
            }
            System.out.println(fruit);
        }
        System.out.println("After removal: " + fruits);
        
        // ListIterator (bidirectional)
        List colors = new ArrayList<>(Arrays.asList("Red", "Green", "Blue"));
        ListIterator listIterator = colors.listIterator();
        
        System.out.println("\nUsing ListIterator (forward):");
        while (listIterator.hasNext()) {
            System.out.println(listIterator.next());
        }
        
        System.out.println("Using ListIterator (backward):");
        while (listIterator.hasPrevious()) {
            System.out.println(listIterator.previous());
        }
        
        // Enhanced for loop (internally uses iterator)
        System.out.println("\nEnhanced for loop:");
        for (String color : colors) {
            System.out.println(color);
        }
        
        // Java 8+ forEach
        System.out.println("\nJava 8 forEach:");
        colors.forEach(color -> System.out.println(color));
    }
    
    // ========== COLLECTIONS UTILITY CLASS ==========
    static void collectionsUtilityDemo() {
        List numbers = new ArrayList<>(Arrays.asList(5, 2, 8, 1, 9, 3, 7, 4, 6));
        
        System.out.println("Original list: " + numbers);
        
        // Sorting
        Collections.sort(numbers);
        System.out.println("Sorted: " + numbers);
        
        // Reverse
        Collections.reverse(numbers);
        System.out.println("Reversed: " + numbers);
        
        // Shuffle
        Collections.shuffle(numbers);
        System.out.println("Shuffled: " + numbers);
        
        // Binary search (list must be sorted)
        Collections.sort(numbers);
        int index = Collections.binarySearch(numbers, 7);
        System.out.println("Binary search for 7: index " + index);
        
        // Min and max
        System.out.println("Min: " + Collections.min(numbers));
        System.out.println("Max: " + Collections.max(numbers));
        
        // Fill
        List list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));
        Collections.fill(list, "X");
        System.out.println("Filled list: " + list);
        
        // Frequency
        List words = Arrays.asList("apple", "banana", "apple", "cherry", "apple");
        int frequency = Collections.frequency(words, "apple");
        System.out.println("Frequency of 'apple': " + frequency);
        
        // Disjoint (no common elements)
        List list1 = Arrays.asList(1, 2, 3);
        List list2 = Arrays.asList(4, 5, 6);
        List list3 = Arrays.asList(3, 4, 5);
        
        System.out.println("list1 and list2 disjoint? " + Collections.disjoint(list1, list2));
        System.out.println("list1 and list3 disjoint? " + Collections.disjoint(list1, list3));
        
        // Synchronized collections (thread-safe)
        List syncList = Collections.synchronizedList(new ArrayList<>());
        Map syncMap = Collections.synchronizedMap(new HashMap<>());
        System.out.println("Created synchronized collections");
    }
    
    // ========== COMPARABLE & COMPARATOR ==========
    static void comparatorDemo() {
        // Comparable example (natural ordering)
        List students = new ArrayList<>();
        students.add(new Student("Alice", 101, 85.5));
        students.add(new Student("Bob", 102, 92.0));
        students.add(new Student("Charlie", 103, 78.5));
        
        Collections.sort(students); // Uses compareTo from Student class
        System.out.println("Students sorted by ID (natural order):");
        students.forEach(System.out::println);
        
        // Comparator example (custom ordering)
        Comparator byName = Comparator.comparing(Student::getName);
        Comparator byGrade = Comparator.comparing(Student::getGrade).reversed();
        Comparator byNameThenGrade = byName.thenComparing(byGrade);
        
        Collections.sort(students, byGrade);
        System.out.println("\nStudents sorted by grade (descending):");
        students.forEach(System.out::println);
        
        Collections.sort(students, byNameThenGrade);
        System.out.println("\nStudents sorted by name then grade:");
        students.forEach(System.out::println);
        
        // Comparator with lambda
        Comparator byId = (s1, s2) -> Integer.compare(s1.getId(), s2.getId());
        Collections.sort(students, byId);
        System.out.println("\nStudents sorted by ID (using lambda):");
        students.forEach(System.out::println);
    }
    
    static class Student implements Comparable {
        private String name;
        private int id;
        private double grade;
        
        public Student(String name, int id, double grade) {
            this.name = name;
            this.id = id;
            this.grade = grade;
        }
        
        // Natural ordering by ID
        @Override
        public int compareTo(Student other) {
            return Integer.compare(this.id, other.id);
        }
        
        // Getters
        public String getName() { return name; }
        public int getId() { return id; }
        public double getGrade() { return grade; }
        
        @Override
        public String toString() {
            return String.format("Student{name='%s', id=%d, grade=%.1f}", name, id, grade);
        }
    }
    
    // ========== JAVA 8+ STREAMS ==========
    static void streamsDemo() {
        List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        
        System.out.println("Original numbers: " + numbers);
        
        // Filter and collect
        List evenNumbers = numbers.stream()
            .filter(n -> n % 2 == 0)
            .collect(Collectors.toList());
        System.out.println("Even numbers: " + evenNumbers);
        
        // Map (transform)
        List squared = numbers.stream()
            .map(n -> n * n)
            .collect(Collectors.toList());
        System.out.println("Squared numbers: " + squared);
        
        // Reduce
        int sum = numbers.stream()
            .reduce(0, Integer::sum);
        System.out.println("Sum: " + sum);
        
        // Max/Min
        Optional max = numbers.stream()
            .max(Integer::compare);
        System.out.println("Max: " + max.orElse(0));
        
        // Count
        long count = numbers.stream()
            .filter(n -> n > 5)
            .count();
        System.out.println("Count > 5: " + count);
        
        // AnyMatch/AllMatch/NoneMatch
        boolean anyEven = numbers.stream()
            .anyMatch(n -> n % 2 == 0);
        System.out.println("Any even? " + anyEven);
        
        // Grouping
        List words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
        Map> byLength = words.stream()
            .collect(Collectors.groupingBy(String::length));
        System.out.println("Words grouped by length: " + byLength);
        
        // Parallel stream
        List parallelProcessed = numbers.parallelStream()
            .map(n -> n * 2)
            .collect(Collectors.toList());
        System.out.println("Parallel processed: " + parallelProcessed);
    }
}
Collections Framework Demo
Click buttons to explore collections framework

10. ⚑ Advanced Java Concepts

Advanced Java concepts build upon the fundamentals to create robust, efficient, and scalable applications. These features are essential for enterprise-level development.

Advanced Topics Covered

  • Multithreading & Concurrency: Parallel execution
  • Java I/O & NIO: File and network operations
  • Generics: Type-safe collections
  • Annotations: Metadata for code
  • Lambda Expressions: Functional programming
  • Streams API: Functional operations on collections
  • JDBC: Database connectivity
  • Java Modules: Modular programming (Java 9+)

Enterprise Java Features

Modern Java development includes:
1. Spring Framework: Dependency injection, MVC
2. JPA/Hibernate: Object-relational mapping
3. Microservices: Spring Boot, REST APIs
4. Testing: JUnit, Mockito
5. Build Tools: Maven, Gradle

Example: Advanced Java Concepts

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;
import java.io.*;
import java.nio.file.*;
import java.sql.*;
import java.time.*;
import java.util.function.*;

public class AdvancedJavaConcepts {
    
    public static void main(String[] args) throws Exception {
        
        System.out.println("=== MULTITHREADING & CONCURRENCY ===");
        multithreadingDemo();
        
        System.out.println("\n=== GENERICS ===");
        genericsDemo();
        
        System.out.println("\n=== LAMBDA EXPRESSIONS ===");
        lambdaDemo();
        
        System.out.println("\n=== STREAMS API ===");
        streamsApiDemo();
        
        System.out.println("\n=== OPTIONAL CLASS ===");
        optionalDemo();
        
        System.out.println("\n=== DATE-TIME API (Java 8+) ===");
        dateTimeDemo();
        
        System.out.println("\n=== ANNOTATIONS ===");
        annotationsDemo();
        
        System.out.println("\n=== JAVA I/O & NIO ===");
        ioDemo();
        
        System.out.println("\n=== JDBC (DATABASE CONNECTIVITY) ===");
        jdbcDemo();
        
        System.out.println("\n=== JAVA MODULES (Java 9+) ===");
        modulesDemo();
    }
    
    // ========== MULTITHREADING ==========
    static void multithreadingDemo() throws InterruptedException, ExecutionException {
        // Thread creation
        Thread thread1 = new Thread(() -> {
            for (int i = 1; i <= 3; i++) {
                System.out.println("Thread-1: " + i);
                try { Thread.sleep(500); } catch (InterruptedException e) {}
            }
        });
        
        Thread thread2 = new Thread(() -> {
            for (int i = 1; i <= 3; i++) {
                System.out.println("Thread-2: " + i);
                try { Thread.sleep(300); } catch (InterruptedException e) {}
            }
        });
        
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        
        // Thread pool with ExecutorService
        ExecutorService executor = Executors.newFixedThreadPool(3);
        
        List> futures = new ArrayList<>();
        for (int i = 1; i <= 5; i++) {
            final int taskId = i;
            futures.add(executor.submit(() -> {
                System.out.println("Task " + taskId + " executed by " + Thread.currentThread().getName());
                return taskId * 10;
            }));
        }
        
        // Get results
        for (Future future : futures) {
            System.out.println("Task result: " + future.get());
        }
        
        executor.shutdown();
        
        // Synchronization example
        Counter counter = new Counter();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });
        
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });
        
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        
        System.out.println("Final count (with synchronization): " + counter.getCount());
        
        // Concurrent collections
        ConcurrentHashMap concurrentMap = new ConcurrentHashMap<>();
        concurrentMap.put("A", 1);
        concurrentMap.put("B", 2);
        concurrentMap.putIfAbsent("A", 3); // Won't replace
        
        System.out.println("ConcurrentHashMap: " + concurrentMap);
    }
    
    static class Counter {
        private int count = 0;
        
        public synchronized void increment() {
            count++;
        }
        
        public int getCount() {
            return count;
        }
    }
    
    // ========== GENERICS ==========
    static void genericsDemo() {
        // Generic class
        Box intBox = new Box<>(42);
        Box stringBox = new Box<>("Hello Generics");
        
        System.out.println("Integer Box: " + intBox.get());
        System.out.println("String Box: " + stringBox.get());
        
        // Generic method
        Integer[] intArray = {1, 2, 3, 4, 5};
        String[] strArray = {"A", "B", "C", "D"};
        
        System.out.println("Integer array contains 3: " + contains(intArray, 3));
        System.out.println("String array contains 'X': " + contains(strArray, "X"));
        
        // Bounded type parameters
        System.out.println("Max of 5, 10: " + maximum(5, 10));
        System.out.println("Max of 3.14, 2.71: " + maximum(3.14, 2.71));
        
        // Wildcards
        List intList = Arrays.asList(1, 2, 3);
        List doubleList = Arrays.asList(1.1, 2.2, 3.3);
        List numberList = Arrays.asList(1, 2.5, 3);
        
        System.out.println("Sum of integers: " + sumOfList(intList));
        System.out.println("Sum of doubles: " + sumOfList(doubleList));
        System.out.println("Sum of numbers: " + sumOfList(numberList));
        
        // Generic class with multiple type parameters
        Pair pair = new Pair<>("Age", 25);
        System.out.println("Pair: " + pair.getKey() + " = " + pair.getValue());
    }
    
    // Generic class
    static class Box {
        private T value;
        
        public Box(T value) {
            this.value = value;
        }
        
        public T get() {
            return value;
        }
        
        public void set(T value) {
            this.value = value;
        }
    }
    
    // Generic method
    static  boolean contains(T[] array, T element) {
        for (T item : array) {
            if (item.equals(element)) {
                return true;
            }
        }
        return false;
    }
    
    // Bounded type parameter
    static > T maximum(T a, T b) {
        return a.compareTo(b) > 0 ? a : b;
    }
    
    // Wildcard method
    static double sumOfList(List<? extends Number> list) {
        double sum = 0.0;
        for (Number num : list) {
            sum += num.doubleValue();
        }
        return sum;
    }
    
    // Multiple type parameters
    static class Pair {
        private K key;
        private V value;
        
        public Pair(K key, V value) {
            this.key = key;
            this.value = value;
        }
        
        public K getKey() { return key; }
        public V getValue() { return value; }
    }
    
    // ========== LAMBDA EXPRESSIONS ==========
    static void lambdaDemo() {
        // Traditional way (anonymous class)
        Runnable oldWay = new Runnable() {
            @Override
            public void run() {
                System.out.println("Running old way");
            }
        };
        
        // Lambda way
        Runnable newWay = () -> System.out.println("Running new way");
        
        oldWay.run();
        newWay.run();
        
        // Functional interfaces with lambdas
        Calculator add = (a, b) -> a + b;
        Calculator multiply = (a, b) -> a * b;
        
        System.out.println("5 + 3 = " + add.calculate(5, 3));
        System.out.println("5 * 3 = " + multiply.calculate(5, 3));
        
        // Method references
        List names = Arrays.asList("Alice", "Bob", "Charlie");
        names.forEach(System.out::println);
        
        // Comparator with lambda
        List people = Arrays.asList(
            new Person("Alice", 25),
            new Person("Bob", 30),
            new Person("Charlie", 20)
        );
        
        // Sort by age
        people.sort((p1, p2) -> Integer.compare(p1.getAge(), p2.getAge()));
        System.out.println("Sorted by age: " + people);
        
        // Sort by name
        people.sort(Comparator.comparing(Person::getName));
        System.out.println("Sorted by name: " + people);
        
        // Predicate (returns boolean)
        Predicate isEven = n -> n % 2 == 0;
        System.out.println("Is 4 even? " + isEven.test(4));
        
        // Function (transforms input)
        Function stringLength = String::length;
        System.out.println("Length of 'Hello': " + stringLength.apply("Hello"));
        
        // Consumer (accepts input, returns void)
        Consumer printer = System.out::println;
        printer.accept("Hello Consumer!");
        
        // Supplier (provides values)
        Supplier randomSupplier = Math::random;
        System.out.println("Random number: " + randomSupplier.get());
    }
    
    // Functional interface
    @FunctionalInterface
    interface Calculator {
        int calculate(int a, int b);
    }
    
    static class Person {
        private String name;
        private int age;
        
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        
        public String getName() { return name; }
        public int getAge() { return age; }
        
        @Override
        public String toString() {
            return name + "(" + age + ")";
        }
    }
    
    // ========== STREAMS API ==========
    static void streamsApiDemo() {
        List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        
        // Intermediate operations (lazy)
        Stream stream = numbers.stream()
            .filter(n -> n % 2 == 0)      // Even numbers
            .map(n -> n * 2)              // Double them
            .sorted(Comparator.reverseOrder()); // Sort descending
        
        // Terminal operation (eager)
        List result = stream.collect(Collectors.toList());
        System.out.println("Processed numbers: " + result);
        
        // More stream operations
        long count = numbers.stream().count();
        System.out.println("Count: " + count);
        
        int sum = numbers.stream()
            .mapToInt(Integer::intValue)
            .sum();
        System.out.println("Sum: " + sum);
        
        Optional max = numbers.stream().max(Integer::compare);
        max.ifPresent(m -> System.out.println("Max: " + m));
        
        // Grouping
        List words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
        Map> byLength = words.stream()
            .collect(Collectors.groupingBy(String::length));
        System.out.println("Words by length: " + byLength);
        
        // Partitioning
        Map> partitioned = numbers.stream()
            .collect(Collectors.partitioningBy(n -> n > 5));
        System.out.println("Numbers > 5: " + partitioned.get(true));
        System.out.println("Numbers <= 5: " + partitioned.get(false));
        
        // Reduce
        Optional product = numbers.stream()
            .reduce((a, b) -> a * b);
        System.out.println("Product: " + product.orElse(0));
        
        // Parallel stream
        long parallelCount = numbers.parallelStream()
            .filter(n -> n % 2 == 0)
            .count();
        System.out.println("Even numbers (parallel): " + parallelCount);
    }
    
    // ========== OPTIONAL CLASS ==========
    static void optionalDemo() {
        // Creating Optionals
        Optional empty = Optional.empty();
        Optional present = Optional.of("Hello");
        Optional nullable = Optional.ofNullable(null);
        
        System.out.println("Empty optional: " + empty);
        System.out.println("Present optional: " + present);
        System.out.println("Nullable optional: " + nullable);
        
        // Checking and getting
        if (present.isPresent()) {
            System.out.println("Value: " + present.get());
        }
        
        // Default values
        String value1 = empty.orElse("Default");
        String value2 = present.orElse("Default");
        System.out.println("Empty orElse: " + value1);
        System.out.println("Present orElse: " + value2);
        
        // orElseGet with Supplier
        String value3 = empty.orElseGet(() -> "Generated default");
        System.out.println("orElseGet: " + value3);
        
        // orElseThrow
        try {
            String value4 = empty.orElseThrow(() -> new IllegalArgumentException("No value"));
        } catch (IllegalArgumentException e) {
            System.out.println("orElseThrow caught: " + e.getMessage());
        }
        
        // ifPresent
        present.ifPresent(val -> System.out.println("Value exists: " + val));
        
        // map and filter
        Optional upper = present.map(String::toUpperCase);
        System.out.println("Mapped to uppercase: " + upper.orElse("none"));
        
        Optional filtered = present.filter(val -> val.length() > 10);
        System.out.println("Filtered (length > 10): " + filtered.orElse("none"));
        
        // flatMap
        Optional> nested = Optional.of(Optional.of("Nested"));
        Optional flattened = nested.flatMap(Function.identity());
        System.out.println("Flattened: " + flattened.orElse("none"));
    }
    
    // ========== DATE-TIME API ==========
    static void dateTimeDemo() {
        // Current dates/times
        LocalDate today = LocalDate.now();
        LocalTime now = LocalTime.now();
        LocalDateTime current = LocalDateTime.now();
        
        System.out.println("Today: " + today);
        System.out.println("Now: " + now);
        System.out.println("Current: " + current);
        
        // Creating specific dates
        LocalDate birthday = LocalDate.of(1990, Month.JANUARY, 15);
        LocalTime meetingTime = LocalTime.of(14, 30);
        LocalDateTime event = LocalDateTime.of(2024, 12, 25, 18, 0);
        
        System.out.println("Birthday: " + birthday);
        System.out.println("Meeting: " + meetingTime);
        System.out.println("Event: " + event);
        
        // Manipulating dates
        LocalDate nextWeek = today.plusWeeks(1);
        LocalDate lastMonth = today.minusMonths(1);
        LocalDate nextYear = today.plusYears(1);
        
        System.out.println("Next week: " + nextWeek);
        System.out.println("Last month: " + lastMonth);
        System.out.println("Next year: " + nextYear);
        
        // Comparing dates
        boolean isBefore = birthday.isBefore(today);
        boolean isAfter = nextWeek.isAfter(today);
        
        System.out.println("Birthday is before today? " + isBefore);
        System.out.println("Next week is after today? " + isAfter);
        
        // Period and Duration
        Period age = Period.between(birthday, today);
        System.out.println("Age: " + age.getYears() + " years, " + 
                          age.getMonths() + " months, " + 
                          age.getDays() + " days");
        
        // Formatting
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
        String formatted = current.format(formatter);
        System.out.println("Formatted: " + formatted);
        
        // Parsing
        LocalDateTime parsed = LocalDateTime.parse("25/12/2024 18:00", formatter);
        System.out.println("Parsed: " + parsed);
        
        // Time zones
        ZonedDateTime zoned = ZonedDateTime.now(ZoneId.of("America/New_York"));
        System.out.println("New York time: " + zoned);
        
        // Instant (machine time)
        Instant instant = Instant.now();
        System.out.println("Instant: " + instant);
    }
    
    // ========== ANNOTATIONS ==========
    static void annotationsDemo() {
        // Using annotations
        MyClass obj = new MyClass();
        obj.deprecatedMethod();
        obj.newMethod();
        
        // Reflection to read annotations
        Class<?> clazz = MyClass.class;
        
        if (clazz.isAnnotationPresent(Author.class)) {
            Author author = clazz.getAnnotation(Author.class);
            System.out.println("Class author: " + author.name() + ", version: " + author.version());
        }
        
        // Method annotations
        try {
            Method method = clazz.getMethod("deprecatedMethod");
            if (method.isAnnotationPresent(Deprecated.class)) {
                System.out.println("deprecatedMethod is marked as @Deprecated");
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
    
    // Custom annotation
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @interface Author {
        String name();
        String version() default "1.0";
    }
    
    @Author(name = "John Doe", version = "2.0")
    static class MyClass {
        
        @Deprecated
        public void deprecatedMethod() {
            System.out.println("This method is deprecated");
        }
        
        @SuppressWarnings("unused")
        public void newMethod() {
            System.out.println("This is the new method");
        }
    }
    
    // ========== I/O DEMO ==========
    static void ioDemo() throws IOException {
        // Traditional I/O
        try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"));
             BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
            
            String line;
            while ((line = reader.readLine()) != null) {
                writer.write(line.toUpperCase());
                writer.newLine();
            }
            System.out.println("File processed with traditional I/O");
            
        } catch (FileNotFoundException e) {
            System.out.println("File not found, creating sample...");
            // Create sample file
            try (BufferedWriter writer = new BufferedWriter(new FileWriter("test.txt"))) {
                writer.write("Line 1: Hello World");
                writer.newLine();
                writer.write("Line 2: Java I/O Demo");
                writer.newLine();
                writer.write("Line 3: File Operations");
            }
        }
        
        // NIO (New I/O)
        Path source = Paths.get("test.txt");
        Path destination = Paths.get("copy.txt");
        
        Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING);
        System.out.println("File copied using NIO");
        
        // Read all lines
        List lines = Files.readAllLines(source);
        System.out.println("File contents (" + lines.size() + " lines):");
        lines.forEach(System.out::println);
        
        // File attributes
        System.out.println("File size: " + Files.size(source) + " bytes");
        System.out.println("Is regular file? " + Files.isRegularFile(source));
        System.out.println("Is readable? " + Files.isReadable(source));
        
        // Delete created files
        Files.deleteIfExists(destination);
        Files.deleteIfExists(Paths.get("output.txt"));
    }
    
    // ========== JDBC DEMO ==========
    static void jdbcDemo() {
        // Note: This is a demonstration. Actual database needed for real execution.
        System.out.println("JDBC Demonstration (conceptual):");
        
        // Connection URL pattern
        String url = "jdbc:mysql://localhost:3306/mydatabase";
        String username = "root";
        String password = "password";
        
        try (Connection connection = DriverManager.getConnection(url, username, password)) {
            System.out.println("Database connected!");
            
            // Create statement
            Statement statement = connection.createStatement();
            
            // Create table
            String createTableSQL = """
                CREATE TABLE IF NOT EXISTS students (
                    id INT PRIMARY KEY AUTO_INCREMENT,
                    name VARCHAR(100) NOT NULL,
                    age INT,
                    grade DOUBLE
                )
                """;
            statement.execute(createTableSQL);
            
            // Insert data
            String insertSQL = """
                INSERT INTO students (name, age, grade) 
                VALUES ('Alice', 20, 85.5), ('Bob', 21, 92.0)
                """;
            int rowsInserted = statement.executeUpdate(insertSQL);
            System.out.println(rowsInserted + " rows inserted");
            
            // Query data
            String selectSQL = "SELECT * FROM students";
            ResultSet resultSet = statement.executeQuery(selectSQL);
            
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                int age = resultSet.getInt("age");
                double grade = resultSet.getDouble("grade");
                
                System.out.printf("Student: id=%d, name=%s, age=%d, grade=%.1f%n", 
                                  id, name, age, grade);
            }
            
            // Prepared statement (prevents SQL injection)
            String preparedSQL = "INSERT INTO students (name, age, grade) VALUES (?, ?, ?)";
            try (PreparedStatement pstmt = connection.prepareStatement(preparedSQL)) {
                pstmt.setString(1, "Charlie");
                pstmt.setInt(2, 22);
                pstmt.setDouble(3, 78.5);
                pstmt.executeUpdate();
            }
            
            // Transactions
            connection.setAutoCommit(false);
            try {
                // Multiple operations
                statement.executeUpdate("UPDATE students SET grade = grade + 5 WHERE age > 20");
                statement.executeUpdate("DELETE FROM students WHERE grade < 60");
                connection.commit();
                System.out.println("Transaction committed");
            } catch (SQLException e) {
                connection.rollback();
                System.out.println("Transaction rolled back");
            }
            
        } catch (SQLException e) {
            System.out.println("Database error: " + e.getMessage());
        }
    }
    
    // ========== MODULES DEMO ==========
    static void modulesDemo() {
        System.out.println("Java Modules (Project Jigsaw) - Java 9+");
        System.out.println("""
            Module System Benefits:
            1. Strong encapsulation
            2. Reliable configuration
            3. Scalable development
            4. Improved performance
            
            Module Declaration (module-info.java):
            module com.example.myapp {
                requires java.base;           // Implicit
                requires java.sql;
                requires transitive com.example.utils;
                
                exports com.example.myapp.api;
                exports com.example.myapp.internal to com.example.test;
                
                opens com.example.myapp.model to com.example.persistence;
                
                uses com.example.spi.ServiceProvider;
                provides com.example.spi.Service with com.example.myapp.ServiceImpl;
            }
            
            Key Concepts:
            - Module: Group of packages with module-info.java
            - Requires: Dependencies on other modules
            - Exports: Packages accessible to other modules
            - Opens: Packages accessible via reflection
            - Provides/Uses: Service loader mechanism
            """);
    }
}
Advanced Java Concepts
Click buttons to explore advanced Java concepts