Lecture 1 / 12
Lecture 01 · Foundations

What is Java?

Beginner ~30 min No prerequisites

Introduction

Java is a high-level, class-based, object-oriented programming language that is designed to have as few implementation dependencies as possible. The slogan “Write Once, Run Anywhere” reflects its capability to run on any device that has a Java Virtual Machine (JVM).

Why Learn Java?

  • Ubiquitous – used in Android, enterprise back-ends, desktop apps and big-data ecosystems.
  • Strong static typing helps catch many bugs at compile time.
  • Rich standard library and a huge ecosystem of third-party libraries.
  • Excellent tooling (IDEA, Eclipse, VS Code, Maven/Gradle, JUnit).
✅ Quick fact
The first public release of Java (JDK 1.0) appeared in 1996.

Hello World – Your First Java Program

Save the following as HelloWorld.java and run it from the command line:

HelloWorld.java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

Compile and run:

javac HelloWorld.java   # produces HelloWorld.class
java HelloWorld         # prints “Hello, World!”
🎯 Exercise 1.1

Install the latest JDK, create the HelloWorld.java file, compile it and run it. Verify the output in your terminal.

Key Features of Java

  • Object-Oriented: Everything in Java revolves around objects and classes.
  • Platform Independent: Java code is compiled into bytecode that runs on the JVM.
  • Secure: No direct pointer access and includes built-in security features.
  • Robust: Strong memory management and exception handling.
  • Multithreaded: Supports concurrent execution of two or more threads.

How Java Works

Java follows a two-step execution process:

  1. Compilation: The Java source file (.java) is compiled into bytecode (.class) using the Java compiler (javac).
  2. Execution: The JVM interprets or compiles the bytecode into machine code that your system can execute.
💡 Did you know?
The JVM acts like a translator between Java bytecode and your operating system, making Java platform-independent.

Basic Structure of a Java Program

  • Class: The main building block of a Java program.
  • Main Method: Entry point where execution begins.
  • Statements: Instructions executed by the program.

Common Errors Beginners Make

  • Forgetting to match the file name with the class name.
  • Missing semicolons (;) at the end of statements.
  • Incorrect capitalization (Java is case-sensitive).
  • Not installing or configuring the JDK properly.
🎯 Exercise 1.2

Modify the HelloWorld program to print your name instead of "Hello, World!".

🎯 Exercise 1.3

Try printing multiple lines using System.out.println() and observe how output is displayed.

Summary

In this lecture, you learned what Java is, why it is widely used, and how to write and run your first Java program. You also explored its key features and basic structure. In the next lecture, we will dive deeper into setting up your development environment and understanding variables.

Lecture 02 · Foundations

Setting up the JDK

Beginner ~35 min Requires: Lecture 01

Download & Install

Head to Eclipse Adoptium or Oracle JDK and download the appropriate bundle for your OS.

Verify Installation

java -version
javac -version
ℹ️ Expected output

Both commands should print the version number (e.g. java 21.0.1) and indicate that the JDK is correctly on your PATH.

IDE Recommendation

For a smoother learning experience pick an IDE:

  • IntelliJ IDEA Community – rich refactoring, debugger, and Maven/Gradle integration.
  • Eclipse – classic, plugin-heavy.
  • VS Code with the Java Extension Pack.
🎯 Exercise 2.1

Install your favourite IDE, open a new project, create a Main.java file with a main method, and run the “Hello World” program inside the IDE.

Understanding JDK, JRE, and JVM

  • JDK (Java Development Kit): Full toolkit for developing Java applications.
  • JRE (Java Runtime Environment): Provides libraries and JVM to run Java programs.
  • JVM (Java Virtual Machine): Executes compiled Java bytecode.
💡 Key Insight
Installing the JDK automatically includes the JRE and JVM, so you don’t need to install them separately.

Setting Environment Variables (Important)

To run Java from any directory, you need to configure system environment variables:

Windows

  • Search for Environment Variables in system settings.
  • Add the JDK bin folder path to the PATH variable.

Linux / macOS

export JAVA_HOME=/path/to/jdk
export PATH=$JAVA_HOME/bin:$PATH

Command Line vs IDE

You can write and run Java programs using:

  • Command Line: More control, better for understanding fundamentals.
  • IDE: Faster development with debugging and auto-completion.

Creating Your First Project Structure

A typical Java project structure looks like this:

MyProject/
 ├── src/
 │   └── Main.java
 └── bin/
ℹ️ Note
The src folder contains source code, while compiled files are often placed in a separate directory.

Troubleshooting Common Issues

  • 'java' is not recognized: PATH variable is not set correctly.
  • Version mismatch: Multiple Java versions installed on your system.
  • Permission errors: Try running terminal as administrator (Windows) or use sudo (Linux/macOS).
🎯 Exercise 2.2

Open your terminal and verify that both java and javac commands work from any directory.

🎯 Exercise 2.3

Create a simple project folder manually, write a Java file, compile it using terminal, and run it without using an IDE.

Summary

In this lecture, you installed the JDK, verified your setup, and explored development tools and environment configuration. You also learned the difference between JDK, JRE, and JVM, and how to troubleshoot common setup issues. In the next lecture, we will begin writing more structured Java programs using variables and data types.

Lecture 02 · Foundations

Setting up the JDK

Beginner ~35 min Requires: Lecture 01

Download & Install

Head to Eclipse Adoptium or Oracle JDK and download the appropriate bundle for your OS.

Verify Installation

java -version
javac -version
ℹ️ Expected output

Both commands should print the version number (e.g. java 21.0.1) and indicate that the JDK is correctly on your PATH.

IDE Recommendation

For a smoother learning experience pick an IDE:

  • IntelliJ IDEA Community – rich refactoring, debugger, and Maven/Gradle integration.
  • Eclipse – classic, plugin‑heavy.
  • VS Code with the Java Extension Pack.
🎯 Exercise 2.1

Install your favourite IDE, open a new project, create a Main.java file with a main method, and run the “Hello World” program inside the IDE.

Lecture 03 · Foundations

Variables & Data Types

Beginner ~45 min Requires: Lecture 02

Primitive Types

Java has 8 primitive types:

Type Size Example
byte 8 bit byte b = 100;
short 16 bit short s = 20000;
int 32 bit int i = 123456;
long 64 bit long l = 123456789L;
float 32 bit float f = 3.14f;
double 64 bit double d = 3.14159;
char 16 bit (Unicode) char c = 'A';
boolean 1 bit (logical) boolean ok = true;

Reference Types

Anything that isn’t a primitive is a *reference* type: String, arrays, objects, etc.

VariablesDemo.java
public class VariablesDemo {
    public static void main(String[] args) {
        int age = 30;
        double price = 19.99;
        boolean isMember = true;
        String name = "Alice";

        System.out.println(name + ", age " + age);
    }
}
🎯 Exercise 3.1

Create a program that declares one variable of each primitive type, prints them, then swaps two integer values without using a third temporary variable.

Lecture 04 · Foundations

Control Flow

Beginner ~45 min Requires: Lecture 03

If / else

IfDemo.java
int score = 85;
if (score >= 90) {
    System.out.println("Grade A");
} else if (score >= 80) {
    System.out.println("Grade B");
} else {
    System.out.println("Needs improvement");
}

Switch (Java 17+ enhanced switch)

SwitchDemo.java
String day = "MONDAY";

String result = switch (day) {
    case "MONDAY", "TUESDAY", "WEDNESDAY" -> "Mid‑week";
    case "THURSDAY", "FRIDAY" -> "Almost weekend";
    case "SATURDAY", "SUNDAY" -> "Weekend";
    default -> "Invalid day";
};

System.out.println(result);

Loops

  • for (int i=0; i<n; i++) – classic counting loop.
  • enhanced for – iterate over arrays/collections (for (String s : list)).
  • while / do‑while – condition‑driven loops.
LoopDemo.java
int[] numbers = { 2, 4, 6, 8 };
int sum = 0;

for (int n : numbers) {
    sum += n;
}
System.out.println("Sum = " + sum);
🎯 Exercise 4.1

Write a program that prints the first 20 Fibonacci numbers using a while loop.

Lecture 05 · Foundations

Methods

Beginner ~40 min Requires: Lecture 04

Method Syntax

A method in Java consists of: modifiers returnType name(parameters) { body }

MathUtils.java
public class MathUtils {

    public static int add(int a, int b) {
        return a + b;
    }

    public static double average(int[] values) {
        int sum = 0;
        for (int v : values) {
            sum += v;
        }
        return ( double ) sum / values.length;
    }
}

Parameter Passing

  • Primitives are passed **by value** – the method receives a copy.
  • Objects are passed **by reference value** – the method receives a copy of the reference, allowing it to mutate the object.

Method Overloading

Same name, different parameter list.

OverloadDemo.java
public class OverloadDemo {

    static int max(int a, int b) {
        return (a > b) ? a : b;
    }

    static double max(double a, double b) {
        return (a > b) ? a : b;
    }
}
🎯 Exercise 5.1

Write a utility class that provides overloaded factorial methods for int and long. Add a simple main method that demonstrates both.

Lecture 06 · Core Concepts

Object‑Oriented Programming

Intermediate ~60 min Requires: Lecture 05

Core Pillars

  • Encapsulation – hide internal state behind private fields and expose behaviour via methods.
  • Inheritance – reuse code with extends.
  • Polymorphism – treat objects of different subclasses uniformly, often via interfaces.
  • Abstraction – define contracts with abstract classes or interfaces.

Class definition

Person.java
public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }

    public void birthday() { age++; }
}

Inheritance example

Employee.java
public class Employee extends Person {
    private String department;

    public Employee(String name, int age, String dept) {
        super(name, age);
        department = dept;
    }

    public String getDepartment() { return department; }
}
🎯 Exercise 6.1

Model a simple library system: create a base class Item (with id, title) and subclasses Book and Magazine. Add a method printInfo() that is overridden in each subclass.

Lecture 07 · Core Concepts

Collections Framework

Intermediate ~55 min Requires: Lecture 06

Why Collections?

They provide well‑tested, efficient implementations of common data structures (lists, sets, maps) plus a rich set of utility methods.

List – ordered, duplicates allowed

ListDemo.java
import java.util.*; // List, ArrayList

public class ListDemo {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Alice");   // duplicate allowed

        for (String n : names) {
            System.out.println(n);
        }
    }
}

Set – unique elements

SetDemo.java
import java.util.*; // HashSet

public class SetDemo {
    public static void main(String[] args) {
        Set<Integer> primes = new HashSet<>();
        primes.add(2);
        primes.add(3);
        primes.add(2);  // ignored – duplicate

        System.out.println(primes);
    }
}

Map – key/value pairs

MapDemo.java
import java.util.*; // Map, HashMap

public class MapDemo {
    public static void main(String[] args) {
        Map<String, Integer> scores = new HashMap<>();
        scores.put("Alice", 90);
        scores.put("Bob", 85);
        scores.put("Alice", 95); // overwrites previous

        System.out.println(scores);
    }
}
🎯 Exercise 7.1

Implement a simple phone‑book using a HashMap<String, String> where the key is a person’s name and the value is a phone number. Add methods to add, lookup and remove entries.

Lecture 08 · Core Concepts

Generics

Intermediate ~45 min Requires: Lecture 07

Why Generics?

They let you write **type‑safe** collections and APIs without casting.

Simple generic class

Box.java
public class Box<T> {
    private T value;

    public Box(T value) {
        this.value = value;
    }

    public T get() { return value; }
    public void set(T value) { this.value = value; }
}

Using bounded type parameters

Restrict a type to subclasses of a given class or interface.

NumberUtils.java
public class NumberUtils {
    public static <T extends Number>
    double sum(List<T> list) {
        double total = 0;
        for (T n : list) {
            total += n.doubleValue();
        }
        return total;
    }
}
🎯 Exercise 8.1

Create a generic Pair<K,V> class that stores two values. Write a short demo that creates a Pair<String,Integer> and prints both elements.

Lecture 09 · Advanced

Streams & Lambdas (Java 8+)

Intermediate ~55 min Requires: Lecture 08

Lambda syntax

A lambda expression is an anonymous function that can be treated as an instance of a functional interface.

LambdaDemo.java
import java.util.function.*;

public class LambdaDemo {
    public static void main(String[] args) {
        Predicate<Integer> isEven = n -> n % 2 == 0;
        System.out.println(isEven.test(42)); // true
    }
}

Stream pipeline

Streams enable functional‑style processing of sequences of elements.

StreamDemo.java
import java.util.stream.*;
import java.util.*;

public class StreamDemo {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1,2,3,4,5);
        int sum = numbers.stream()
                               .filter(n -> n % 2 == 0)   // keep evens
                               .mapToInt(n -> n * n)       // square them
                               .sum();                 // sum

        System.out.println("Result = " + sum);
    }
}
🎯 Exercise 9.1

Read a list of words from an array, convert them to uppercase, filter those longer than 5 characters, sort them alphabetically and print each on its own line – all using the Stream API.

Lecture 10 · Advanced

Exception Handling

Intermediate ~45 min Requires: Lecture 09

Checked vs Unchecked

  • Checked – compiler forces you to handle or declare (IOException, SQLException).
  • Unchecked – subclasses of RuntimeException (e.g. NullPointerException) need not be declared.

Try‑catch‑finally

ExceptionDemo.java
import java.io.*;

public class ExceptionDemo {
    public static void main(String[] args) {
        try {
            BufferedReader br = new BufferedReader(new FileReader("missing.txt"));
            System.out.println(br.readLine());
        } catch (FileNotFoundException e) {
            System.err.println("File not found!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            System.out.println("Cleanup if necessary");
        }
    }
}

Custom exception

InvalidAgeException.java
public class InvalidAgeException extends Exception {
    public InvalidAgeException(String message) {
        super(message);
    }
}
🎯 Exercise 10.1

Create a BankAccount class with a withdraw(double amount) method. Throw a custom InsufficientFundsException when the balance would become negative, and write a driver program that catches and reports the error.

Lecture 11 · Advanced

Concurrency

Intermediate ~60 min Requires: Lecture 10

Threads basics

Two ways to create a thread:

  1. Extend Thread and override run().
  2. Implement Runnable (or Callable) and pass it to a Thread or an ExecutorService.
ThreadDemo.java
class MyTask implements Runnable {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("Thread " + Thread.currentThread().getName() + ": " + i);
            try { Thread.sleep(500); } catch (InterruptedException e) { }
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyTask(), "Worker‑1");
        Thread t2 = new Thread(new MyTask(), "Worker‑2");
        t1.start();
        t2.start();
    }
}

Executor framework

Higher‑level API for managing thread pools.

ExecutorDemo.java
import java.util.concurrent.*;

public class ExecutorDemo {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 10; i++) {
            int id = i;
            pool.submit(() -> {
                System.out.println("Task " + id + " executed by " + Thread.currentThread().getName());
            });
        }
        pool.shutdown();
    }
}
🎯 Exercise 11.1

Implement a simple producer‑consumer pipeline using a BlockingQueue and two threads (one producing numbers, the other consuming and printing them). Use an ExecutorService to run both.

Lecture 12 · Capstone

Capstone Project – Console To‑Do List

Advanced ~90 min Requires: All Lectures

Build a **single‑file** console application that lets the user manage a to‑do list. The program should let you:

  • Add a task (string description).
  • Mark a task as completed.
  • Delete a task.
  • Display all tasks with status.
  • Persist tasks to a plain‑text file on exit and reload them on start.

Suggested Architecture

  1. Model – a Task class with id, description and completed flag.
  2. Repository – a List<Task> stored in memory; use Files API for persistence.
  3. Service – static methods for add/save/load/delete.
  4. UI loop – a while(true) menu that reads user input with Scanner.

Starter Code (you’ll flesh it out)

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

class Task {
    private static int counter = 1;
    private final int id;
    private String description;
    private boolean done;

    public Task(String description) {
        this.id = counter++;
        this.description = description;
        this.done = false;
    }

    public int getId() { return id; }
    public String getDescription() { return description; }
    public boolean isDone() { return done; }
    public void toggle() { done = !done; }

    public String serialize() {
        return id + "," + description.replaceAll(",", "\\,") + "," + done;
    }

    public static Task deserialize(String line) {
        String[] parts = line.split(",", 3);
        Task t = new Task(parts[1]);
        t.id = Integer.parseInt(parts[0]);          // reset id
        t.done = Boolean.parseBoolean(parts[2]);
        return t;
    }
}

public class TodoApp {
    private static final Path FILE = Paths.get("tasks.txt");
    private static final List<Task> tasks = new ArrayList<>();

    public static void main(String[] args) throws Exception {
        load();
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("\n--- To‑Do List ---");
            tasks.forEach(t -> System.out.println(t.getId() + ". [" + (t.isDone() ? "x" : " ") + "] " + t.getDescription()));
            System.out.println("\nChoose: (a)dd, (t)oggle, (d)elete, (q)uit");
            String cmd = sc.nextLine().trim().toLowerCase();
            switch (cmd) {
                case "a":
                    System.out.print("Enter description: ");
                    String desc = sc.nextLine();
                    tasks.add(new Task(desc));
                    break;
                case "t":
                    System.out.print("Id to toggle: ");
                    int id = Integer.parseInt(sc.nextLine());
                    tasks.stream()
                         .filter(t -> t.getId() == id)
                         .findFirst()
                         .ifPresent(Task::toggle);
                    break;
                case "d":
                    System.out.print("Id to delete: ");
                    int delId = Integer.parseInt(sc.nextLine());
                    tasks.removeIf(t -> t.getId() == delId);
                    break;
                case "q":
                    save();
                    System.out.println("Good‑bye!");
                    return;
                default:
                    System.out.println("Unknown command");
            }
        }
    }

    private static void load() throws Exception {
        if (Files.exists(FILE)) {
            Files.lines(FILE).forEach(l -> tasks.add(Task.deserialize(l)));
        }
    }

    private static void save() throws Exception {
        List<String> lines = new ArrayList<>();
        tasks.forEach(t -> lines.add(t.serialize()));
        Files.write(FILE, lines);
    }
}

What You’ll Demonstrate

  • Class design & encapsulation.
  • Collections (ArrayList) and streaming operations.
  • File I/O with NIO.
  • Basic console UI handling.
  • Exception handling for I/O and parsing errors.
🎯 Final Challenge

Extend the app with:

  1. A priority field (enum LOW/MEDIUM/HIGH) and sort the output by priority.
  2. Use java.time.LocalDate to add a due‑date, and display overdue tasks.
  3. Write JUnit tests for the Task class and the persistence logic.