Java is a statically-typed, object-oriented, platform-independent programming language first released by Sun Microsystems in 1995. It follows the principle of 'Write Once, Run Anywhere' — code compiled on Windows runs unchanged on Linux or macOS. Java is the backbone of Android development, enterprise backend systems, and large-scale distributed applications at companies like Google, Amazon, LinkedIn, and Netflix.


Step 1 — JDK vs JRE vs JVM: Understanding the Ecosystem

Before writing a single line of Java, you must understand the three components that make Java work.

JVM — Java Virtual Machine

  • An abstract computing machine that executes Java bytecode.
  • It is NOT a physical machine — it is a software layer.
  • Handles memory management, garbage collection, and security.
  • Makes Java platform-independent: JVM is the only platform-dependent piece.

JRE — Java Runtime Environment

  • JRE = JVM + Java standard class libraries (java.lang, java.util, etc.).
  • Used to RUN Java programs. End-users need the JRE.
  • Does NOT include development tools like the compiler (javac).

JDK — Java Development Kit

  • JDK = JRE + development tools (javac compiler, debugger, javadoc, jar).
  • Used to DEVELOP and compile Java programs. Developers need the JDK.
  • Download: JDK 21 LTS from https://adoptium.net (recommended).

Step 2 — Your First Java Program

Every Java program starts with a class. The entry point for any Java application is the main method with an exact signature.

HelloWorld.javajava
// Every Java file must have a public class matching the filename
public class HelloWorld {

    // The entry point: JVM calls this method to start your program
    // 'public'  — accessible from anywhere
    // 'static'  — belongs to the class, not an instance
    // 'void'    — returns nothing
    // 'String[] args' — command-line arguments array
    public static void main(String[] args) {
        System.out.println("Hello, World!"); // prints to stdout with newline
        System.out.print("No newline here"); // prints without newline
        System.out.printf("Name: %s, Age: %d%n", "Kuldeep", 25); // formatted
    }
}
Terminal — Compile and Runbash
# Compile: generates HelloWorld.class in the same directory
javac HelloWorld.java

# Run: JVM loads HelloWorld.class and calls main()
java HelloWorld

# Output:
# Hello, World!
# No newline hereName: Kuldeep, Age: 25

Step 3 — Data Types: Primitives vs Reference Types

Java has two categories of data types. Primitive types store raw values directly in memory (stack). Reference types store a memory address (pointer) to an object on the heap.

8 Primitive Data Types

  • byte — 8-bit integer. Range: -128 to 127. Use for raw binary data.
  • short — 16-bit integer. Range: -32,768 to 32,767.
  • int — 32-bit integer. Range: ~-2.1 billion to 2.1 billion. Default for integers.
  • long — 64-bit integer. Range: ±9.2 × 10^18. Suffix with L: 100L.
  • float — 32-bit floating point. Suffix with f: 3.14f. Imprecise.
  • double — 64-bit floating point. Default for decimals. More precise than float.
  • boolean — true or false. 1 bit of information.
  • char — 16-bit Unicode character. Single quotes: 'A', '\n', '\u0041'.
DataTypes.javajava
public class DataTypes {
    public static void main(String[] args) {

        // --- Primitive Types ---
        byte   b  = 127;          // max byte value
        short  s  = 32000;
        int    i  = 2_000_000;    // underscores for readability (Java 7+)
        long   l  = 9_000_000_000L; // L suffix required for long literals
        float  f  = 3.14f;        // f suffix required for float literals
        double d  = 3.14159265358979;
        boolean flag = true;
        char   c  = 'A';          // Unicode: c = '\u0041' is also 'A'

        System.out.println("int max: " + Integer.MAX_VALUE);   // 2147483647
        System.out.println("long max: " + Long.MAX_VALUE);     // 9223372036854775807
        System.out.println("double max: " + Double.MAX_VALUE); // 1.7976931348623157E308

        // --- Reference Types ---
        String name = "Kuldeep";   // String is a class, not a primitive
        int[]  nums = {1, 2, 3};   // Array is a reference type
        String nullStr = null;     // Reference types can be null; primitives CANNOT

        // --- Autoboxing: primitive <-> Wrapper class ---
        Integer boxed = 42;        // int  autoboxed to Integer object
        int     unboxed = boxed;   // Integer unboxed back to int

        // Wrapper classes: Integer, Long, Double, Float, Boolean, Character, Byte, Short
    }
}

Step 4 — Variables: Declaration, Initialization, and Scope

Variables.javajava
public class Variables {

    // --- Class-level (field) variables ---
    static int classVar = 10;   // belongs to the class
    int instanceVar = 20;        // belongs to each object instance

    public static void main(String[] args) {

        // --- Local variables: declared inside a method ---
        int x;           // declared but NOT initialized
        x = 5;           // must initialize before use — compiler error otherwise
        int y = 10;      // declare + initialize in one step

        // --- var (local type inference, Java 10+) ---
        var message = "Hello"; // compiler infers type as String
        var count   = 42;      // compiler infers type as int
        // var can ONLY be used for local variables, not fields or parameters

        // --- final: makes a variable a constant (cannot be reassigned) ---
        final double PI = 3.14159;
        // PI = 3.0; // COMPILE ERROR: cannot assign to final variable

        // --- Scope: variable lives within the block {} it was declared in ---
        {
            int inner = 100; // only accessible inside this block
            System.out.println(inner); // OK
        }
        // System.out.println(inner); // COMPILE ERROR: inner is out of scope

        System.out.println("x=" + x + ", y=" + y + ", message=" + message);
    }
}

Step 5 — Operators

Java operators are divided into groups. Understanding operator precedence prevents subtle bugs.

Operators.javajava
public class Operators {
    public static void main(String[] args) {

        // --- Arithmetic Operators ---
        int a = 10, b = 3;
        System.out.println(a + b);  // 13  — addition
        System.out.println(a - b);  // 7   — subtraction
        System.out.println(a * b);  // 30  — multiplication
        System.out.println(a / b);  // 3   — integer division (truncates decimal)
        System.out.println(a % b);  // 1   — modulo (remainder)
        System.out.println((double) a / b); // 3.3333 — cast to double first!

        // --- Increment / Decrement ---
        int x = 5;
        System.out.println(x++); // 5  — post-increment: returns THEN increments
        System.out.println(x);   // 6
        System.out.println(++x); // 7  — pre-increment: increments THEN returns

        // --- Assignment Operators ---
        int n = 10;
        n += 5;  // n = n + 5  = 15
        n -= 3;  // n = n - 3  = 12
        n *= 2;  // n = n * 2  = 24
        n /= 4;  // n = n / 4  = 6
        n %= 4;  // n = n % 4  = 2

        // --- Comparison Operators (return boolean) ---
        System.out.println(10 == 10); // true
        System.out.println(10 != 5);  // true
        System.out.println(10 > 5);   // true
        System.out.println(10 < 5);   // false
        System.out.println(10 >= 10); // true
        System.out.println(10 <= 9);  // false

        // --- Logical Operators ---
        boolean p = true, q = false;
        System.out.println(p && q); // false — AND: both must be true
        System.out.println(p || q); // true  — OR: at least one must be true
        System.out.println(!p);     // false — NOT: inverts

        // Short-circuit evaluation:
        // In (a && b): if a is false, b is NEVER evaluated
        // In (a || b): if a is true,  b is NEVER evaluated
        int[] arr = null;
        if (arr != null && arr.length > 0) { // safe: arr.length not evaluated if null
            System.out.println(arr[0]);
        }

        // --- Bitwise Operators ---
        System.out.println(5 & 3);  // 1  — AND per bit: 101 & 011 = 001
        System.out.println(5 | 3);  // 7  — OR  per bit: 101 | 011 = 111
        System.out.println(5 ^ 3);  // 6  — XOR per bit: 101 ^ 011 = 110
        System.out.println(~5);     // -6 — NOT per bit (inverts all bits)
        System.out.println(5 << 1); // 10 — left shift = multiply by 2
        System.out.println(20 >> 2);// 5  — right shift = divide by 4

        // --- Ternary Operator ---
        int age = 20;
        String status = (age >= 18) ? "Adult" : "Minor";
        System.out.println(status); // Adult

        // --- instanceof Operator ---
        Object obj = "Hello";
        System.out.println(obj instanceof String); // true
        // Java 16+ pattern matching:
        if (obj instanceof String str) { // casts and binds in one step
            System.out.println(str.toUpperCase()); // HELLO
        }
    }
}

Step 6 — Control Flow: if, switch, loops

ControlFlow.javajava
public class ControlFlow {
    public static void main(String[] args) {

        // =========================================
        // IF / ELSE IF / ELSE
        // =========================================
        int score = 85;
        if (score >= 90) {
            System.out.println("Grade: A");
        } else if (score >= 80) {
            System.out.println("Grade: B"); // prints this
        } else if (score >= 70) {
            System.out.println("Grade: C");
        } else {
            System.out.println("Grade: F");
        }

        // =========================================
        // SWITCH STATEMENT (traditional)
        // =========================================
        int day = 3;
        switch (day) {
            case 1:
                System.out.println("Monday");
                break;              // break prevents fall-through to next case
            case 2:
                System.out.println("Tuesday");
                break;
            case 3:
                System.out.println("Wednesday"); // prints this
                break;
            default:
                System.out.println("Other day");
        }

        // =========================================
        // SWITCH EXPRESSION (Java 14+, preferred modern style)
        // =========================================
        String dayName = switch (day) {
            case 1 -> "Monday";
            case 2 -> "Tuesday";
            case 3 -> "Wednesday";
            case 4 -> "Thursday";
            case 5 -> "Friday";
            default -> "Weekend";
        };
        System.out.println(dayName); // Wednesday

        // =========================================
        // FOR LOOP
        // =========================================
        for (int i = 0; i < 5; i++) {
            System.out.print(i + " "); // 0 1 2 3 4
        }
        System.out.println();

        // =========================================
        // ENHANCED FOR LOOP (for-each) — for arrays/collections
        // =========================================
        int[] numbers = {10, 20, 30, 40, 50};
        for (int num : numbers) {
            System.out.print(num + " "); // 10 20 30 40 50
        }
        System.out.println();

        // =========================================
        // WHILE LOOP
        // =========================================
        int count = 0;
        while (count < 3) {
            System.out.println("count = " + count);
            count++;
        }

        // =========================================
        // DO-WHILE LOOP — executes body at least once
        // =========================================
        int x = 10;
        do {
            System.out.println("do-while: x = " + x); // prints even though x >= 10
            x++;
        } while (x < 10); // condition is false, but body ran once

        // =========================================
        // BREAK and CONTINUE
        // =========================================
        for (int i = 0; i < 10; i++) {
            if (i == 3) continue; // skip 3
            if (i == 6) break;    // stop at 6
            System.out.print(i + " "); // 0 1 2 4 5
        }
        System.out.println();

        // Labeled break (break out of outer loop from inner loop)
        outer:
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (i == 1 && j == 1) break outer; // exits both loops
                System.out.print("(" + i + "," + j + ") ");
            }
        }
        // Output: (0,0) (0,1) (0,2) (1,0)
    }
}

Step 7 — Arrays

Arrays in Java are fixed-size, ordered collections of elements of the same type. Once created, the size cannot change.

Arrays.javajava
import java.util.Arrays; // for Arrays.sort(), Arrays.toString()

public class ArraysExample {
    public static void main(String[] args) {

        // --- 1D Array Declaration and Initialization ---
        int[] arr1 = new int[5];         // creates array of 5 zeros
        int[] arr2 = {1, 2, 3, 4, 5};   // array literal (inline init)
        int[] arr3 = new int[]{10, 20};  // explicit new with values

        // Access by index (0-based)
        arr1[0] = 100;
        arr1[1] = 200;
        System.out.println(arr1[0]);     // 100
        System.out.println(arr2.length); // 5 (not a method! it's a field)

        // ArrayIndexOutOfBoundsException if index >= length or < 0
        // arr2[5] = 99; // RUNTIME ERROR

        // --- Iterating ---
        for (int i = 0; i < arr2.length; i++) {
            System.out.print(arr2[i] + " "); // 1 2 3 4 5
        }

        // --- Sorting ---
        int[] unsorted = {5, 3, 8, 1, 9, 2};
        Arrays.sort(unsorted); // sorts in-place
        System.out.println(Arrays.toString(unsorted)); // [1, 2, 3, 5, 8, 9]

        // --- Binary Search (array must be sorted first!) ---
        int idx = Arrays.binarySearch(unsorted, 5); // returns index of element
        System.out.println("Index of 5: " + idx);   // 3

        // --- Copying ---
        int[] copy = Arrays.copyOf(arr2, arr2.length);   // full copy
        int[] partial = Arrays.copyOfRange(arr2, 1, 4);  // indices 1,2,3 -> {2,3,4}

        // --- 2D Arrays (matrix) ---
        int[][] matrix = new int[3][3]; // 3 rows, 3 columns
        int[][] grid = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        };
        System.out.println(grid[1][2]); // 6 (row 1, col 2)

        // Iterating 2D array
        for (int[] row : grid) {
            for (int val : row) {
                System.out.printf("%3d", val);
            }
            System.out.println();
        }

        // --- Jagged arrays (rows of different lengths) ---
        int[][] jagged = new int[3][];
        jagged[0] = new int[]{1};
        jagged[1] = new int[]{2, 3};
        jagged[2] = new int[]{4, 5, 6};
    }
}

Step 8 — Methods

A method is a named block of reusable code. Methods define behavior. In Java, every method must live inside a class.

Methods.javajava
public class Methods {

    // --- Basic method: no params, no return ---
    static void greet() {
        System.out.println("Hello!");
    }

    // --- Method with parameters and return value ---
    static int add(int a, int b) {
        return a + b;  // 'return' exits the method and sends a value back
    }

    // --- Method overloading: same name, different parameter lists ---
    static double add(double a, double b) {
        return a + b;
    }
    static int add(int a, int b, int c) {
        return a + b + c;
    }

    // --- Varargs: variable number of arguments ---
    static int sum(int... numbers) { // 'numbers' is treated as int[]
        int total = 0;
        for (int n : numbers) total += n;
        return total;
    }

    // --- Pass by value: primitives ---
    static void tryToChange(int x) {
        x = 999; // only modifies the LOCAL copy
    }

    // --- Pass by reference (object reference by value) ---
    static void modifyArray(int[] arr) {
        arr[0] = 999; // modifies the ORIGINAL array through the reference
    }

    // --- Recursive method ---
    static int factorial(int n) {
        if (n <= 1) return 1;        // base case: MUST have one or you get StackOverflowError
        return n * factorial(n - 1); // recursive call
    }
    // factorial(5) = 5 * factorial(4) = 5 * 4 * 3 * 2 * 1 = 120

    public static void main(String[] args) {
        greet();                           // Hello!
        System.out.println(add(3, 4));     // 7  (int version)
        System.out.println(add(1.5, 2.5)); // 4.0 (double version)
        System.out.println(add(1, 2, 3));  // 6  (three-arg version)
        System.out.println(sum(1,2,3,4,5));// 15

        int val = 10;
        tryToChange(val);
        System.out.println(val); // still 10 — primitive was copied

        int[] arr = {1, 2, 3};
        modifyArray(arr);
        System.out.println(arr[0]); // 999 — array was modified!

        System.out.println(factorial(5)); // 120
    }
}

Step 9 — Strings: The Most-Used Reference Type

String in Java is immutable — once created, its characters cannot change. Every string operation that appears to modify a string actually creates a new String object. Strings are stored in the String Pool (a special area of the heap) for memory efficiency.

Strings.javajava
public class Strings {
    public static void main(String[] args) {

        // --- String creation ---
        String s1 = "Hello";           // String literal — goes into String Pool
        String s2 = new String("Hello"); // new object on heap (NOT in pool)

        // == compares REFERENCES, not content!
        System.out.println(s1 == s2);       // false (different objects)
        System.out.println(s1.equals(s2));  // true  (same content) — ALWAYS use .equals()

        // --- Common String methods ---
        String str = "  Hello, World!  ";
        System.out.println(str.length());          // 17
        System.out.println(str.trim());            // "Hello, World!" (removes leading/trailing spaces)
        System.out.println(str.strip());           // "Hello, World!" (Unicode-aware, Java 11+)
        System.out.println(str.toUpperCase());     // "  HELLO, WORLD!  "
        System.out.println(str.toLowerCase());     // "  hello, world!  "
        System.out.println(str.contains("World")); // true
        System.out.println(str.startsWith(" "));   // true
        System.out.println(str.indexOf("World"));  // 8 (or -1 if not found)
        System.out.println(str.substring(7, 12));  // "Hello" — indices 7 to 11
        System.out.println(str.replace("World", "Java")); // "  Hello, Java!  "
        System.out.println(str.isEmpty());         // false
        System.out.println("".isBlank());          // true (Java 11+)

        // --- Splitting ---
        String csv = "apple,banana,cherry";
        String[] fruits = csv.split(",");
        System.out.println(fruits[1]); // banana

        // --- Joining ---
        String joined = String.join(" - ", "one", "two", "three");
        System.out.println(joined); // one - two - three

        // --- String.format and formatted() ---
        String msg = String.format("Name: %s, Age: %d, GPA: %.2f", "Kuldeep", 22, 3.856);
        System.out.println(msg); // Name: Kuldeep, Age: 22, GPA: 3.86

        // --- char operations ---
        String word = "Java";
        char ch = word.charAt(0);       // 'J'
        char[] chars = word.toCharArray();
        String back = new String(chars); // back to String

        // --- Immutability: why it matters ---
        String original = "Hello";
        String upper = original.toUpperCase(); // creates a NEW String object
        System.out.println(original); // "Hello" — unchanged!
        System.out.println(upper);    // "HELLO"

        // --- StringBuilder: mutable string for performance ---
        // Never concatenate strings in a loop with +! Use StringBuilder.
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 5; i++) {
            sb.append("item").append(i).append(", ");
        }
        sb.delete(sb.length() - 2, sb.length()); // remove trailing ", "
        System.out.println(sb.toString()); // item0, item1, item2, item3, item4

        // StringBuilder methods
        StringBuilder sb2 = new StringBuilder("Hello");
        sb2.insert(5, " World");   // "Hello World"
        sb2.reverse();              // "dlroW olleH"
        sb2.replace(0, 5, "Java"); // "Java olleH"
        System.out.println(sb2);   // Java olleH
    }
}

Step 10 — Type Casting

TypeCasting.javajava
public class TypeCasting {
    public static void main(String[] args) {

        // --- Widening (implicit/automatic) casting ---
        // Goes from smaller type to larger type. Safe, no data loss.
        // byte -> short -> int -> long -> float -> double
        int myInt = 9;
        double myDouble = myInt; // automatic: int widened to double
        System.out.println(myDouble); // 9.0

        // --- Narrowing (explicit) casting ---
        // Goes from larger type to smaller type. MUST be explicit. Risk of data loss.
        double pi = 3.99;
        int truncated = (int) pi;  // explicit cast: decimal part LOST
        System.out.println(truncated); // 3 (NOT rounded, truncated)

        long big = 1234567890123L;
        int overflow = (int) big; // data loss! value wraps around
        System.out.println(overflow); // 1912276171 (garbage value)

        // --- Numeric type promotion in expressions ---
        byte x = 10, y = 20;
        // byte result = x + y; // COMPILE ERROR: x+y is promoted to int
        byte result = (byte)(x + y); // must cast back

        // --- char and int interop ---
        char ch = 'A';
        int ascii = ch;          // char widened to int
        System.out.println(ascii); // 65
        char back = (char)(ascii + 1); // 66 -> 'B'
        System.out.println(back);  // B

        // --- String to number conversion ---
        int parsed = Integer.parseInt("42");       // String -> int
        double parsedD = Double.parseDouble("3.14"); // String -> double

        // --- Number to String ---
        String s1 = String.valueOf(100);   // "100"
        String s2 = Integer.toString(100); // "100"
        String s3 = "" + 100;              // "100" (less efficient)
    }
}

Quick Reference: Java Keywords

Most Important Java Keywords

  • public / private / protected — access modifiers controlling visibility.
  • static — belongs to the class itself, not to instances.
  • final — for constants (variables), non-overridable methods, non-inheritable classes.
  • void — method return type meaning 'returns nothing'.
  • new — allocates a new object on the heap.
  • this — refers to the current object instance.
  • super — refers to the parent class.
  • return — exits a method and optionally returns a value.
  • null — a literal meaning 'no object reference'.
  • instanceof — tests whether an object is an instance of a class/interface.
  • try / catch / finally / throw / throws — exception handling.
  • abstract / interface / implements / extends — OOP building blocks.
  • synchronized — makes a method/block thread-safe.
  • import — brings a class or package into scope.