👑 Premium Sign in Sign up

Language lab · Beginner → Enterprise

☕ Java

Concept → trace → write code → run tests → explain why → improve → mini-project. Start in the browser if you like; set up your own computer when you are ready.

💻 Runs on your computerOfficial download & docs ↗

📖 Topics with real code (16)

Every verified output below was produced by running the code with openjdk 21.0.13 2026-10-20 LTS on 5 Nov 2026. Open a topic to edit the code and run it on our isolated compile server (sign in needed).

🌱 Beginner (5)

Variables and data types

Java is statically typed: every variable has a fixed type. Primitives: int, long, double, boolean, char. String is a class. var lets the compiler infer the type. Casting converts between types.

public class Main {
    public static void main(String[] args) {
        int age = 15;
        long population = 1_428_000_000L;
        double height = 1.62;
        boolean isStudent = true;
        char grade = 'A';
        String name = "Asha";
        var city = "Hyderabad";            // inferred as String
        System.out.println(name + " " + age + " " + height + " " + isStudent + " " + grade + " " + city);
        System.out.println("population: " + population + ", int max: " + Integer.MAX_VALUE);
        int marks = Integer.parseInt("88") + 2;
        double avg = (double) 7 / 2;       // cast before dividing
        System.out.println(marks + " " + avg + " " + (7 / 2) + " " + (int) 9.99);
    }
}

✅ Verified output

Asha 15 1.62 true A Hyderabad
population: 1428000000, int max: 2147483647
90 3.5 3 9
Operators and boolean logic

Integer division drops the decimal part; % is the remainder. && and || short-circuit. == compares primitives; use .equals() for Strings.

public class Main {
    public static void main(String[] args) {
        int a = 17, b = 5;
        System.out.println((a + b) + " " + (a - b) + " " + (a * b) + " " + (a / b) + " " + (a % b) + " " + Math.pow(a, 2));
        boolean hasTicket = true, isVip = false;
        System.out.println("enter: " + (hasTicket && !isVip) + " either: " + (hasTicket || isVip));
        String s1 = "hello", s2 = new String("hello");
        System.out.println("== " + (s1 == s2) + "  equals " + s1.equals(s2));
        int x = 5;
        x += 3; x *= 2; x++;
        System.out.println("x = " + x + ", bits 6&3=" + (6 & 3) + " 6|3=" + (6 | 3));
    }
}

✅ Verified output

22 12 85 3 2 289.0
enter: true either: true
== false  equals true
x = 17, bits 6&3=2 6|3=7
Conditionals: if, else, switch

if/else picks a branch. The modern switch expression (Java 14+) returns a value and needs no break.

public class Main {
    public static void main(String[] args) {
        int marks = 78;
        String grade;
        if (marks >= 90) grade = "A+";
        else if (marks >= 75) grade = "A";
        else if (marks >= 35) grade = "Pass";
        else grade = "Fail";
        System.out.println("Grade: " + grade);

        String day = "SAT";
        String kind = switch (day) {
            case "SAT", "SUN" -> "Weekend";
            default -> "School day";
        };
        System.out.println(kind + ", ternary: " + (marks >= 35 ? "passed" : "try again"));
    }
}

✅ Verified output

Grade: A
Weekend, ternary: passed
Loops: for, while, do-while, for-each

for counts, while checks first, do-while runs at least once, for-each walks arrays and lists. break and continue control the loop.

public class Main {
    public static void main(String[] args) {
        for (int i = 1; i <= 5; i++) {
            if (i == 3) continue;
            System.out.println("for " + i);
        }
        int n = 10;
        while (n > 0) {
            if (n < 4) break;
            System.out.println("while " + n);
            n -= 3;
        }
        int k = 100;
        do { System.out.println("do-while runs once: " + k); } while (k < 0);
        for (String s : new String[]{"red", "green"}) System.out.println("each " + s);
        for (int r = 1; r <= 3; r++) {
            StringBuilder row = new StringBuilder();
            for (int c = 1; c <= 3; c++) row.append(String.format("%3d", r * c));
            System.out.println(row);
        }
    }
}

✅ Verified output

for 1
for 2
for 4
for 5
while 10
while 7
while 4
do-while runs once: 100
each red
each green
  1  2  3
  2  4  6
  3  6  9
Methods, parameters and overloading

A method has a return type and parameters. Overloading gives several methods the same name with different parameters. Primitives are passed by value.

public class Main {
    static int area(int length, int width) { return length * width; }
    static int area(int side) { return area(side, side); }          // overload
    static double area(double radius, boolean circle) { return Math.PI * radius * radius; }
    static void tryChange(int value) { value = 99; }                  // copy only
    static double average(int... nums) {                              // varargs
        int sum = 0;
        for (int x : nums) sum += x;
        return (double) sum / nums.length;
    }
    public static void main(String[] args) {
        System.out.println(area(5, 3) + " " + area(4) + " " + String.format("%.2f", area(1.0, true)));
        int v = 1;
        tryChange(v);
        System.out.println("still " + v + ", average " + average(4, 9, 2, 7));
    }
}

✅ Verified output

15 16 3.14
still 1, average 5.5

🌿 Intermediate (7)

Arrays and ArrayList

Arrays have a fixed size; ArrayList grows. Arrays.sort and Collections.sort order them; streams can filter and map.

import java.util.*;

public class Main {
    public static void main(String[] args) {
        int[] fixed = {72, 95, 64, 88};
        Arrays.sort(fixed);
        System.out.println(Arrays.toString(fixed) + " length " + fixed.length);
        List<Integer> marks = new ArrayList<>(List.of(72, 95, 64, 88));
        marks.add(91);
        Collections.sort(marks);
        System.out.println(marks + " first " + marks.get(0) + " last " + marks.get(marks.size() - 1));
        List<Integer> oddSquares = new ArrayList<>();
        for (int x = 1; x <= 5; x++) if (x % 2 == 1) oddSquares.add(x * x);
        System.out.println("odd squares " + oddSquares);
        int[][] grid = {{1, 2}, {3, 4}};
        System.out.println("2D: " + Arrays.deepToString(grid));
    }
}

✅ Verified output

[64, 72, 88, 95] length 4
[64, 72, 88, 91, 95] first 64 last 95
odd squares [1, 9, 25]
2D: [[1, 2], [3, 4]]
Strings and StringBuilder

Strings are immutable; StringBuilder is fast for building text in a loop. String.format and text blocks help formatting.

public class Main {
    public static void main(String[] args) {
        String t = "  GayaGnan learning  ".strip();
        System.out.println(t.toUpperCase() + " | " + t.replace("learning", "labs") + " | " + t.length());
        String[] words = t.split(" ");
        System.out.println(String.join("-", words) + " starts with Gaya: " + t.startsWith("Gaya"));
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= 5; i++) sb.append(i).append(i < 5 ? "," : "");
        System.out.println(sb + " reversed " + new StringBuilder("level").reverse());
        System.out.println(String.format("Total: %.2f for %d items", 49.5 * 3, 3));
        String block = """
            Line one
            Line two""";
        System.out.println(block);
    }
}

✅ Verified output

GAYAGNAN LEARNING | GayaGnan labs | 17
GayaGnan-learning starts with Gaya: true
1,2,3,4,5 reversed level
Total: 148.50 for 3 items
Line one
Line two
Maps and sets

HashMap stores key-value pairs; TreeMap keeps keys sorted; HashSet keeps unique items. merge() is handy for counting.

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> stock = new TreeMap<>(Map.of("pen", 40, "book", 12));
        stock.put("eraser", 25);
        stock.put("pen", stock.get("pen") - 5);
        stock.forEach((k, v) -> System.out.println(k + " " + v));
        System.out.println("has ruler? " + stock.containsKey("ruler") + ", ruler or 0: " + stock.getOrDefault("ruler", 0));
        Set<String> a = new TreeSet<>(Set.of("maths", "science", "art"));
        Set<String> both = new TreeSet<>(a);
        both.retainAll(Set.of("science", "music"));
        System.out.println("both " + both);
        Map<String, Integer> freq = new TreeMap<>();
        for (String w : "the cat and the hat and the bat".split(" ")) freq.merge(w, 1, Integer::sum);
        System.out.println(freq);
    }
}

✅ Verified output

book 12
eraser 25
pen 35
has ruler? false, ruler or 0: 0
both [science]
{and=2, bat=1, cat=1, hat=1, the=3}
Classes, encapsulation and records

private fields with public methods protect an object's data. A record (Java 16+) is a short way to write an immutable data class.

public class Main {
    static class BankAccount {
        private final String owner;
        private double balance;
        BankAccount(String owner, double balance) { this.owner = owner; this.balance = balance; }
        void deposit(double amount) {
            if (amount <= 0) throw new IllegalArgumentException("amount must be positive");
            balance += amount;
        }
        double getBalance() { return balance; }
        String getOwner() { return owner; }
    }
    record Student(String name, int marks) {}

    public static void main(String[] args) {
        BankAccount acc = new BankAccount("Asha", 500);
        acc.deposit(250);
        System.out.println(acc.getOwner() + " " + acc.getBalance());
        Student s = new Student("Ravi", 95);
        System.out.println(s + " " + s.name() + " equal: " + s.equals(new Student("Ravi", 95)));
    }
}

✅ Verified output

Asha 750.0
Student[name=Ravi, marks=95] Ravi equal: true
Inheritance, interfaces and polymorphism

An interface lists methods a class promises to provide; abstract classes share code. The same loop calls the right area() for each shape.

import java.util.List;

public class Main {
    interface Shape { double area(); default String describe() { return getClass().getSimpleName() + " " + String.format("%.2f", area()); } }
    static class Circle implements Shape { double r; Circle(double r) { this.r = r; } public double area() { return Math.PI * r * r; } }
    static class Rect implements Shape { double w, h; Rect(double w, double h) { this.w = w; this.h = h; } public double area() { return w * h; } }
    static abstract class Animal { abstract String sound(); String speak() { return "says " + sound(); } }
    static class Dog extends Animal { String sound() { return "Woof"; } }

    public static void main(String[] args) {
        for (Shape s : List.of(new Circle(1), new Rect(3, 4))) System.out.println(s.describe());
        Animal a = new Dog();
        System.out.println("Dog " + a.speak() + ", is Animal: " + (a instanceof Animal));
    }
}

✅ Verified output

Circle 3.14
Rect 12.00
Dog says Woof, is Animal: true
Exceptions

try/catch/finally handles errors. Checked exceptions must be declared or caught; custom exceptions extend Exception or RuntimeException. try-with-resources closes things automatically.

public class Main {
    static class InsufficientFundsException extends Exception {
        InsufficientFundsException(String m) { super(m); }
    }
    static double withdraw(double balance, double amount) throws InsufficientFundsException {
        if (amount > balance) throw new InsufficientFundsException("need " + (amount - balance) + " more");
        return balance - amount;
    }
    public static void main(String[] args) {
        try {
            System.out.println(10 / 0);
        } catch (ArithmeticException e) {
            System.out.println("ArithmeticException: " + e.getMessage());
        } finally {
            System.out.println("finally always runs");
        }
        try {
            withdraw(100, 150);
        } catch (InsufficientFundsException e) {
            System.out.println("Custom: " + e.getMessage());
        }
        try { Integer.parseInt("abc"); } catch (NumberFormatException e) { System.out.println("Bad number"); }
    }
}

✅ Verified output

ArithmeticException: / by zero
finally always runs
Custom: need 50.0 more
Bad number
Files with java.nio

Files.writeString and Files.readAllLines handle text files. Paths are built with Path.of. The file goes to the temp folder and is deleted after.

import java.nio.file.*;
import java.util.List;

public class Main {
    public static void main(String[] args) throws Exception {
        Path file = Files.createTempFile("marks", ".csv");
        Files.writeString(file, "name,marks\nAsha,88\nRavi,95\n");
        List<String> lines = Files.readAllLines(file);
        int total = 0;
        for (String line : lines.subList(1, lines.size())) {
            String[] parts = line.split(",");
            System.out.println(parts[0] + " -> " + parts[1]);
            total += Integer.parseInt(parts[1]);
        }
        System.out.println("average " + (double) total / (lines.size() - 1));
        Files.delete(file);
        System.out.println("deleted: " + !Files.exists(file));
    }
}

✅ Verified output

Asha -> 88
Ravi -> 95
average 91.5
deleted: true

🌳 Advanced (4)

Recursion and memoization

A recursive method calls itself with a smaller input. A HashMap cache turns the slow fib into a fast one; long holds fib(80).

import java.util.*;

public class Main {
    static Map<Integer, Long> memo = new HashMap<>();
    static long fib(int n) {
        if (n < 2) return n;
        if (memo.containsKey(n)) return memo.get(n);
        long v = fib(n - 1) + fib(n - 2);
        memo.put(n, v);
        return v;
    }
    static int binarySearch(int[] a, int target, int lo, int hi) {
        if (lo > hi) return -1;
        int mid = (lo + hi) >>> 1;
        if (a[mid] == target) return mid;
        return a[mid] < target ? binarySearch(a, target, mid + 1, hi) : binarySearch(a, target, lo, mid - 1);
    }
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 10; i++) sb.append(fib(i)).append(' ');
        System.out.println(sb + "| fib(80) = " + fib(80));
        int[] sorted = {2, 5, 8, 12, 16, 23, 38};
        System.out.println("index of 23: " + binarySearch(sorted, 23, 0, sorted.length - 1));
    }
}

✅ Verified output

0 1 1 2 3 5 8 13 21 34 | fib(80) = 23416728348467685
index of 23: 5
Generics, lambdas and streams

Generics make code type-safe for any type. Lambdas are short functions; the Stream API filters, maps, sorts and groups data.

import java.util.*;
import java.util.function.Function;
import java.util.stream.*;

public class Main {
    record Student(String name, String cls, int marks) {}
    static <T> T applyTwice(Function<T, T> f, T x) { return f.apply(f.apply(x)); }
    static <T extends Comparable<T>> T max(List<T> list) { return Collections.max(list); }

    public static void main(String[] args) {
        List<Student> s = List.of(new Student("Asha", "8A", 88), new Student("Ravi", "8B", 95), new Student("Meena", "8A", 72));
        System.out.println(s.stream().filter(x -> x.marks() >= 80).map(Student::name).collect(Collectors.toList()));
        System.out.println(s.stream().sorted(Comparator.comparingInt(Student::marks).reversed()).map(Student::name).toList());
        System.out.println(new TreeMap<>(s.stream().collect(Collectors.groupingBy(Student::cls, Collectors.averagingInt(Student::marks)))));
        System.out.println(applyTwice(n -> n * 3, 2) + " " + applyTwice(t -> t + "!", "hi") + " " + max(List.of(4, 9, 2)));
    }
}

✅ Verified output

[Asha, Ravi]
[Ravi, Asha, Meena]
{8A=80.0, 8B=95.0}
18 hi!! 9
Concurrency: threads and executors

An ExecutorService runs tasks on a pool of threads; Future gets each result. AtomicInteger is a thread-safe counter, so 4 threads × 1000 increments give exactly 4000.

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    public static void main(String[] args) throws Exception {
        ExecutorService pool = Executors.newFixedThreadPool(3);
        long start = System.currentTimeMillis();
        List<Future<Integer>> results = new ArrayList<>();
        for (int i = 1; i <= 3; i++) {
            final int n = i;
            results.add(pool.submit(() -> { Thread.sleep(200); return n * n; }));
        }
        List<Integer> squares = new ArrayList<>();
        for (Future<Integer> f : results) squares.add(f.get());
        System.out.println("squares " + squares + " fast: " + (System.currentTimeMillis() - start < 550));
        AtomicInteger counter = new AtomicInteger();
        List<Callable<Void>> jobs = new ArrayList<>();
        for (int t = 0; t < 4; t++) jobs.add(() -> { for (int i = 0; i < 1000; i++) counter.incrementAndGet(); return null; });
        pool.invokeAll(jobs);
        pool.shutdown();
        System.out.println("counter " + counter.get());
    }
}

✅ Verified output

squares [1, 4, 9] fast: true
counter 4000
References, equality and the heap

Objects live on the heap and variables hold references. Two references can point to one object. equals/hashCode decide value equality, which HashSet relies on.

import java.util.*;

public class Main {
    static class Point {
        final int x, y;
        Point(int x, int y) { this.x = x; this.y = y; }
        @Override public boolean equals(Object o) { return o instanceof Point p && p.x == x && p.y == y; }
        @Override public int hashCode() { return Objects.hash(x, y); }
    }
    static void addMark(List<Integer> list) { list.add(100); }   // changes the caller's list

    public static void main(String[] args) {
        List<Integer> a = new ArrayList<>(List.of(1, 2));
        List<Integer> b = a;               // same object
        List<Integer> c = new ArrayList<>(a); // copy
        addMark(a);
        System.out.println("a " + a + " b " + b + " c " + c + " same: " + (a == b));
        Point p1 = new Point(1, 2), p2 = new Point(1, 2);
        System.out.println("== " + (p1 == p2) + " equals " + p1.equals(p2) + " set size " + new HashSet<>(List.of(p1, p2)).size());
        List<Integer> fixed = List.of(1, 2);
        try { fixed.add(3); } catch (UnsupportedOperationException e) { System.out.println("List.of is immutable"); }
    }
}

✅ Verified output

a [1, 2, 100] b [1, 2, 100] c [1, 2] same: true
== false equals true set size 1
List.of is immutable

🛠️ Set up and verify

  1. Install
    winget install EclipseAdoptium.Temurin.21.JDK
    brew install openjdk@21
    sudo apt install openjdk-21-jdk
  2. Editor: Visual Studio Code with Extension Pack for Java (or IntelliJ IDEA Community).
  3. Create Hello.java
    public class Hello {
        public static void main(String[] args) {
            System.out.println("Hello, GayaGnan!");
        }
    }
  4. Run it and check the version
    javac Hello.java && java Hello
    java -version
  5. Health check: paste what your terminal printed.

Install one language at a time. These commands only install or print versions; nothing here deletes files. Versions change: check the official page and note the version you installed.

🧭 The 14-lab path

  • 00 · Setup and verify — install the runtime and editor, run hello world, print the version, fix PATH
  • 01 · Syntax and trace — follow each line and the value of every variable
  • 02 · Logic — conditionals and decision trees
  • 03 · Loops — trace tables and loop challenges
  • 04 · Functions — break problems down and test each piece
  • 05 · Data structures — lists, maps, stacks, queues and trees you can see
  • 06 · Debugging — bug cases with a hint ladder
  • 07 · Algorithms — search, sort and Big-O comparison
  • 08 · Files and data — read and write fixture files
  • 09 · API or database — talk to a mock API or a sandbox database
  • 10 · Testing — write tests and read failing assertions
  • 11 · Security — secure coding and dependency hygiene for this language
  • 12 · Project — one portfolio mini-project
  • 13 · Assessment — quiz, coding test, rubric and an explain-your-answer prompt

📚 Modules

  1. syntax
  2. control flow
  3. methods
  4. arrays and strings
  5. classes and objects
  6. encapsulation
  7. inheritance and interfaces
  8. exceptions
  9. collections
  10. generics
  11. streams
  12. files
  13. concurrency basics
  14. JUnit testing
  15. JDBC concepts
  16. Spring Boot REST

🏗️ Mini-projects

  • Library management (in-memory)
  • Bank account simulator (no real money)
  • REST student service
  • Order workflow

Projects use synthetic data only.