๐ Java: The Platform-Independent Powerhouse
Java is a high-level, statically typed, object-oriented programming language designed for platform independence. The core philosophy is "Write Once, Run Anywhere (WORA)", achieved through compilation into platform-neutral bytecode executed by the Java Virtual Machine (JVM).
๐ฏ Java's Execution Model
Unlike interpreted languages, Java uses a two-step execution model: source code โ bytecode (via javac) โ JVM execution. The JVM then uses Just-In-Time (JIT) compilation to convert hot code paths into native machine code, delivering near-native performance.
๐ข Industry Applications
Java powers enterprise systems, banking software, cloud platforms, Android apps, distributed systems, and large-scale backend services. It prioritizes reliability, security, and maintainability.
๐ Java Versions & LTS
Java follows a six-month release cycle with Long-Term Support (LTS) versions every 3 years: Java 8 LTS, Java 11 LTS, Java 17 LTS, and Java 21 LTS. Each LTS receives updates for years, making them ideal for enterprise production.
Why Java Dominates Enterprise Development
Platform independence means you develop on Windows, deploy on Linux servers, and run on any JVM-enabled platform. Automatic memory management via garbage collection eliminates manual memory errors like memory leaks and dangling pointers. Multi-threading support is built into the language, enabling efficient concurrent applications.
Java's ecosystem includes Spring Framework (enterprise), Hibernate (ORM), Maven/Gradle (build tools), and thousands of libraries for every imaginable use case.
// Java Hello World with comprehensive explanation
public class JavaBasics {
/**
* The main method is the entry point for all Java applications.
* The JVM calls this method when the program starts.
*
* @param args Command-line arguments passed to the program
*/
public static void main(String[] args) {
// System.out is a PrintStream connected to standard output
System.out.println("โ Java Mastery Track Loaded");
System.out.println("Java Version: " + System.getProperty("java.version"));
System.out.println("JVM Name: " + System.getProperty("java.vm.name"));
System.out.println("OS: " + System.getProperty("os.name"));
// Demonstrate type inference with 'var' (Java 10+)
var message = "Java is platform-independent!";
System.out.println(message);
}
}โ๏ธ JVM Architecture & Memory Management
The Java Virtual Machine (JVM) is the cornerstone of Java's platform independence. It's an abstract execution engine that runs Java bytecode, providing memory management, garbage collection, security, and runtime optimization.
๐ JVM Memory Architecture
- Heap: Stores all objects and arrays (shared across threads). Divided into Young Generation (Eden + Survivor spaces) and Old Generation.
- Stack: Each thread has its own stack containing method frames. Each frame stores local variables, operand stack, and frame data.
- Method Area (MetaSpace): Stores class metadata, static variables, constants pool, and method bytecode.
- PC Register: Each thread has a program counter pointing to current instruction.
- Native Method Stack: Used for native methods written in C/C++.
โป๏ธ Garbage Collection
Java's automatic garbage collection identifies and removes objects no longer referenced. Modern GC algorithms (G1, ZGC, Shenandoah) provide predictable pause times, making Java suitable for real-time applications.
๐ง JRE vs JDK
- JRE (Java Runtime Environment): JVM + Core Libraries (for running Java apps)
- JDK (Java Development Kit): JRE + Development Tools (javac, javadoc, jar, jdb, jconsole)
- JVM: Runtime execution engine only
โก JIT Compilation
The Just-In-Time compiler identifies frequently executed code ("hot spots") and compiles them to native machine code. This combines the portability of bytecode with near-native execution speed. JIT can optimize code based on actual runtime behavior.
Understanding the Class Loading Process
The Class Loader Subsystem has three phases: Loading (finding and loading bytecode), Linking (verification, preparation, and resolution), and Initialization (executing static initializers). The Bootstrap Class Loader loads core Java classes, Extension Class Loader loads extension libraries, and Application Class Loader loads user classes. This hierarchical delegation ensures security and prevents class conflicts.
// JVM Architecture Demonstration
public class JVMDemo {
// Static block - executes during class initialization
static {
System.out.println("Class loaded by: " +
JVMDemo.class.getClassLoader());
}
public static void main(String[] args) {
// Show available processors
System.out.println("Available processors: " +
Runtime.getRuntime().availableProcessors());
// Show memory information
System.out.println("Total memory: " +
Runtime.getRuntime().totalMemory() / 1024 / 1024 + " MB");
System.out.println("Free memory: " +
Runtime.getRuntime().freeMemory() / 1024 / 1024 + " MB");
System.out.println("Max memory: " +
Runtime.getRuntime().maxMemory() / 1024 / 1024 + " MB");
// Force garbage collection (advisory)
System.gc();
System.out.println("GC requested");
// Show Java system properties
System.out.println("\n=== JVM Properties ===");
System.out.println("java.vm.name: " +
System.getProperty("java.vm.name"));
System.out.println("java.vm.version: " +
System.getProperty("java.vm.version"));
System.out.println("java.vm.vendor: " +
System.getProperty("java.vm.vendor"));
}
}
// Run with memory options:
// java -Xms256m -Xmx1024m JVMDemo๐ฆ Variables & Data Types Deep Dive
Java is statically typed - every variable must have a declared type at compile time. This enables early error detection and optimized memory allocation.
๐ Primitive Types (8 types)
- byte: 8-bit signed (-128 to 127) - efficient for arrays
- short: 16-bit signed (-32,768 to 32,767)
- int: 32-bit signed (-2ยณยน to 2ยณยน-1) - default for integers
- long: 64-bit signed (suffix L) - for large numbers
- float: 32-bit IEEE 754 (suffix F)
- double: 64-bit IEEE 754 - default for decimals
- char: 16-bit Unicode (0 to 65,535)
- boolean: true/false (not 0/1 like C)
๐ฆ Reference Types
Reference types store memory addresses pointing to objects on the heap:
- String: Immutable sequence of characters (interned for efficiency)
- Arrays: Fixed-size collections (covariant type system)
- Classes & Objects: User-defined types with fields and methods
- Interfaces: Abstract types defining contracts
๐พ Variable Scope & Lifetime
- Local variables: Declared in methods, stack-allocated, scope limited to method
- Instance variables: Belong to objects, heap-allocated, exist with object
- Static variables: Class-level, method area, exist for program lifetime
๐ Constants with 'final'
The final keyword creates immutable references. For primitives, the value cannot change; for references, the reference cannot change (object can still be modified).
Memory Efficiency of Primitives
Primitive types store values directly on the stack, making them extremely fast for operations. Reference types incur additional overhead (8-16 bytes per reference + object header). For large data structures, consider using primitive arrays or specialized collections like ArrayList<Integer> which auto-box primitives (though this adds overhead).
Type promotion: In expressions, smaller types are automatically promoted to larger types: byte โ short โ int โ long โ float โ double. Casting can force conversion but may lose precision.
// Comprehensive Variables & Data Types Example
public class VariablesDemo {
// Static variable (class-level)
static String className = "JavaBasics";
// Instance variables
private int instanceCount;
private String instanceName;
// Constants
public static final double PI = 3.141592653589793;
private static final int MAX_RETRIES = 3;
public static void main(String[] args) {
// Primitive type declarations
byte age = 25; // -128 to 127
short year = 2024; // -32,768 to 32,767
int population = 800000000; // up to 2 billion
long worldPopulation = 8000000000L; // suffix L required
float price = 99.99F; // suffix F required
double pi = 3.14159265359; // default for decimals
char grade = 'A'; // single quotes
boolean isActive = true; // true or false only
// Type conversion
int largeNumber = 1000;
byte smallNumber = (byte) largeNumber; // Explicit casting - may overflow
System.out.println("Cast overflow: " + smallNumber);
// Automatic type promotion
byte a = 10, b = 20;
int sum = a + b; // bytes promoted to int
System.out.println("Sum: " + sum);
// Reference types
String message = "Hello, Java!"; // String literal (interned)
String newString = new String("Hello"); // Creates new object
// String immutability demonstration
String s1 = "Hello";
String s2 = s1;
s1 = s1 + " World"; // Creates new String, s2 unchanged
System.out.println("s2: " + s2); // Still "Hello"
// Type inference with var (Java 10+)
var inferredString = "Type inferred as String";
var inferredInt = 42;
// var inferredNull = null; // ERROR: cannot infer type from null
// Boxing and unboxing
Integer boxedInt = Integer.valueOf(100); // Explicit boxing
int unboxedInt = boxedInt; // Auto-unboxing
System.out.println("Boxed: " + boxedInt);
// Array declaration
int[] numbers = {1, 2, 3, 4, 5};
String[] names = new String[10];
names[0] = "Alice";
// Multi-dimensional array
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println("Matrix[1][1]: " + matrix[1][1]); // 5
}
}๐ข Java Operators: Complete Reference
Operators perform operations on variables and values. Java supports a rich set of operators with well-defined precedence and associativity rules.
๐ Operator Categories
- Arithmetic: +, -, *, /, %, ++, -- (prefix/postfix)
- Relational: ==, !=, >, <, >=, <= (return boolean)
- Logical: && (AND), || (OR), ! (NOT) - short-circuit evaluation
- Bitwise: &, |, ^ (XOR), ~ (complement), <<, >>, >>> (unsigned shift)
- Assignment: =, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, >>>=
- Ternary: condition ? value1 : value2
- Instanceof: checks object type at runtime
โ ๏ธ Operator Precedence (Highest to Lowest)
Postfix (expr++ expr--) โ Unary (++expr --expr +expr -expr ~ !) โ Multiplicative (* / %) โ Additive (+ -) โ Shift (<< >> >>>) โ Relational (< > <= >= instanceof) โ Equality (== !=) โ Bitwise AND (&) โ Bitwise XOR (^) โ Bitwise OR (|) โ Logical AND (&&) โ Logical OR (||) โ Ternary (?:) โ Assignment (= += -= etc.)
๐ฏ Important Notes
- Integer Division: 10 / 3 = 3 (truncates toward zero)
- Modulus with negatives: -10 % 3 = -1 (sign follows dividend)
- Short-circuit: false && expr2 (expr2 never evaluated)
- Bitwise vs Logical: & and | evaluate both operands; && and || short-circuit
Understanding Bitwise Operations
Bitwise operators work at the binary level, making them extremely fast for flags, permissions, and low-level optimizations. & (AND) is used for masking, | (OR) for setting bits, ^ (XOR) for toggling, and << / >> for efficient multiplication/division by powers of 2.
Unsigned right shift (>>>) shifts zeros into the leftmost position, useful for treating numbers as unsigned. The instanceof operator prevents ClassCastException by checking type before casting.
// Comprehensive Operators Demo
public class OperatorsDemo {
public static void main(String[] args) {
// Arithmetic operators
int a = 10, b = 3;
System.out.println("a + b = " + (a + b)); // 13
System.out.println("a - b = " + (a - b)); // 7
System.out.println("a * b = " + (a * b)); // 30
System.out.println("a / b = " + (a / b)); // 3 (integer division)
System.out.println("a % b = " + (a % b)); // 1 (remainder)
// Increment/Decrement
int x = 5;
System.out.println("x++ = " + (x++)); // 5 (post-increment)
System.out.println("++x = " + (++x)); // 7 (pre-increment)
// Relational operators
System.out.println("a > b: " + (a > b)); // true
System.out.println("a == 10: " + (a == 10)); // true
// Logical operators with short-circuit
boolean condition1 = false;
boolean condition2 = true;
// Short-circuit AND - second operand not evaluated
if (condition1 && condition2) {
System.out.println("This won't execute");
}
// Short-circuit OR
if (condition1 || condition2) {
System.out.println("OR short-circuit: true");
}
// Bitwise operators
int flags = 0b1010; // 10 in binary
int mask = 0b0110; // 6 in binary
System.out.println("flags & mask = " + (flags & mask)); // 2 (0010)
System.out.println("flags | mask = " + (flags | mask)); // 14 (1110)
System.out.println("flags ^ mask = " + (flags ^ mask)); // 12 (1100)
System.out.println("~flags = " + (~flags)); // -11
// Shift operators
int value = 8; // 1000 binary
System.out.println("value << 2 = " + (value << 2)); // 32 (multiply by 4)
System.out.println("value >> 1 = " + (value >> 1)); // 4 (divide by 2)
// Negative shift demonstration
int negative = -8;
System.out.println("negative >> 1 = " + (negative >> 1)); // -4 (sign preserved)
System.out.println("negative >>> 1 = " + (negative >>> 1)); // Large positive
// Ternary operator
int score = 85;
String grade = (score >= 90) ? "A" :
(score >= 80) ? "B" :
(score >= 70) ? "C" : "F";
System.out.println("Grade: " + grade);
// instanceof operator
Object obj = "Hello";
System.out.println("obj instanceof String: " + (obj instanceof String));
System.out.println("obj instanceof Integer: " + (obj instanceof Integer));
// Assignment operators
int num = 10;
num += 5; // num = num + 5
num *= 2; // num = num * 2
System.out.println("After assignments: " + num);
// Compound assignments with bitwise
int bitField = 0b0101;
bitField |= 0b0010; // Set bit
System.out.println("BitField: " + Integer.toBinaryString(bitField));
}
}๐ Control Flow: Decision Making in Java
Control flow statements determine execution paths based on conditions. Java provides if-else, switch, and enhanced switch expressions (Java 14+).
๐ if-else Statements
Used for conditional branching with boolean conditions. Supports if, if-else, and if-else if chains. Conditions are evaluated in order until a true condition is found.
๐ฏ Switch Statements (Traditional)
Switch works with int, char, String, enum, and wrapper types. Each case must have a break to prevent fall-through (unless intended). The default case handles unmatched values.
โก Switch Expressions (Java 14+)
Enhanced switch with arrow syntax (case โ value), multiple case labels, and yield for returning values. Switch expressions are more concise and prevent fall-through bugs.
๐ท๏ธ Pattern Matching (Java 17+)
Pattern matching for switch allows type checking and variable capture in a single expression, reducing boilerplate instanceof checks.
When to Use Switch vs If-Else
Use switch when comparing a single variable against multiple constant values. Use if-else for range checks, complex boolean expressions, or when conditions involve multiple variables. Switch expressions with arrow syntax (Java 14+) are more readable and eliminate accidental fall-through.
Performance note: For many cases, the JVM optimizes switch statements using tableswitch (contiguous values) or lookupswitch (non-contiguous), making them as fast as or faster than if-else chains.
// Control Flow Comprehensive Examples
public class ControlFlowDemo {
public static void main(String[] args) {
// 1. Basic if-else
int score = 85;
if (score >= 90) {
System.out.println("Excellent!");
} else if (score >= 75) {
System.out.println("Good job!");
} else if (score >= 60) {
System.out.println("Passing");
} else {
System.out.println("Need improvement");
}
// 2. Nested if with logical operators
int age = 25;
boolean hasLicense = true;
if (age >= 18) {
if (hasLicense) {
System.out.println("Can drive");
} else {
System.out.println("Need license to drive");
}
} else {
System.out.println("Too young to drive");
}
// 3. Traditional switch (fall-through)
int day = 3;
String dayName;
switch (day) {
case 1:
dayName = "Monday";
break;
case 2:
dayName = "Tuesday";
break;
case 3:
dayName = "Wednesday";
break;
case 4:
dayName = "Thursday";
break;
case 5:
dayName = "Friday";
break;
default:
dayName = "Weekend";
}
System.out.println("Day: " + dayName);
// 4. Switch with String (Java 7+)
String fruit = "Apple";
switch (fruit) {
case "Apple":
System.out.println("Red or green");
break;
case "Banana":
System.out.println("Yellow");
break;
default:
System.out.println("Unknown fruit");
}
// 5. Switch expression (Java 14+)
String grade = "B";
String message = switch (grade) {
case "A", "A+" -> "Excellent performance!";
case "B", "B+" -> "Good work!";
case "C" -> "Satisfactory";
default -> {
System.out.println("Need improvement");
yield "Please review your work";
}
};
System.out.println("Message: " + message);
// 6. Switch with yield (Java 14+)
int number = 2;
int result = switch (number) {
case 1:
yield 10;
case 2:
yield 20;
default:
yield 0;
};
System.out.println("Result: " + result);
// 7. Pattern matching for switch (Java 17+)
Object obj = "Hello Java";
String typeInfo = switch (obj) {
case String s -> "String of length: " + s.length();
case Integer i -> "Integer value: " + i;
case Long l -> "Long value: " + l;
case Double d -> "Double value: " + d;
case null -> "null value";
default -> "Unknown type";
};
System.out.println("Type info: " + typeInfo);
// 8. Guarded patterns (Java 17+)
Object value = 42;
String processed = switch (value) {
case Integer i when i > 0 -> "Positive integer: " + i;
case Integer i when i < 0 -> "Negative integer: " + i;
case Integer i -> "Zero integer";
default -> "Not an integer";
};
System.out.println("Processed: " + processed);
}
}๐ Iteration: Loops and Control
Loops enable repeated execution of code blocks. Java provides four loop constructs optimized for different scenarios.
๐ Loop Types
- for loop: Best when iteration count is known (index-based)
- enhanced for loop: Simplified iteration over arrays/collections
- while loop: Condition-based, may execute zero times
- do-while loop: Executes at least once before checking condition
๐ฎ Loop Control
- break: Exits current loop immediately
- continue: Skips current iteration, continues with next
- labeled break/continue: Exits/skips specific nested loops
- return: Exits method (also exits all loops)
โก Performance Considerations
- Cache array length in variable:
for(int i=0, len=arr.length; i - Use enhanced for loop when index not needed
- Avoid expensive operations in loop conditions
- Consider parallel streams for large data sets (Java 8+)
Choosing the Right Loop
Use for loop when iterating a known number of times, especially when you need the index. Use enhanced for for arrays/collections when index not needed (more readable, fewer errors). Use while when iteration depends on a condition that may change during execution. Use do-while when the loop body must execute at least once (e.g., user input validation).
Labeled breaks are essential for exiting nested loops - without labels, you'd need complex flags or multiple condition checks.
// Comprehensive Loop Examples
public class LoopsDemo {
public static void main(String[] args) {
// 1. Traditional for loop
System.out.println("=== Traditional for loop ===");
for (int i = 0; i < 5; i++) {
System.out.println("i = " + i);
}
// 2. For loop with multiple variables
System.out.println("\n=== Multiple variables ===");
for (int i = 0, j = 10; i < j; i++, j--) {
System.out.println("i=" + i + ", j=" + j);
}
// 3. Enhanced for loop (foreach)
System.out.println("\n=== Enhanced for loop ===");
String[] languages = {"Java", "Python", "JavaScript", "C++"};
for (String lang : languages) {
System.out.println("Language: " + lang);
}
// 4. While loop
System.out.println("\n=== While loop ===");
int count = 0;
while (count < 3) {
System.out.println("While count: " + count);
count++;
}
// 5. Do-while loop (executes at least once)
System.out.println("\n=== Do-while loop ===");
int attempts = 0;
do {
System.out.println("Attempt " + (attempts + 1));
attempts++;
} while (attempts < 3);
// 6. Infinite loop with break
System.out.println("\n=== Break example ===");
int counter = 0;
while (true) {
System.out.println("Iteration: " + counter);
counter++;
if (counter >= 3) {
break; // Exit infinite loop
}
}
// 7. Continue statement
System.out.println("\n=== Continue example ===");
for (int i = 0; i < 5; i++) {
if (i % 2 == 0) {
continue; // Skip even numbers
}
System.out.println("Odd number: " + i);
}
// 8. Nested loops
System.out.println("\n=== Multiplication Table ===");
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
System.out.print((i * j) + " ");
}
System.out.println();
}
// 9. Labeled break (exiting nested loops)
System.out.println("\n=== Labeled break ===");
outerLoop:
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
if (i == 2 && j == 2) {
System.out.println("Breaking out of both loops at i=" + i + ", j=" + j);
break outerLoop;
}
System.out.println("i=" + i + ", j=" + j);
}
}
// 10. Labeled continue
System.out.println("\n=== Labeled continue ===");
outer:
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
if (i == 2) {
System.out.println("Skipping row " + i);
continue outer;
}
System.out.println("i=" + i + ", j=" + j);
}
}
// 11. Iterating over arrays with index
System.out.println("\n=== Array iteration with index ===");
int[] numbers = {10, 20, 30, 40, 50};
for (int i = 0; i < numbers.length; i++) {
System.out.println("Index " + i + ": " + numbers[i]);
}
// 12. Reverse iteration
System.out.println("\n=== Reverse iteration ===");
for (int i = numbers.length - 1; i >= 0; i--) {
System.out.println("Reverse index " + i + ": " + numbers[i]);
}
// 13. While with complex condition
System.out.println("\n=== Complex condition ===");
int n = 10;
while (n > 0 && n % 2 == 0) {
System.out.println("n = " + n);
n -= 2;
}
// 14. For-each with primitive array
System.out.println("\n=== For-each with array ===");
for (int num : numbers) {
System.out.print(num + " ");
}
System.out.println();
}
}โ๏ธ Methods: Building Reusable Code
Methods are blocks of code that perform specific tasks, promoting code reuse, modularity, and abstraction. Every Java method belongs to a class.
๐ง Method Components
- Access Modifier: public, private, protected, or package-private (default)
- Return Type: Primitive type, reference type, or void (no return)
- Method Name: camelCase, descriptive (verb-noun pattern)
- Parameters: Input values with types (zero or more)
- Method Body: Code to execute (in curly braces)
- Exceptions: Throws clause for checked exceptions
โก Method Overloading
Multiple methods with the same name but different parameter lists (type, count, or order). The compiler selects the correct method at compile time based on arguments.
๐ Varargs (Variable Arguments)
Using Type... name syntax, methods can accept zero or more arguments of the same type. Varargs must be the last parameter.
๐ Recursion
Methods can call themselves - useful for tree traversal, factorial, Fibonacci. Must have a base case to prevent infinite recursion.
Stack Frame Mechanics
When a method is called, the JVM creates a stack frame containing local variables, operand stack, and frame data. Recursion creates multiple frames, which can lead to StackOverflowError if too deep. Tail recursion optimization isn't guaranteed in Java, so iterative solutions are often preferred for large depths.
Method overloading vs overriding: Overloading is compile-time polymorphism (same name, different parameters). Overriding is runtime polymorphism (subclass redefines parent method).
// Comprehensive Methods Example
public class MethodsDemo {
// ========== Basic Methods ==========
// Void method with parameters
public static void greet(String name) {
System.out.println("Hello, " + name + "!");
}
// Method with return value
public static int add(int a, int b) {
return a + b;
}
// Method with double return
public static double divide(double a, double b) {
if (b == 0) {
System.out.println("Warning: Division by zero");
return Double.NaN;
}
return a / b;
}
// ========== Method Overloading ==========
// Overloaded - different parameter count
public static int add(int a, int b, int c) {
return a + b + c;
}
// Overloaded - different parameter type
public static double add(double a, double b) {
return a + b;
}
// Overloaded - different order
public static String add(String a, int b) {
return a + b;
}
// ========== Varargs Methods ==========
// Variable arguments (must be last parameter)
public static int sum(int... numbers) {
int total = 0;
for (int num : numbers) {
total += num;
}
return total;
}
// Varargs with regular parameters
public static void log(String level, String... messages) {
System.out.print("[" + level + "] ");
for (String msg : messages) {
System.out.print(msg + " ");
}
System.out.println();
}
// ========== Recursion Examples ==========
// Factorial using recursion
public static long factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
// Fibonacci using recursion
public static long fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// Tail recursion (optimization not guaranteed)
public static long factorialTail(int n, long accumulator) {
if (n <= 1) return accumulator;
return factorialTail(n - 1, n * accumulator);
}
// ========== Access Modifiers Demo ==========
private static int privateCounter = 0;
static int packagePrivateCounter = 0; // package-private (default)
protected static int protectedCounter = 0;
public static int publicCounter = 0;
private static void privateMethod() {
System.out.println("Private method called");
}
public static void demonstrateAccess() {
privateMethod(); // Can call private within same class
privateCounter++; // Can access private variable
System.out.println("Access demo complete");
}
// ========== Main Method ==========
public static void main(String[] args) {
System.out.println("=== Method Demos ===\n");
// Basic methods
greet("CodeOrbitPro");
System.out.println("Add: " + add(5, 3));
System.out.println("Divide: " + divide(10, 3));
// Method overloading
System.out.println("\n=== Overloading ===");
System.out.println("add(int,int): " + add(5, 3));
System.out.println("add(int,int,int): " + add(5, 3, 2));
System.out.println("add(double,double): " + add(5.5, 3.2));
System.out.println("add(String,int): " + add("Value: ", 42));
// Varargs
System.out.println("\n=== Varargs ===");
System.out.println("sum(1,2,3): " + sum(1, 2, 3));
System.out.println("sum(1,2,3,4,5): " + sum(1, 2, 3, 4, 5));
log("INFO", "Application", "started", "successfully");
log("ERROR", "Failed", "to", "connect");
// Recursion
System.out.println("\n=== Recursion ===");
System.out.println("factorial(5): " + factorial(5));
System.out.println("factorial(10): " + factorial(10));
System.out.println("fibonacci(10): " + fibonacci(10));
// Tail recursion demo
System.out.println("factorialTail(5): " + factorialTail(5, 1));
// Method chaining (fluent interface)
System.out.println("\n=== Method Chaining ===");
StringBuilder sb = new StringBuilder();
sb.append("Hello")
.append(" ")
.append("World")
.append("!")
.append(" Java");
System.out.println(sb.toString());
// Access modifiers
System.out.println("\n=== Access Modifiers ===");
demonstrateAccess();
System.out.println("publicCounter: " + publicCounter);
}
// ========== Static vs Instance Methods ==========
static class Calculator {
// Instance variable
private int result;
// Instance method (requires object)
public void addToResult(int value) {
this.result += value;
}
// Static method (called on class)
public static int multiply(int a, int b) {
return a * b;
}
// Getter
public int getResult() {
return result;
}
}
}๐ Arrays & Collections Framework
Arrays store fixed-size sequences of elements of the same type. Java's Collections Framework provides dynamic data structures like ArrayList, LinkedList, HashMap, and HashSet.
๐ Arrays
Arrays are zero-indexed, fixed-size, and store contiguous memory. They provide O(1) access by index but cannot be resized. Multi-dimensional arrays are arrays of arrays.
๐ฆ Collections Framework
- List: Ordered, allows duplicates (ArrayList, LinkedList)
- Set: No duplicates (HashSet, TreeSet, LinkedHashSet)
- Map: Key-value pairs (HashMap, TreeMap, LinkedHashMap)
- Queue: FIFO operations (PriorityQueue, ArrayDeque)
โก Utility Classes
java.util.Arrays provides sorting, searching, and copying. java.util.Collections provides synchronization, unmodifiable views, and utility methods.
Choosing the Right Collection
ArrayList for indexed access with frequent reads. LinkedList for frequent insertions/deletions. HashSet for fast uniqueness checks. TreeSet for sorted unique elements. HashMap for fast key-value lookups. LinkedHashMap for insertion-ordered iteration.
Performance: ArrayList O(1) access, O(n) insertion; HashMap O(1) average for get/put; TreeMap O(log n) for operations. Collections utilities provide thread-safe wrappers and unmodifiable views.
// Arrays and Collections Framework
import java.util.*;
public class CollectionsDemo {
public static void main(String[] args) {
// ========== Arrays ==========
System.out.println("=== Arrays ===");
// Array declaration and initialization
int[] numbers = {10, 20, 30, 40, 50};
String[] names = new String[5];
names[0] = "Alice";
names[1] = "Bob";
// Multi-dimensional array
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println("Matrix[1][1]: " + matrix[1][1]); // 5
// Array traversal
System.out.print("Numbers: ");
for (int num : numbers) {
System.out.print(num + " ");
}
System.out.println();
// Array utility methods
Arrays.sort(numbers);
System.out.println("Sorted: " + Arrays.toString(numbers));
int index = Arrays.binarySearch(numbers, 30);
System.out.println("Index of 30: " + index);
int[] copy = Arrays.copyOf(numbers, numbers.length);
System.out.println("Array equal: " + Arrays.equals(numbers, copy));
// ========== ArrayList ==========
System.out.println("\n=== ArrayList ===");
List list = new ArrayList<>();
// Adding elements
list.add("Java");
list.add("Python");
list.add("JavaScript");
list.add(1, "C++"); // Insert at index
// Accessing
System.out.println("Element at index 2: " + list.get(2));
System.out.println("Size: " + list.size());
// Iteration
System.out.print("List: ");
for (String lang : list) {
System.out.print(lang + " ");
}
System.out.println();
// Removal
list.remove("Python");
list.remove(1);
System.out.println("After removal: " + list);
// ========== LinkedList ==========
System.out.println("\n=== LinkedList ===");
LinkedList linkedList = new LinkedList<>();
linkedList.add("First");
linkedList.add("Second");
linkedList.addFirst("Zeroth");
linkedList.addLast("Last");
System.out.println("LinkedList: " + linkedList);
System.out.println("First: " + linkedList.getFirst());
System.out.println("Last: " + linkedList.getLast());
// ========== HashSet ==========
System.out.println("\n=== HashSet ===");
Set set = new HashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Apple"); // Duplicate - ignored
set.add("Orange");
System.out.println("Set: " + set);
System.out.println("Contains 'Banana': " + set.contains("Banana"));
// ========== TreeSet (Sorted) ==========
System.out.println("\n=== TreeSet ===");
Set treeSet = new TreeSet<>();
treeSet.add(5);
treeSet.add(2);
treeSet.add(8);
treeSet.add(1);
System.out.println("Sorted TreeSet: " + treeSet);
// ========== HashMap ==========
System.out.println("\n=== HashMap ===");
Map map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);
map.put("Alice", 26); // Overwrites previous value
System.out.println("Alice's age: " + map.get("Alice"));
System.out.println("Contains key 'Bob': " + map.containsKey("Bob"));
System.out.println("All entries: " + map);
// Iterating over Map
for (Map.Entry entry : map.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
// ========== TreeMap (Sorted by key) ==========
System.out.println("\n=== TreeMap ===");
Map treeMap = new TreeMap<>();
treeMap.put("Zebra", 3);
treeMap.put("Apple", 5);
treeMap.put("Monkey", 2);
System.out.println("Sorted TreeMap: " + treeMap);
// ========== PriorityQueue ==========
System.out.println("\n=== PriorityQueue ===");
Queue queue = new PriorityQueue<>();
queue.offer(10);
queue.offer(5);
queue.offer(20);
queue.offer(1);
System.out.print("PriorityQueue order: ");
while (!queue.isEmpty()) {
System.out.print(queue.poll() + " ");
}
System.out.println();
// ========== Stack (Deque) ==========
System.out.println("\n=== Stack using Deque ===");
Deque stack = new ArrayDeque<>();
stack.push("First");
stack.push("Second");
stack.push("Third");
System.out.println("Pop: " + stack.pop());
System.out.println("Peek: " + stack.peek());
System.out.println("Stack after pops: " + stack);
// ========== Collections Utilities ==========
System.out.println("\n=== Collections Utilities ===");
List numList = Arrays.asList(3, 1, 4, 1, 5, 9, 2);
Collections.sort(numList);
System.out.println("Sorted list: " + numList);
Collections.reverse(numList);
System.out.println("Reversed: " + numList);
Collections.shuffle(numList);
System.out.println("Shuffled: " + numList);
// Max and min
System.out.println("Max: " + Collections.max(numList));
System.out.println("Min: " + Collections.min(numList));
// Frequency
System.out.println("Frequency of 1: " + Collections.frequency(numList, 1));
// ========== Synchronized Collections ==========
System.out.println("\n=== Synchronized Collections ===");
List syncList = Collections.synchronizedList(new ArrayList<>());
syncList.add("Thread-safe");
syncList.add("Collection");
System.out.println("Synchronized list: " + syncList);
// ========== Unmodifiable Collections ==========
List unmodifiable = Collections.unmodifiableList(list);
// unmodifiable.add("New"); // Throws UnsupportedOperationException
System.out.println("Unmodifiable list: " + unmodifiable);
}
} ๐ Strings & StringBuilder
Strings in Java are immutable objects stored in the String Pool for memory efficiency. StringBuilder and StringBuffer provide mutable alternatives for string manipulation.
๐ค String Immutability
String objects cannot be modified after creation. Any operation that appears to modify a string creates a new String object. This enables string interning and thread safety.
๐พ String Pool
String literals are stored in the String Pool (heap). When creating a literal, the JVM checks the pool first, reusing existing strings to save memory.
โก StringBuilder vs StringBuffer
- StringBuilder: Mutable, non-synchronized (faster) - use for single-threaded
- StringBuffer: Mutable, synchronized (slower) - use for multi-threaded
String Performance Optimization
String concatenation in loops creates many intermediate String objects. Use StringBuilder for repeated concatenation. The compiler optimizes simple concatenations (String s = "a" + "b" + "c") but not loops.
String interning: String.intern() manually adds strings to the pool. Use when many identical strings exist. Java 7+ stores interned strings in heap, not PermGen.
// Strings and StringBuilder Comprehensive Guide
public class StringsDemo {
public static void main(String[] args) {
// ========== String Creation ==========
System.out.println("=== String Creation ===");
// String literal (stored in String Pool)
String s1 = "Hello";
String s2 = "Hello";
// Using new keyword (creates new object, not in pool)
String s3 = new String("Hello");
System.out.println("s1 == s2: " + (s1 == s2)); // true (same reference)
System.out.println("s1 == s3: " + (s1 == s3)); // false (different objects)
System.out.println("s1.equals(s3): " + s1.equals(s3)); // true (same content)
// ========== String Interning ==========
String s4 = s3.intern(); // Adds to pool and returns pool reference
System.out.println("After intern: " + (s1 == s4)); // true
// ========== Common String Methods ==========
System.out.println("\n=== String Methods ===");
String text = " Java Programming Language ";
System.out.println("Length: " + text.length());
System.out.println("Trim: '" + text.trim() + "'");
System.out.println("Uppercase: " + text.toUpperCase());
System.out.println("Lowercase: " + text.toLowerCase());
System.out.println("Contains 'Java': " + text.contains("Java"));
System.out.println("Starts with 'Java': " + text.trim().startsWith("Java"));
System.out.println("Ends with 'Language': " + text.trim().endsWith("Language"));
System.out.println("Index of 'Programming': " + text.indexOf("Programming"));
// Substring
String sub = text.substring(5, 15);
System.out.println("Substring (5-15): '" + sub + "'");
// Replace
System.out.println("Replace: " + text.replace("Java", "Python"));
// Split
String[] words = text.trim().split(" ");
System.out.print("Split words: ");
for (String word : words) {
System.out.print(word + " | ");
}
System.out.println();
// Join
String joined = String.join("-", words);
System.out.println("Joined: " + joined);
// Format
String formatted = String.format("Name: %s, Age: %d, Score: %.2f",
"Alice", 25, 92.5);
System.out.println("Formatted: " + formatted);
// ========== Character Operations ==========
System.out.println("\n=== Character Operations ===");
char ch = text.charAt(5);
System.out.println("Character at index 5: " + ch);
char[] chars = text.toCharArray();
System.out.println("First 5 chars: " + new String(chars, 0, 5));
// ========== StringBuilder (Mutable Strings) ==========
System.out.println("\n=== StringBuilder ===");
StringBuilder sb = new StringBuilder();
// Appending
sb.append("Hello");
sb.append(" ");
sb.append("World");
System.out.println("After append: " + sb);
// Insert
sb.insert(5, " Beautiful");
System.out.println("After insert: " + sb);
// Replace
sb.replace(6, 15, "Awesome");
System.out.println("After replace: " + sb);
// Delete
sb.delete(6, 13);
System.out.println("After delete: " + sb);
// Reverse
sb.reverse();
System.out.println("Reversed: " + sb);
sb.reverse(); // Back to original
// Capacity and length
System.out.println("Length: " + sb.length());
System.out.println("Capacity: " + sb.capacity());
// Set length (truncate)
sb.setLength(5);
System.out.println("After setLength(5): " + sb);
// ========== Performance Comparison ==========
System.out.println("\n=== Performance Comparison ===");
// Inefficient string concatenation (creates many objects)
long start = System.nanoTime();
String result = "";
for (int i = 0; i < 10000; i++) {
result += "a";
}
long end = System.nanoTime();
System.out.println("String concatenation: " + (end - start) / 1_000_000 + " ms");
// Efficient StringBuilder (single object)
start = System.nanoTime();
StringBuilder sb2 = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb2.append("a");
}
result = sb2.toString();
end = System.nanoTime();
System.out.println("StringBuilder: " + (end - start) / 1_000_000 + " ms");
// ========== Regular Expressions ==========
System.out.println("\n=== Regular Expressions ===");
String email = "user@example.com";
String emailRegex = "^[A-Za-z0-9+_.-]+@(.+)$";
System.out.println("Email valid: " + email.matches(emailRegex));
// Pattern matching
String logLine = "ERROR 2024-01-15: Connection failed";
boolean hasError = logLine.matches(".*ERROR.*");
System.out.println("Contains ERROR: " + hasError);
// Replace with regex
String phone = "123-456-7890";
String digits = phone.replaceAll("-", "");
System.out.println("Phone digits: " + digits);
// ========== String Pool Demonstration ==========
System.out.println("\n=== String Pool ===");
String str1 = "Java";
String str2 = "Java";
String str3 = new String("Java");
String str4 = new String("Java").intern();
System.out.println("str1 == str2: " + (str1 == str2)); // true
System.out.println("str1 == str3: " + (str1 == str3)); // false
System.out.println("str1 == str4: " + (str1 == str4)); // true
// ========== Text Blocks (Java 13+) ==========
System.out.println("\n=== Text Blocks ===");
String json = """
{
"name": "Java",
"version": "17",
"features": ["text blocks", "records", "sealed classes"]
}
""";
System.out.println("JSON: " + json);
// ========== StringJoiner ==========
System.out.println("\n=== StringJoiner ===");
StringJoiner joiner = new StringJoiner(", ", "[", "]");
joiner.add("Apple");
joiner.add("Banana");
joiner.add("Orange");
System.out.println("Joined list: " + joiner);
}
}๐๏ธ Object-Oriented Programming Fundamentals
Java is a pure object-oriented language where everything is an object (except primitives). OOP principles enable modular, reusable, and maintainable code.
๐ฆ Classes and Objects
A class is a blueprint for creating objects. Objects have state (fields) and behavior (methods). Constructors initialize object state.
๐ญ Four Pillars of OOP
- Encapsulation: Hiding internal state using private fields and public methods
- Inheritance: Creating hierarchies with 'extends' (is-a relationship)
- Polymorphism: Method overriding and overloading (compile-time and runtime)
- Abstraction: Abstract classes and interfaces hiding implementation details
๐ง Access Modifiers
- private: Accessible only within the same class
- default (package-private): Accessible within same package
- protected: Accessible within package + subclasses
- public: Accessible from anywhere
Understanding Inheritance vs Composition
Inheritance (is-a) creates tight coupling - changes in parent affect all children. Composition (has-a) is more flexible: class Car { Engine engine; }. Favor composition over inheritance for better maintainability. Java supports single inheritance (one parent class) but multiple interfaces.
Polymorphism allows treating derived classes as base types, enabling flexible, extensible code through interfaces and abstract classes.
// Object-Oriented Programming Comprehensive Example
public class OOPDemo {
// ========== Encapsulation Example ==========
static class BankAccount {
// Private fields - encapsulation
private String accountNumber;
private double balance;
private String ownerName;
// Constructor
public BankAccount(String accountNumber, String ownerName, double initialBalance) {
this.accountNumber = accountNumber;
this.ownerName = ownerName;
this.balance = initialBalance;
}
// Public getters and setters (controlled access)
public String getAccountNumber() {
return accountNumber;
}
public String getOwnerName() {
return ownerName;
}
public double getBalance() {
return balance;
}
// Business logic with validation
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("Deposited: $" + amount);
} else {
System.out.println("Invalid deposit amount");
}
}
public boolean withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("Withdrawn: $" + amount);
return true;
}
System.out.println("Insufficient funds");
return false;
}
public void displayInfo() {
System.out.println("Account: " + accountNumber);
System.out.println("Owner: " + ownerName);
System.out.println("Balance: $" + balance);
}
}
// ========== Inheritance Example ==========
static 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 sound");
}
public void displayInfo() {
System.out.println("Name: " + name + ", Age: " + age);
}
}
static class Dog extends Animal {
private String breed;
public Dog(String name, int age, String breed) {
super(name, age); // Call parent constructor
this.breed = breed;
}
// Method overriding (polymorphism)
@Override
public void makeSound() {
System.out.println(name + " barks: Woof! Woof!");
}
// Additional method
public void fetch() {
System.out.println(name + " is fetching the ball");
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.println("Breed: " + breed);
}
}
static class Cat extends Animal {
private String color;
public Cat(String name, int age, String color) {
super(name, age);
this.color = color;
}
@Override
public void makeSound() {
System.out.println(name + " meows: Meow! Meow!");
}
public void climb() {
System.out.println(name + " is climbing");
}
}
// ========== Abstraction with Abstract Class ==========
abstract static class Shape {
protected String color;
public Shape(String color) {
this.color = color;
}
// Abstract methods - must be implemented by subclasses
public abstract double area();
public abstract double perimeter();
// Concrete method
public void displayColor() {
System.out.println("Color: " + color);
}
}
static class Circle extends Shape {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
@Override
public double perimeter() {
return 2 * Math.PI * radius;
}
}
static class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(String color, double width, double height) {
super(color);
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
@Override
public double perimeter() {
return 2 * (width + height);
}
}
// ========== Interface Example ==========
interface Drawable {
void draw();
// Default method (Java 8+)
default void print() {
System.out.println("Printing drawing");
}
// Static method (Java 8+)
static void info() {
System.out.println("Drawable interface");
}
}
interface Resizable {
void resize(double factor);
}
// Multiple interface implementation
static class Drawing implements Drawable, Resizable {
private String name;
public Drawing(String name) {
this.name = name;
}
@Override
public void draw() {
System.out.println("Drawing: " + name);
}
@Override
public void resize(double factor) {
System.out.println("Resizing " + name + " by factor " + factor);
}
}
// ========== Polymorphism Example ==========
static class AnimalShelter {
private List animals = new ArrayList<>();
public void addAnimal(Animal animal) {
animals.add(animal);
}
public void makeAllSounds() {
for (Animal animal : animals) {
animal.makeSound(); // Polymorphic call
}
}
public void feedAll() {
for (Animal animal : animals) {
animal.eat(); // Polymorphic call
}
}
}
// ========== Main Demonstration ==========
public static void main(String[] args) {
System.out.println("=== Encapsulation ===");
BankAccount account = new BankAccount("12345", "Alice", 1000);
account.deposit(500);
account.withdraw(200);
account.displayInfo();
// account.balance = 10000; // Error - private field
System.out.println("\n=== Inheritance & Polymorphism ===");
Dog dog = new Dog("Max", 3, "Golden Retriever");
Cat cat = new Cat("Whiskers", 2, "Gray");
dog.eat();
dog.makeSound();
dog.fetch();
cat.makeSound();
cat.climb();
System.out.println("\n=== Polymorphic Behavior ===");
AnimalShelter shelter = new AnimalShelter();
shelter.addAnimal(dog);
shelter.addAnimal(cat);
shelter.makeAllSounds();
System.out.println("\n=== Abstraction ===");
Shape circle = new Circle("Red", 5.0);
Shape rectangle = new Rectangle("Blue", 4.0, 6.0);
System.out.println("Circle area: " + circle.area());
System.out.println("Circle perimeter: " + circle.perimeter());
circle.displayColor();
System.out.println("Rectangle area: " + rectangle.area());
System.out.println("Rectangle perimeter: " + rectangle.perimeter());
System.out.println("\n=== Interfaces ===");
Drawing drawing = new Drawing("Mountain Landscape");
drawing.draw();
drawing.resize(1.5);
drawing.print();
Drawable.info();
System.out.println("\n=== Casting & instanceof ===");
// Upcasting (automatic)
Animal animal = new Dog("Buddy", 2, "Beagle");
// Downcasting (explicit - requires check)
if (animal instanceof Dog) {
Dog downcastDog = (Dog) animal;
downcastDog.fetch();
}
System.out.println("\n=== Object Class Methods ===");
System.out.println("toString: " + dog.toString());
System.out.println("HashCode: " + dog.hashCode());
// equals comparison
Dog dog1 = new Dog("Max", 3, "Golden");
Dog dog2 = new Dog("Max", 3, "Golden");
System.out.println("dog1 equals dog2: " + dog1.equals(dog2)); // Override to compare content
}
} ๐ก๏ธ Exception Handling: Robust Error Management
Exception handling separates error-handling code from regular logic, making programs more robust and maintainable. Java provides checked and unchecked exceptions.
๐ Exception Hierarchy
- Throwable: Base class for all errors and exceptions
- Error: Serious problems (OutOfMemoryError, StackOverflowError) - not meant to be caught
- Exception: Conditions programs should catch
- RuntimeException: Unchecked exceptions (programming errors) - not required to catch
๐ง Try-Catch-Finally
The try block contains code that might throw exceptions. Catch blocks handle specific exception types. Finally block always executes (resource cleanup).
๐ก Try-with-Resources (Java 7+)
Automatically closes resources implementing AutoCloseable. Eliminates need for explicit finally blocks.
โก Custom Exceptions
Create application-specific exceptions by extending Exception (checked) or RuntimeException (unchecked).
Checked vs Unchecked Exceptions
Checked exceptions (IOException, SQLException) must be caught or declared in throws clause - compile-time enforced. Unchecked exceptions (NullPointerException, IllegalArgumentException) are runtime errors indicating programming bugs.
Best practice: Use checked exceptions for recoverable conditions (file not found). Use unchecked for programming errors (invalid arguments). Try-with-resources is superior to try-finally for managing resources like files, sockets, and database connections.
// Exception Handling Comprehensive Guide
import java.io.*;
import java.nio.file.*;
import java.util.*;
public class ExceptionsDemo {
// ========== Custom Exception ==========
static class InsufficientFundsException extends Exception {
private double amount;
public InsufficientFundsException(String message, double amount) {
super(message);
this.amount = amount;
}
public double getAmount() {
return amount;
}
}
static class InvalidAgeException extends RuntimeException {
public InvalidAgeException(String message) {
super(message);
}
}
// ========== BankAccount with custom exceptions ==========
static class BankAccount {
private double balance;
public BankAccount(double initialBalance) {
this.balance = initialBalance;
}
public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException(
"Insufficient funds. Needed: $" + amount + ", Available: $" + balance,
amount - balance
);
}
balance -= amount;
System.out.println("Withdrawn: $" + amount);
}
public double getBalance() {
return balance;
}
}
// ========== Person with validation ==========
static class Person {
private String name;
private int age;
public void setName(String name) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("Name cannot be null or empty");
}
this.name = name;
}
public void setAge(int age) {
if (age < 0 || age > 150) {
throw new InvalidAgeException("Age must be between 0 and 150");
}
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
// ========== Main Demonstration ==========
public static void main(String[] args) {
System.out.println("=== Basic Try-Catch-Finally ===");
try {
int result = 10 / 0;
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
System.out.println("Caught ArithmeticException: " + e.getMessage());
} finally {
System.out.println("Finally block always executes");
}
System.out.println("\n=== Multiple Catch Blocks ===");
try {
String str = null;
System.out.println(str.length()); // NullPointerException
} catch (NullPointerException e) {
System.out.println("NullPointerException: " + e.getMessage());
} catch (Exception e) {
System.out.println("Generic exception: " + e.getClass().getSimpleName());
}
System.out.println("\n=== Multi-Catch (Java 7+) ===");
try {
int[] arr = new int[3];
arr[5] = 10; // ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException | NullPointerException e) {
System.out.println("Multi-catch caught: " + e.getClass().getSimpleName());
}
System.out.println("\n=== Try-with-Resources (AutoCloseable) ===");
// Traditional try-finally (verbose)
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("test.txt"));
String line = reader.readLine();
System.out.println("Line: " + line);
} catch (FileNotFoundException e) {
System.out.println("File not found: " + e.getMessage());
} catch (IOException e) {
System.out.println("IO Error: " + e.getMessage());
} finally {
try {
if (reader != null) reader.close();
} catch (IOException e) {
System.out.println("Error closing reader");
}
}
// Try-with-resources (cleaner)
System.out.println("\nTry-with-resources (automatic close):");
try (BufferedReader reader2 = new BufferedReader(new FileReader("test.txt"))) {
String line = reader2.readLine();
System.out.println("Line: " + line);
} catch (FileNotFoundException e) {
System.out.println("File not found: " + e.getMessage());
} catch (IOException e) {
System.out.println("IO Error: " + e.getMessage());
}
// Creating a file for demonstration
try (FileWriter writer = new FileWriter("test.txt")) {
writer.write("Hello, Java Exception Handling!");
} catch (IOException e) {
System.out.println("Error writing file");
}
System.out.println("\n=== Custom Checked Exception ===");
BankAccount account = new BankAccount(500);
try {
account.withdraw(100); // Success
account.withdraw(500); // This will throw exception
} catch (InsufficientFundsException e) {
System.out.println("Exception: " + e.getMessage());
System.out.println("Shortfall: $" + e.getAmount());
}
System.out.println("Final balance: $" + account.getBalance());
System.out.println("\n=== Custom Unchecked Exception ===");
Person person = new Person();
try {
person.setName("Alice");
person.setAge(200); // Invalid age - throws RuntimeException
System.out.println(person);
} catch (InvalidAgeException e) {
System.out.println("Validation error: " + e.getMessage());
}
System.out.println("\n=== Exception Chaining ===");
try {
try {
throw new IOException("Original IO error");
} catch (IOException e) {
throw new RuntimeException("Wrapper exception", e);
}
} catch (RuntimeException e) {
System.out.println("Caught: " + e.getMessage());
System.out.println("Cause: " + e.getCause().getMessage());
}
System.out.println("\n=== Finally Block Guarantee ===");
try {
System.out.println("In try block");
return; // Even with return, finally executes
} finally {
System.out.println("Finally block - executes even with return!");
}
System.out.println("\n=== Print Stack Trace ===");
try {
throw new IllegalArgumentException("Invalid argument");
} catch (IllegalArgumentException e) {
e.printStackTrace(); // Prints full stack trace for debugging
}
System.out.println("\n=== Suppressed Exceptions ===");
// In try-with-resources, exceptions from close() are suppressed
try (CloseableResource resource = new CloseableResource()) {
throw new IOException("Main exception");
} catch (IOException e) {
System.out.println("Main: " + e.getMessage());
for (Throwable suppressed : e.getSuppressed()) {
System.out.println("Suppressed: " + suppressed.getMessage());
}
}
System.out.println("\n=== Nested Try-Catch ===");
try {
int[] numbers = {1, 2, 3};
try {
System.out.println(numbers[5]); // Inner exception
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Inner catch: " + e.getMessage());
throw new RuntimeException("Wrapped", e); // Re-throw
}
} catch (RuntimeException e) {
System.out.println("Outer catch: " + e.getMessage());
}
System.out.println("\n=== Throws Declaration ===");
try {
methodThatThrows();
} catch (IOException e) {
System.out.println("Caught from method: " + e.getMessage());
}
}
// Method declaring checked exception
static void methodThatThrows() throws IOException {
throw new IOException("Method declared to throw IOException");
}
// Custom AutoCloseable resource
static class CloseableResource implements AutoCloseable {
@Override
public void close() throws IOException {
throw new IOException("Error while closing resource");
}
}
}๐พ File I/O: Reading and Writing Files
Java provides multiple APIs for file operations: java.io (stream-based), java.nio (modern, buffer-based), and Files utility class (Java 7+).
๐ java.io Package
Stream-based I/O with FileReader, FileWriter, BufferedReader, BufferedWriter. Good for simple operations but less performant for large files.
โก java.nio Package (NIO.2)
Modern file API with Path, Files, and Paths classes. Provides better performance, symbolic link handling, and file system operations.
๐ Key Operations
- Reading: Files.readAllLines(), BufferedReader, Scanner
- Writing: Files.write(), BufferedWriter, PrintWriter
- Directory: Files.list(), Files.walk(), FileVisitor
- Operations: Copy, move, delete, create directories
Choosing the Right I/O API
Use NIO.2 (Files class) for most operations - cleaner API, better performance, and powerful features like file tree walking. Use BufferedReader for reading text files line by line. Use Scanner for parsing structured data. Use ObjectInputStream for serialization.
Performance tip: Buffered I/O (BufferedReader/BufferedWriter) is 10-100x faster than unbuffered. For very large files, use memory-mapped files (FileChannel). Always use try-with-resources to ensure proper cleanup.
// File I/O Comprehensive Guide
import java.io.*;
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Stream;
public class FileIODemo {
private static final String TEST_FILE = "sample.txt";
private static final String OUTPUT_FILE = "output.txt";
public static void main(String[] args) {
System.out.println("=== java.io File Operations ===");
demoJavaIO();
System.out.println("\n=== java.nio.file File Operations ===");
demoNIO();
System.out.println("\n=== Directory Operations ===");
demoDirectoryOperations();
System.out.println("\n=== File Attributes ===");
demoFileAttributes();
System.out.println("\n=== File Searching ===");
demoFileSearch();
System.out.println("\n=== File Copying ===");
demoCopyOperations();
}
static void demoJavaIO() {
// ========== Writing with FileWriter ==========
System.out.println("Writing with FileWriter:");
try (FileWriter writer = new FileWriter(TEST_FILE)) {
writer.write("Hello, Java File I/O!\n");
writer.write("This is line 2\n");
writer.write("This is line 3\n");
System.out.println("File written successfully");
} catch (IOException e) {
System.err.println("Error writing: " + e.getMessage());
}
// ========== Writing with BufferedWriter (more efficient) ==========
System.out.println("\nWriting with BufferedWriter:");
try (BufferedWriter bw = new BufferedWriter(new FileWriter(OUTPUT_FILE))) {
for (int i = 1; i <= 5; i++) {
bw.write("Line " + i + ": Java is powerful");
bw.newLine();
}
System.out.println("Buffered write complete");
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
// ========== Reading with BufferedReader ==========
System.out.println("\nReading with BufferedReader:");
try (BufferedReader br = new BufferedReader(new FileReader(TEST_FILE))) {
String line;
int lineNum = 1;
while ((line = br.readLine()) != null) {
System.out.println("Line " + lineNum + ": " + line);
lineNum++;
}
} catch (IOException e) {
System.err.println("Error reading: " + e.getMessage());
}
// ========== Reading with Scanner ==========
System.out.println("\nReading with Scanner:");
try (Scanner scanner = new Scanner(new File(TEST_FILE))) {
while (scanner.hasNextLine()) {
System.out.println("Scanner: " + scanner.nextLine());
}
} catch (FileNotFoundException e) {
System.err.println("File not found: " + e.getMessage());
}
// ========== PrintWriter (formatted output) ==========
System.out.println("\nPrintWriter (formatted output):");
try (PrintWriter pw = new PrintWriter(new FileWriter("formatted.txt"))) {
pw.printf("Name: %s, Age: %d, Score: %.2f%n", "Alice", 25, 92.5);
pw.printf("Name: %s, Age: %d, Score: %.2f%n", "Bob", 30, 88.3);
System.out.println("Formatted file created");
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
}
static void demoNIO() {
Path path = Paths.get(TEST_FILE);
// ========== Read all lines at once ==========
System.out.println("Reading all lines (NIO):");
try {
List lines = Files.readAllLines(path, StandardCharsets.UTF_8);
for (String line : lines) {
System.out.println("NIO: " + line);
}
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
// ========== Write lines with Files.write ==========
System.out.println("\nWriting lines (NIO):");
List newLines = Arrays.asList(
"NIO Line 1: Modern file API",
"NIO Line 2: More efficient",
"NIO Line 3: Built-in charset support"
);
try {
Path outputPath = Paths.get("nio_output.txt");
Files.write(outputPath, newLines, StandardCharsets.UTF_8);
System.out.println("NIO file written");
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
// ========== Read as String (Java 11+) ==========
System.out.println("\nReading entire file as String:");
try {
String content = Files.readString(Path.of("nio_output.txt"));
System.out.println("Content: " + content);
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
// ========== Stream lines (memory efficient for large files) ==========
System.out.println("\nStreaming lines (memory efficient):");
try (Stream lines = Files.lines(Paths.get(TEST_FILE))) {
lines.filter(line -> line.contains("Java"))
.forEach(line -> System.out.println("Contains Java: " + line));
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
// ========== FileChannel for binary operations ==========
System.out.println("\nFileChannel operations:");
try (FileChannel channel = FileChannel.open(Paths.get("channel_test.txt"),
StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
String data = "Channel data";
channel.write(ByteBuffer.wrap(data.getBytes()));
System.out.println("Channel write complete");
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
}
static void demoDirectoryOperations() {
Path dir = Paths.get("test_directory");
// ========== Create directory ==========
System.out.println("Creating directory:");
try {
if (!Files.exists(dir)) {
Files.createDirectory(dir);
System.out.println("Directory created: " + dir);
} else {
System.out.println("Directory already exists");
}
} catch (IOException e) {
System.err.println("Error creating directory: " + e.getMessage());
}
// ========== Create nested directories ==========
Path nestedDir = Paths.get("test_directory/subdir/nested");
try {
Files.createDirectories(nestedDir); // Creates all parent directories
System.out.println("Nested directory created: " + nestedDir);
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
// ========== List directory contents ==========
System.out.println("\nListing directory contents:");
try (Stream paths = Files.list(Paths.get("."))) {
paths.filter(Files::isRegularFile)
.forEach(p -> System.out.println("File: " + p.getFileName()));
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
// ========== Walk directory tree ==========
System.out.println("\nWalking directory tree:");
try (Stream walk = Files.walk(Paths.get("test_directory"))) {
walk.forEach(p -> {
String type = Files.isDirectory(p) ? "[DIR]" : "[FILE]";
System.out.println(type + " " + p);
});
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
// ========== Delete directory recursively ==========
System.out.println("\nDeleting directory tree:");
try {
Files.walk(Paths.get("test_directory"))
.sorted(Comparator.reverseOrder())
.forEach(p -> {
try {
Files.delete(p);
System.out.println("Deleted: " + p);
} catch (IOException e) {
System.err.println("Failed to delete: " + p);
}
});
System.out.println("Directory tree deleted");
} catch (IOException e) {
System.err.println("Error walking directory: " + e.getMessage());
}
}
static void demoFileAttributes() {
Path path = Paths.get(TEST_FILE);
try {
// ========== Basic attributes ==========
System.out.println("File: " + path);
System.out.println("Exists: " + Files.exists(path));
System.out.println("Is readable: " + Files.isReadable(path));
System.out.println("Is writable: " + Files.isWritable(path));
System.out.println("Is executable: " + Files.isExecutable(path));
System.out.println("Is regular file: " + Files.isRegularFile(path));
System.out.println("Is directory: " + Files.isDirectory(path));
System.out.println("Size: " + Files.size(path) + " bytes");
System.out.println("Last modified: " + Files.getLastModifiedTime(path));
// ========== File owner and permissions ==========
System.out.println("Owner: " + Files.getOwner(path));
System.out.println("Posix permissions: " +
Files.getPosixFilePermissions(path, LinkOption.NOFOLLOW_LINKS));
} catch (IOException e) {
System.err.println("Error getting attributes: " + e.getMessage());
}
}
static void demoFileSearch() {
try {
// ========== Find files by name pattern ==========
System.out.println("Finding .java files:");
try (Stream paths = Files.find(Paths.get("."), 10,
(p, attr) -> p.toString().endsWith(".java"))) {
paths.limit(5).forEach(p -> System.out.println(" " + p.getFileName()));
}
// ========== Find files by size ==========
System.out.println("\nFinding files larger than 1KB:");
try (Stream paths = Files.find(Paths.get("."), 5,
(p, attr) -> attr.isRegularFile() && attr.size() > 1024)) {
paths.limit(3).forEach(p -> System.out.println(" " + p.getFileName() +
" (" + p.toFile().length() + " bytes)"));
}
// ========== Find files by modification date ==========
System.out.println("\nFiles modified in last day:");
long oneDayAgo = System.currentTimeMillis() - (24 * 60 * 60 * 1000);
try (Stream paths = Files.find(Paths.get("."), 3,
(p, attr) -> attr.lastModifiedTime().toMillis() > oneDayAgo)) {
paths.limit(5).forEach(p -> System.out.println(" " + p.getFileName()));
}
} catch (IOException e) {
System.err.println("Error searching: " + e.getMessage());
}
}
static void demoCopyOperations() {
Path source = Paths.get(TEST_FILE);
Path target = Paths.get("copy_" + TEST_FILE);
Path moveTarget = Paths.get("moved_" + TEST_FILE);
try {
// ========== Copy file ==========
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
System.out.println("File copied to: " + target);
// ========== Move/Rename file ==========
Files.move(target, moveTarget, StandardCopyOption.REPLACE_EXISTING);
System.out.println("File moved to: " + moveTarget);
// ========== Delete file ==========
Files.delete(moveTarget);
System.out.println("File deleted: " + moveTarget);
// ========== Create temporary file ==========
Path tempFile = Files.createTempFile("temp", ".txt");
System.out.println("Temporary file created: " + tempFile);
Files.write(tempFile, "Temporary content".getBytes());
// Delete on exit (cleanup)
tempFile.toFile().deleteOnExit();
System.out.println("Temporary file will be deleted on exit");
} catch (IOException e) {
System.err.println("Error in copy/move: " + e.getMessage());
}
}
}