Java.

A comprehensive, interactive masterclass covering every major concept — from JVM internals and OOP fundamentals to modern Java 21 features, the Streams API, and concurrent programming.

Java 21 LTS OOP Streams / Lambda Collections ● 22 Live Demos

§ 01

JVM Architecture

Java compiles to platform-neutral bytecode (.class), which the Java Virtual Machine (JVM) interprets or JIT-compiles. This is what makes Java "write once, run anywhere."

JAVA EXECUTION PIPELINE
Source Code — MyApp.java
Text
↓ javac (compiler)
Bytecode — MyApp.class (platform-neutral)
Binary
↓ java (JVM)
Class Loader
loads .class files
JIT Compiler
hot code → native
GC
automatic memory
↓ native execution
Operating System — Windows / macOS / Linux / Android
Any Platform
HelloWorld.java
// Every Java program starts here
public class HelloWorld {

    // Entry point — JVM calls main()
    public static void main(String[] args) {

        // Print to standard output
        System.out.println("Hello, World!");

        // Print without newline
        System.out.print("No newline");

        // Formatted print (like printf)
        System.out.printf(
            "Name: %s, Age: %d%n", "Alice", 30
        );
    }
}
Java SE vs JDK vs JRE — JDK (Java Development Kit) includes the compiler + JRE. JRE (Java Runtime Environment) includes only the JVM. Java SE is the standard edition. LTS versions: 8, 11, 17, 21.
Compiled & Interpreted — javac produces bytecode; the JVM interprets it, and the JIT compiler optimizes "hot paths" to native machine code at runtime for near-C++ performance.

§ 02

Primitives & Types

Java is statically typed. It has 8 primitive types (stored by value on the stack) and Reference types (objects stored on the heap). Every primitive has a Wrapper class.

8 PRIMITIVE TYPES
TypeSizeRange / NotesDefaultWrapper
byte8-bit-128 to 1270Byte
short16-bit-32,768 to 32,7670Short
int32-bit-2.1B to 2.1B (most common)0Integer
long64-bit±9.2 × 10¹⁸ — use L suffix: 42L0LLong
float32-bit~7 decimal digits — use f: 3.14f0.0fFloat
double64-bit~15 decimal digits (default decimal)0.0dDouble
char16-bitUnicode U+0000 to U+FFFF — 'A''\u0000'Character
booleanJVM dep.true / false onlyfalseBoolean
INTERACTIVE — Type Explorer
java output
DataTypes.java
public class DataTypes {
    public static void main(String[] args) {

        // Primitives
        int     age     = 30;
        long    bigNum  = 9_223_372_036L;  // _ separator for readability
        double  pi      = 3.141592653589;
        boolean isJava  = true;
        char    letter  = 'A';

        // Implicit widening cast (safe)
        int i = 100;
        long l = i;     // int → long: automatic
        double d = i;   // int → double: automatic

        // Explicit narrowing cast (may lose data)
        double x = 9.99;
        int    y = (int) x;  // 9 — truncated, not rounded

        // Autoboxing / Unboxing
        Integer boxed   = 42;        // autobox: int → Integer
        int     unboxed = boxed;     // unbox: Integer → int

        // var — local type inference (Java 10+)
        var name = "Alice";     // inferred as String
        var nums = new int[]{ 1, 2, 3 };

        // Wrapper class utilities
        Integer.MAX_VALUE         // 2_147_483_647
        Integer.parseInt("42")   // String → int
        Double.parseDouble("3.14")
        Integer.toBinaryString(255) // "11111111"
        Integer.toHexString(255)   // "ff"
    }
}

§ 03

Operators

Java operators: arithmetic, relational, logical, bitwise, assignment, ternary, and the instanceof pattern match operator (Java 16+).

INTERACTIVE
java output
Operators.java
// Arithmetic
int a = 17, b = 5;
a + b    // 22    a - b  // 12
a * b    // 85    a / b  // 3 (integer division!)
a % b    // 2 (remainder)
Math.pow(a, b)  // 1419857.0

// Increment / Decrement
int x = 5;
x++;  // post-increment: use THEN add
++x;  // pre-increment:  add THEN use

// Bitwise operators
0b1010 & 0b1100   // AND  → 0b1000 = 8
0b1010 | 0b1100   // OR   → 0b1110 = 14
0b1010 ^ 0b1100   // XOR  → 0b0110 = 6
~0b1010           // NOT  → -11 (two's complement)
8 >> 1            // right shift → 4 (÷2)
4 << 2            // left shift  → 16 (×4)

// Ternary
int max = (a > b) ? a : b;   // 17

// instanceof pattern matching (Java 16+)
Object obj = "Hello";
if (obj instanceof String s) {  // s bound automatically
    System.out.println(s.length());  // no cast needed!
}

§ 04

Control Flow

Java 14+ switch expressions use arrow syntax and return values. Pattern matching in switch (Java 21) is a major modernization.

INTERACTIVE
java output
ControlFlow.java
// ── Classic switch ─────────────────────────────
int day = 3;
switch (day) {
    case 1: case 2: case 3:
    case 4: case 5:
        System.out.println("Weekday"); break;
    case 6: case 7:
        System.out.println("Weekend"); break;
    default: System.out.println("Invalid");
}

// ── Switch expression (Java 14+) — cleaner! ────
String type = switch (day) {
    case 1, 2, 3, 4, 5 -> "Weekday";
    case 6, 7             -> "Weekend";
    default               -> "Unknown";
};

// ── Switch with blocks and yield ──────────────
int score = switch (grade) {
    case "A" -> 4;
    case "B" -> 3;
    case "C" -> { log(); yield 2; }  // yield for blocks
    default  -> 0;
};

// ── Pattern matching in switch (Java 21) ──────
Object obj = 42;
String result = switch (obj) {
    case Integer i when i > 0  -> "Positive int: " + i;
    case Integer i               -> "Non-positive: " + i;
    case String  s               -> "String: " + s;
    case null                     -> "null!";
    default                       -> "Other";
};

§ 05

Loops

Java loops: for, while, do-while, enhanced for-each. Labels allow breaking from nested loops.

INTERACTIVE
java output
Loops.java
// Standard for loop
for (int i = 0; i < 5; i++) {
    System.out.print(i + " ");  // 0 1 2 3 4
}

// Enhanced for-each (Iterable or array)
int[] nums = {10, 20, 30, 40};
for (int n : nums) {
    System.out.print(n + " ");
}

// do-while — body runs at least once
int x = 10;
do {
    System.out.println(x);
    x--;
} while (x > 0);

// Labeled break — exit outer loop
outer:
for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
        if (i + j == 6) break outer;
        System.out.print("(" + i + "," + j + ") ");
    }
}

// Labeled continue — skip to next outer iteration
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (j == 1) continue;  // skip j=1
    }
}

§ 06

Arrays

Fixed-size, ordered, typed containers. Java arrays are objects. The Arrays utility class provides sorting, searching, copying, and filling operations.

INTERACTIVE — Array Operations
array state
java output
Arrays.java
import java.util.Arrays;

// Declaration and initialization
int[] arr  = new int[5];             // [0,0,0,0,0]
int[] arr2 = {5, 3, 8, 1, 9, 2};   // literal
int   len  = arr2.length;           // 6 (not a method!)

// Arrays utility class
Arrays.sort(arr2);                   // [1,2,3,5,8,9] in-place
Arrays.sort(arr2, 1, 4);           // sort subarray [1..4)
int idx = Arrays.binarySearch(arr2, 5); // requires sorted!
Arrays.fill(arr2, 0);              // fill all with 0
Arrays.fill(arr2, 2, 5, 99);       // fill [2..5) with 99
int[] copy = Arrays.copyOf(arr2, 4);       // first 4 elements
int[] range = Arrays.copyOfRange(arr2, 1, 4); // [1..4)
String str = Arrays.toString(arr2); // "[1, 2, 3]"
boolean eq = Arrays.equals(arr, arr2);

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

§ 07

Methods

Java methods support overloading (same name, different parameters), varargs, pass-by-value semantics, and recursion. Return types are enforced at compile time.

INTERACTIVE
java output
Methods.java
// ── METHOD OVERLOADING ─────────────────────────
public int    add(int a, int b)             { return a + b; }
public double add(double a, double b)       { return a + b; }
public String add(String a, String b)       { return a + b; }
public int    add(int a, int b, int c)       { return a+b+c; }

// ── VARARGS ────────────────────────────────────
public int sum(int... nums) {   // treated as int[]
    int total = 0;
    for (int n : nums) total += n;
    return total;
}
sum(1, 2, 3);           // 6
sum(10, 20, 30, 40);    // 100

// ── PASS-BY-VALUE ──────────────────────────────
// Primitives: copies value — original unchanged
void doubleIt(int x) { x *= 2; }  // original unchanged

// Objects: copies REFERENCE — internal state CAN change
void modify(StringBuilder sb) { sb.append("!"); } // affects original

// ── RECURSION ─────────────────────────────────
public long factorial(int n) {
    if (n <= 1) return 1;            // base case
    return n * factorial(n - 1);   // recursive case
}
// factorial(10) = 3628800

§ 08

Strings

Strings are immutable objects in Java. The String Pool caches literals. StringBuilder is mutable and far faster for concatenation in loops. Java 15+ text blocks.

INTERACTIVE — String Operations
java output
StringMethods.java
String s = "  Hello, World!  ";

// Information
s.length()          // 18
s.isEmpty()         // false
s.isBlank()         // false (Java 11+)
s.charAt(7)         // 'W'
s.indexOf("World") // 9

// Transform (returns new String!)
s.trim()             // "Hello, World!"
s.strip()            // unicode-aware trim
s.toLowerCase()      // "  hello, world!  "
s.toUpperCase()
s.replace("l", "L") // "  HeLLo, WorLd!  "
s.substring(9, 14)  // "World"
s.repeat(2)          // Java 11+

// Test
s.contains("World")
s.startsWith("  H")
s.endsWith("!  ")
s.matches(".*World.*")

// Split
"a,b,,c".split(",")   // ["a","b","","c"]
String.join("-", "a","b","c") // "a-b-c"
StringBuilder.java
// StringBuilder — mutable, faster in loops
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(", ");
sb.append("World");
sb.insert(5, "!!!");
sb.delete(5, 8);
sb.reverse();
sb.toString();   // convert to String

// String.format (printf-style)
String.format("%-10s %5.2f", "Price:", 9.99);
// "Price:      9.99"
// %s=String %d=int %f=float
// %-10s=left-aligned 10-wide
// %5.2f=5-wide, 2 decimals

// Text block (Java 15+)
String json = """
    {
      "name": "Alice",
      "age": 30
    }
    """;

// String comparison (IMPORTANT!)
String a = "hello";
String b = new String("hello");
a == b;            // false! (different refs)
a.equals(b);      // true (compares content)
a.equalsIgnoreCase(b); // true

§ 09

Classes & Objects

A class is a blueprint. An object is an instance. Java supports constructor overloading, this chaining, static members, and the builder pattern for immutable objects.

CLASS ANATOMY
class BankAccount
FIELDS
-double balance
-String owner
-static int count
CONSTRUCTORS
+BankAccount(owner)
+BankAccount(owner, bal)
METHODS
+void deposit(amount)
+boolean withdraw(amount)
+double getBalance()
+static int getCount()
Access Modifiers
public — accessible everywhere
private — class only (use for fields!)
protected — class + subclasses + package
(default) — same package only
static — belongs to the class, not instances. Shared across all objects. Call via ClassName.method()
final on a field = constant. On a class = cannot be subclassed. On a method = cannot be overridden.
INTERACTIVE — BankAccount simulation
java output
BankAccount.java
public class BankAccount {
    // Fields — private for encapsulation
    private final String owner;
    private double balance;
    private static int count = 0; // shared

    // Constructor chaining with this()
    public BankAccount(String owner) {
        this(owner, 0.0);        // delegates below
    }
    public BankAccount(String owner, double balance) {
        this.owner   = owner;
        this.balance = balance;
        count++;
    }

    public void deposit(double amount) {
        if (amount <= 0) throw new
            IllegalArgumentException("Amount must be positive");
        balance += amount;
    }
    public boolean withdraw(double amount) {
        if (amount > balance) return false;
        balance -= amount;
        return true;
    }
    public double  getBalance()      { return balance; }
    public static int getCount()    { return count; }

    @Override
    public String toString() {
        return String.format("BankAccount[owner=%s, balance=%.2f]",
                              owner, balance);
    }
}

§ 10

Inheritance

Java supports single class inheritance via extends. Every class implicitly extends Object. super accesses the parent class. Override methods with @Override.

CLASS HIERARCHY
Shape
color, area(), perimeter(), toString()
Circle extends Shape
radius, area(), perimeter()
Rectangle extends Shape
width, height, area(), isSquare()
INTERACTIVE — Inheritance Demo
java output
Inheritance.java
public abstract class Shape {
    protected String color;

    public Shape(String color) { this.color = color; }

    public abstract double area();        // must override
    public abstract double perimeter();

    @Override
    public String toString() {
        return "%s[color=%s, area=%.2f]"
            .formatted(getClass().getSimpleName(), color, area());
    }
}

public class Circle extends Shape {
    private final double radius;

    public Circle(String color, double radius) {
        super(color);         // call parent constructor
        this.radius = radius;
    }

    @Override public double area()      { return Math.PI * radius * radius; }
    @Override public double perimeter() { return 2 * Math.PI * radius; }
}

public class Rectangle extends Shape {
    private final double width, height;

    public Rectangle(String c, double w, double h) {
        super(c); width = w; height = h;
    }

    @Override public double area()      { return width * height; }
    @Override public double perimeter() { return 2 * (width + height); }
    public boolean isSquare()          { return width == height; }
}

§ 11

Encapsulation

Hide internal state with private fields and expose controlled access through getters/setters. Java Records (Java 16+) auto-generate this boilerplate.

INTERACTIVE — Encapsulation Demo
java output
Encapsulation.java
public class Person {
    private String name;
    private int    age;
    private String email;

    // Getter — read-only access
    public String getName()  { return name; }
    public int    getAge()   { return age; }

    // Setter — controlled mutation + validation
    public void setName(String name) {
        if (name == null || name.isBlank())
            throw new IllegalArgumentException("Name required");
        this.name = name.trim();
    }
    public void setAge(int age) {
        if (age < 0 || age > 150)
            throw new IllegalArgumentException("Invalid age");
        this.age = age;
    }
}

// Builder Pattern — fluent API for complex objects
public class User {
    private final String name;
    private final String email;
    private final int    age;

    private User(Builder b) {
        name = b.name; email = b.email; age = b.age;
    }

    public static class Builder {
        private String name, email;
        private int age;
        public Builder name(String n)  { name  = n; return this; }
        public Builder email(String e) { email = e; return this; }
        public Builder age(int a)      { age   = a; return this; }
        public User    build()         { return new User(this); }
    }
}

// Usage
User u = new User.Builder()
    .name("Alice")
    .email("alice@example.com")
    .age(30)
    .build();

§ 12

Polymorphism

One interface, many forms. Compile-time polymorphism = overloading. Runtime polymorphism = overriding via dynamic dispatch. The JVM decides which method to call at runtime.

INTERACTIVE — Polymorphism
java output
Polymorphism.java
// Polymorphic references — parent type, child object
Shape s1 = new Circle("red", 5.0);
Shape s2 = new Rectangle("blue", 4, 6);

// Dynamic dispatch — JVM calls the ACTUAL type's method
s1.area();   // Circle.area()    = 78.54
s2.area();   // Rectangle.area() = 24.0

// Polymorphic array
Shape[] shapes = {
    new Circle("red", 3),
    new Rectangle("blue", 4, 5),
    new Circle("green", 7)
};
double totalArea = 0;
for (Shape s : shapes) {
    totalArea += s.area();  // correct method called each time!
}

// Downcasting — with instanceof guard
for (Shape s : shapes) {
    if (s instanceof Rectangle r) {      // Java 16+ pattern
        System.out.println("Square: " + r.isSquare());
    }
}

// Upcasting is always safe (implicit)
Circle c = new Circle("yellow", 2);
Shape upcast = c;   // implicit, safe

// Downcasting needs explicit cast (may throw ClassCastException)
Circle back = (Circle) upcast;  // safe here
// Rectangle bad = (Rectangle) c; // ClassCastException!