Beginner → Junior Dev
0%
Lesson 1Getting Started

What is Java?

Before writing a single line, let's understand what Java actually is, why companies hire Java developers, and what you'll be able to build by the end of this course.

Java is one of the most widely used programming languages in the world. It powers Android apps, large-scale enterprise systems at banks and insurance companies, backend APIs, and much more. Learning Java opens doors to a huge number of jobs — it consistently ranks in the top 3 most in-demand languages for developers.

Why Java for your first language?

Jobs

Huge job market

Java developers are among the most hired in tech. Banks, healthcare, government, startups — all use Java.

Structure

Teaches you to think

Java forces you to be organized. The habits you build here will make you a better developer in any language.

Portable

Write once, run anywhere

Java runs on any device with a JVM installed — Windows, Mac, Linux, Android. No rewriting for each platform.

Ecosystem

Massive ecosystem

Thousands of libraries and frameworks. Spring, Maven, Gradle, JUnit — the tools are all there waiting for you.

How Java runs: the JVM

Most languages compile directly to machine code your CPU understands. Java does something different: it compiles to bytecode, a middle format that any computer can understand — as long as it has the Java Virtual Machine (JVM) installed.

This is why Java is "write once, run anywhere." You write the code once, compile it once, and that compiled file runs identically everywhere.

🏢
Real World: Where you'll see Java

Netflix, LinkedIn, Amazon, and Uber all use Java heavily in their backends. Android's entire development ecosystem is Java/Kotlin. The financial industry runs billions of dollars through Java systems every day.

What you'll build by the end

This course takes you from zero to a working task management console application — a real program that stores tasks, lets you add and complete them, and reads/writes to a file. It's exactly the kind of project you'd put on a resume for a junior developer role.

Along the way you'll learn every concept a junior developer needs: variables, loops, methods, object-oriented design, error handling, and file I/O.

💡
You don't need to install anything yet

You can follow all early lessons using an online Java editor like replit.com or jdoodle.com. When you're ready to work locally, we'll walk through installing JDK + IntelliJ IDEA.

Quick Check
Java compiles your code into bytecode that runs on the JVM. Why is this useful?
Lesson 2Getting Started

Your First Program

Every Java developer's journey starts here — the classic "Hello, World!" But we're going to understand every word of it, not just copy-paste.

HelloWorld.javaYour first program
public class HelloWorld {

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

}

Let's break down every single piece:

1

public class HelloWorld

In Java, all code lives inside a class. Think of a class as a container. The name of the class (HelloWorld) must match the file name exactly — HelloWorld.java. public means other parts of the program can see it.

2

public static void main(String[] args)

This is the entry point — it's the exact method the JVM looks for and calls first when your program runs. Every Java program must have this. For now, memorize it as a formula; the pieces will make sense as you progress.

3

System.out.println("Hello, World!");

System.out represents the console (the black terminal window). println prints text and moves to a new line. The text you want to print goes inside the parentheses as a String (text in double quotes). Every statement ends with a semicolon ;.

4

Curly braces { }

Curly braces group code together. The outer pair belongs to the class, the inner pair belongs to the main method. Everything inside a pair of braces is considered to be "inside" that block.

⚠️
Java is case-sensitive

System is not the same as system. String is not the same as string. This is one of the most common beginner errors. If your code isn't working, check your capitalisation first.

Greetings.javaMultiple outputs
public class Greetings {
    public static void main(String[] args) {

        System.out.println("Hello, World!");   // new line after
        System.out.print("Java is ");           // no new line — stays on same line
        System.out.println("awesome!");
        // Output:
        // Hello, World!
        // Java is awesome!

    }
}
💡
Comments

Lines starting with // are comments — they're notes for humans and are completely ignored by Java. Use them to explain what your code does. Good comments make you a better teammate and future-you will thank present-you.

Your Turn

Write a program that introduces yourself

Create a class called Intro. In the main method, print three lines:

  • Your name
  • Your city or country
  • One hobby or interest

Use println for each line. Check: does your file name match your class name?

Quick Check
Which is the correct method signature that Java looks for as the starting point of a program?
Lesson 3Getting Started

Variables & Data Types

Variables are named storage boxes for your program's data. Java requires you to say exactly what kind of data each box will hold — this is called static typing and it's one of Java's biggest strengths.

Unlike some languages where you can just write x = 5, Java requires a type declaration: you must tell the compiler what kind of data the variable will hold before you use it. This catches mistakes before your program even runs.

DataTypes.javaThe 8 primitive types + String
public class DataTypes {
    public static void main(String[] args) {

        // ─── Integer (whole numbers) ──────────────────
        int age       = 25;          // -2 billion to +2 billion
        long population = 8_000_000_000L; // big numbers, note the L

        // ─── Decimal (floating point) ─────────────────
        double price   = 19.99;      // most common for decimals
        float  rating  = 4.5f;       // less precise, note the f

        // ─── Text ─────────────────────────────────────
        char   grade   = 'A';        // single character, single quotes
        String name    = "Alex";     // text, double quotes. Note: capital S!

        // ─── True/False ───────────────────────────────
        boolean isLoggedIn = true;  // only true or false
        boolean hasDiscount = false;

        // Printing variables
        System.out.println("Name: " + name);   // + joins text with variables
        System.out.println("Age: " + age);
        System.out.println("Price: $" + price);
    }
}
TypeStoresExample valueUse when
intWhole numbers42, -7, 1000Age, count, score
longVery large whole numbers8000000000LTimestamps, large IDs
doubleDecimal numbers3.14, 19.99Prices, measurements
booleanTrue or falsetrue, falseFlags, conditions
charOne character'A', '7', '!'Single letters/symbols
StringText (any length)"Hello"Names, messages, IDs

Declaring vs. Assigning

You can separate the declaration (creating the box) from the assignment (putting something in it):

Variables.javaDeclaration · Assignment · var keyword
// Declare first, assign later
int score;
score = 100;

// Declare and assign in one line (most common)
int lives = 3;

// var — Java 10+: compiler figures out the type for you
var message = "Hello";   // Java infers: String
var count   = 42;        // Java infers: int

// final = a constant. Value can NEVER change after assignment
final double PI = 3.14159;
// PI = 3.0; // ERROR: cannot assign a value to final variable

// Naming convention: camelCase for variables
int numberOfStudents = 30;  // camelCase: first word lowercase, rest capitalized
final int MAX_SIZE = 100;   // constants: ALL_CAPS with underscores
⚠️
String vs. primitive types

String starts with a capital letter — it's not a primitive, it's a class (more on that in the OOP lessons). The 8 primitives all start lowercase: int, double, boolean, char, long, float, short, byte.

Your Turn

Student Profile

Create a class StudentProfile with a main method. Declare and assign variables for:

  • A student's full name (String)
  • Their age (int)
  • Their GPA (double)
  • Whether they're enrolled (boolean)
  • Their grade (char, like 'B')

Print each piece of information on its own line, like: Name: Jordan Lee

Quick Check
You need to store the price of an item like $14.99. Which data type is most appropriate?
Lesson 4Core Syntax

Operators & Expressions

Operators let you do math, compare values, and combine conditions. These are the verbs of your programs — they make things happen.

Operators.javaArithmetic · Comparison · Logical
public class Operators {
    public static void main(String[] args) {

        // ─── Arithmetic ───────────────────────────────
        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!)
        System.out.println(a % b);   // 1  — modulo (remainder)

        // Integer division gotcha:
        double result = (double) a / b;  // 3.333... (cast to double first!)

        // ─── Shorthand assignment ─────────────────────
        int score = 50;
        score += 10;   // same as: score = score + 10  → 60
        score -= 5;    // → 55
        score *= 2;    // → 110
        score++;       // increment by 1 → 111
        score--;       // decrement by 1 → 110

        // ─── Comparison (always return boolean) ──────
        System.out.println(a == b);  // false — equal to
        System.out.println(a != b);  // true  — not equal
        System.out.println(a >  b);  // true  — greater than
        System.out.println(a <= b);  // false — less than or equal

        // ─── Logical ──────────────────────────────────
        boolean isAdult   = true;
        boolean hasTicket = false;

        System.out.println(isAdult && hasTicket); // false — AND: both must be true
        System.out.println(isAdult || hasTicket); // true  — OR:  at least one true
        System.out.println(!isAdult);             // false — NOT: flips the value

        // ─── String concatenation ─────────────────────
        String first = "Java";
        String last  = "Developer";
        System.out.println(first + " " + last); // "Java Developer"

        // printf for formatted output (like a template)
        double price = 9.5;
        System.out.printf("Price: $%.2f%n", price); // Price: $9.50
    }
}
⚠️
The integer division trap

10 / 3 in Java gives 3, not 3.333. When both operands are int, Java throws away the decimal. To get a decimal result, cast one number to double first: (double) 10 / 33.333. This trips up almost every beginner.

Your Turn

Shopping Cart Calculator

Write a program that calculates the total cost of a shopping cart:

  • Three items with prices of your choice (use double)
  • Calculate the subtotal (sum of all items)
  • Apply a 10% discount (multiply by 0.9)
  • Add 8% sales tax to the discounted price (multiply by 1.08)
  • Print the subtotal, discount amount, tax amount, and final total using printf with 2 decimal places
Quick Check
What does 17 % 5 evaluate to?
Lesson 5Core Syntax

Control Flow: if / else

Programs need to make decisions. The if/else statement is how your code chooses what to do based on conditions. It's the foundation of all program logic.

Decisions.javaif · else if · else · switch
public class Decisions {
    public static void main(String[] args) {

        int age = 20;

        // Simple if/else
        if (age >= 18) {
            System.out.println("You can vote!");
        } else {
            System.out.println("Too young to vote.");
        }

        // Multiple conditions with else if
        int score = 85;
        String grade;

        if      (score >= 90) { grade = "A"; }
        else if (score >= 80) { grade = "B"; }
        else if (score >= 70) { grade = "C"; }
        else if (score >= 60) { grade = "D"; }
        else                    { grade = "F"; }

        System.out.println("Grade: " + grade); // Grade: B

        // Combining conditions
        boolean hasID       = true;
        boolean isVIPMember  = false;
        int     memberScore  = 750;

        if (hasID && (isVIPMember || memberScore > 700)) {
            System.out.println("Welcome to the lounge!");
        }

        // Switch statement — clean alternative to many else-ifs
        String day = "MONDAY";
        switch (day) {
            case "MONDAY":
            case "TUESDAY":
            case "WEDNESDAY":
            case "THURSDAY":
            case "FRIDAY":
                System.out.println("Weekday — work day!");
                break;
            case "SATURDAY":
            case "SUNDAY":
                System.out.println("Weekend!");
                break;
            default:
                System.out.println("Unknown day");
        }

        // Ternary operator — shorthand if/else on one line
        int temp = 28;
        String weather = (temp > 25) ? "Hot" : "Cool";
        System.out.println(weather); // Hot
    }
}
💼
Real World: Decision logic is everywhere

Every login system, payment processor, and recommendation engine is built from if/else logic. "Is the user logged in? Is their session valid? Is the payment method declined?" — real applications make hundreds of these decisions per request.

Your Turn

Build a Movie Ticket Pricing System

Write a program that calculates a movie ticket price based on age and day of the week:

  • Under 12: $8.00
  • 12–17: $10.00
  • 18–64: $14.00
  • 65+: $9.00
  • If it's Tuesday ("TUESDAY"), everyone gets $3 off

Create variables for age and day, calculate the price, and print the result. Test with at least 3 different combinations.

Quick Check
The condition age >= 18 && hasLicense evaluates to true when…
Lesson 6Core Syntax

Loops

Loops let your program repeat actions without copy-pasting code. Instead of printing 100 lines by hand, one loop does it in 3 lines. They're indispensable.

Loops.javafor · while · do-while · for-each
public class Loops {
    public static void main(String[] args) {

        // ─── for loop: when you know how many times ────
        for (int i = 1; i <= 5; i++) {
            System.out.println("Count: " + i);
        }
        // Prints: Count: 1, Count: 2 ... Count: 5
        //   ↑init    ↑condition  ↑update (runs after each iteration)

        // Counting down
        for (int i = 10; i > 0; i--) {
            System.out.print(i + " ");
        }
        System.out.println(); // blank println = new line

        // ─── while loop: when you don't know how many ─
        int coins = 100;
        while (coins > 0) {
            System.out.println("Coins remaining: " + coins);
            coins -= 25;
        }
        System.out.println("Out of coins!");

        // ─── do-while: always runs at least once ──────
        int attempts = 0;
        do {
            System.out.println("Attempt #" + (attempts + 1));
            attempts++;
        } while (attempts < 3);

        // ─── break: exit loop early ───────────────────
        for (int i = 1; i <= 100; i++) {
            if (i % 7 == 0) {
                System.out.println("First multiple of 7: " + i);
                break; // exit the loop immediately
            }
        }

        // ─── continue: skip this iteration ───────────
        for (int i = 1; i <= 10; i++) {
            if (i % 2 == 0) continue; // skip even numbers
            System.out.print(i + " ");  // prints: 1 3 5 7 9
        }
    }
}
⚠️
The infinite loop

If your loop condition never becomes false, the program hangs forever. Always make sure your loop variable is moving toward the condition being false. If your program freezes, that's usually why. Press Ctrl+C to stop it.

NestedLoops.javaLoops inside loops — multiplication table
// Nested loops: a loop inside a loop
for (int row = 1; row <= 5; row++) {
    for (int col = 1; col <= 5; col++) {
        System.out.printf("%4d", row * col);
    }
    System.out.println();
}
// Output:
//    1   2   3   4   5
//    2   4   6   8  10
//    3   6   9  12  15
//  ...
Your Turn

FizzBuzz — the classic interview question

Write a loop from 1 to 100. For each number:

  • If divisible by both 3 and 5: print FizzBuzz
  • If divisible by 3 only: print Fizz
  • If divisible by 5 only: print Buzz
  • Otherwise: print the number

This question appears in real Java developer interviews. If you can write this cleanly, you're on your way.

Quick Check
What's the difference between break and continue inside a loop?
Lesson 7Core Syntax

Methods

Methods are named blocks of reusable code. Instead of writing the same logic over and over, you write it once in a method and call it by name. They're how real programs stay organized.

Methods.javaDefining · Parameters · Return values
public class Methods {

    // ── Method anatomy ──────────────────────────────────────
    //   access  return-type  name    (parameters)
    //     ↓         ↓         ↓          ↓                 
    public static void   greet  (String name) {
        System.out.println("Hello, " + name + "!");
    }   // void = returns nothing

    // Method that returns a value
    public static int add(int x, int y) {
        return x + y;  // return sends a value back to the caller
    }

    // Method with multiple parameters and logic
    public static double calculateBMI(double weightKg, double heightM) {
        return weightKg / (heightM * heightM);
    }

    // Method that returns a boolean
    public static boolean isEven(int number) {
        return number % 2 == 0;
    }

    // Method that calls other methods
    public static String getBMICategory(double weightKg, double heightM) {
        double bmi = calculateBMI(weightKg, heightM); // calls the method above
        if      (bmi < 18.5) return "Underweight";
        else if (bmi < 25.0) return "Normal";
        else if (bmi < 30.0) return "Overweight";
        else                  return "Obese";
    }

    public static void main(String[] args) {
        greet("Alex");                            // Hello, Alex!
        greet("World");                           // Hello, World!

        int sum = add(3, 7);
        System.out.println("Sum: " + sum);        // Sum: 10

        System.out.println(isEven(4));             // true
        System.out.println(isEven(7));             // false

        String category = getBMICategory(70, 1.75);
        System.out.println("BMI: " + category);    // BMI: Normal
    }
}
The Single Responsibility Rule

Each method should do exactly one thing. A method called calculateAndPrintTax is doing two things — calculating AND printing. Better: one method calculates and returns the tax, a different one prints it. This makes your code easier to test and reuse.

Method overloading

Java lets you have multiple methods with the same name, as long as their parameters differ. This is called overloading.

Overloading.javaSame name, different parameters
public static double area(double side) {
    return side * side;           // square
}

public static double area(double width, double height) {
    return width * height;        // rectangle
}

public static double area(double radius, boolean isCircle) {
    return Math.PI * radius * radius; // circle
}

// Java picks the right version based on arguments you provide
area(5.0);           // → 25.0 (square)
area(4.0, 6.0);      // → 24.0 (rectangle)
area(3.0, true);     // → 28.27 (circle)
Your Turn

Build a Calculator Utility

Create a class Calculator with these methods:

  • add(double a, double b) → returns sum
  • subtract(double a, double b) → returns difference
  • multiply(double a, double b) → returns product
  • divide(double a, double b) → returns quotient, but returns 0 and prints an error if b is 0
  • power(double base, int exponent) → returns base raised to exponent (use a loop, not Math.pow)

In main, call each method and print the results.

Quick Check
A method is declared as public static int square(int n). What must happen inside the method body?
Lesson 8Data Structures

Arrays

An array is a list of values stored under one variable name. Instead of creating score1, score2, score3... you create one array that holds all of them.

Arrays.javaDeclaration · Access · for-each · 2D arrays
public class ArraysDemo {
    public static void main(String[] args) {

        // ─── Creating arrays ──────────────────────────
        int[] scores = {85, 92, 78, 95, 88}; // array literal

        String[] names = new String[3]; // empty array of size 3
        names[0] = "Alice";             // assign by index (starts at 0!)
        names[1] = "Bob";
        names[2] = "Carol";

        // ─── Accessing elements ───────────────────────
        System.out.println(scores[0]); // 85 — first element
        System.out.println(scores[4]); // 88 — last element
        System.out.println(scores.length); // 5 — size of array

        // ─── Loop through array ───────────────────────
        int total = 0;
        for (int i = 0; i < scores.length; i++) {
            total += scores[i];
        }
        double average = (double) total / scores.length;
        System.out.printf("Average: %.1f%n", average); // 87.6

        // ─── for-each loop: cleaner when you just need values
        for (String name : names) {
            System.out.println("Hello, " + name);
        }

        // ─── Arrays utility methods ───────────────────
        import java.util.Arrays; // add this at the top of your file
        Arrays.sort(scores);
        System.out.println(Arrays.toString(scores)); // [78, 85, 88, 92, 95]

        // ─── 2D array: rows and columns ───────────────
        int[][] grid = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        };
        System.out.println(grid[1][2]); // 6 — row 1, column 2
    }
}
🚫
ArrayIndexOutOfBoundsException

Arrays in Java start at index 0, not 1. A 5-element array has valid indices 0–4. Trying to access index 5 crashes with ArrayIndexOutOfBoundsException. This is the most common array error. Always use array.length - 1 for the last element, or use a for-each loop.

Your Turn

Grade Statistics

Create an array of 10 test scores (make them up). Write separate methods that:

  • Return the highest score
  • Return the lowest score
  • Return the average
  • Count how many scores are above 80

Call all methods from main and print a formatted report. Bonus: sort the array and print it in order.

Quick Check
You have int[] nums = {10, 20, 30, 40, 50}. What is nums[3]?
Lesson 9Data Structures

ArrayList & Collections

Arrays are fixed size — you can't add or remove elements. In real applications, data changes all the time. ArrayList is the go-to resizable list that professional Java developers use every day.

ListsDemo.javaArrayList · HashMap · common operations
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Collections;

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

        // ─── ArrayList: resizable list ────────────────
        ArrayList<String> tasks = new ArrayList<>();

        tasks.add("Buy groceries");   // add to end
        tasks.add("Walk the dog");
        tasks.add("Do laundry");
        tasks.add(0, "Wake up");       // insert at index 0

        System.out.println(tasks.size());       // 4
        System.out.println(tasks.get(1));        // "Buy groceries"
        System.out.println(tasks.contains("Walk the dog")); // true

        tasks.remove("Do laundry");      // remove by value
        tasks.remove(0);                // remove by index
        tasks.set(0, "Go shopping");    // update existing item

        // For-each loop works great with ArrayList
        for (String task : tasks) {
            System.out.println("- " + task);
        }

        Collections.sort(tasks);               // alphabetical sort
        Collections.reverse(tasks);             // reverse the list

        // ─── HashMap: key-value pairs ─────────────────
        // Like a dictionary: look up a value by its key
        HashMap<String, Integer> ages = new HashMap<>();

        ages.put("Alice", 30);
        ages.put("Bob",   25);
        ages.put("Carol", 35);

        System.out.println(ages.get("Bob"));     // 25
        System.out.println(ages.containsKey("Alice")); // true

        // Loop through a HashMap
        for (String name : ages.keySet()) {
            System.out.println(name + " is " + ages.get(name));
        }
    }
}
ArrayListRegular Array
Resizable — add/remove freelyFixed size once created
get(i), add(), remove()array[i] direct access
Works with Collections utilitiesWorks with Arrays utilities
Slightly slower (boxing overhead)Faster for fixed-size data
Can't store primitives directlyCan store int, double, etc.
💼
Real World: Collections are everywhere

ArrayList and HashMap are among the most-used classes in all of Java. Every user list, product catalog, shopping cart, and configuration object in a real application uses them. Getting comfortable with these makes you immediately useful on a team.

Your Turn

Mini Contact Book

Build a contact book using a HashMap<String, String> where the key is a name and the value is a phone number. Write methods to:

  • Add a contact
  • Look up a contact by name (handle the case where they don't exist)
  • Delete a contact
  • Print all contacts in alphabetical order (hint: convert keys to an ArrayList and sort it)
Quick Check
You have an ArrayList<String> list with 3 items. After list.add("new item"), what does list.size() return?
Lesson 10Data Structures

Strings in Depth

Strings are everywhere — usernames, messages, file paths, API responses. Java's String class has dozens of built-in methods. Knowing them saves hours of writing code from scratch.

StringMethods.javaEssential String operations
public class StringMethods {
    public static void main(String[] args) {

        String s = "  Hello, Java World!  ";

        // ─── Basic info ───────────────────────────────
        System.out.println(s.length());           // 22 (including spaces)
        System.out.println(s.isEmpty());          // false
        System.out.println(s.isBlank());          // false (Java 11+)

        // ─── Searching ────────────────────────────────
        System.out.println(s.contains("Java"));   // true
        System.out.println(s.indexOf("Java"));    // 9 (position)
        System.out.println(s.startsWith("  Hello")); // true
        System.out.println(s.endsWith("!  "));    // true

        // ─── Transformation ───────────────────────────
        System.out.println(s.trim());             // removes leading/trailing spaces
        System.out.println(s.toLowerCase());      // "  hello, java world!  "
        System.out.println(s.toUpperCase());      // "  HELLO, JAVA WORLD!  "
        System.out.println(s.replace("Java", "Python")); // replaces text

        // ─── Extracting parts ─────────────────────────
        String trimmed = s.trim();
        System.out.println(trimmed.substring(7));     // "Java World!"
        System.out.println(trimmed.substring(7, 11)); // "Java" (7 to 10)
        System.out.println(trimmed.charAt(0));        // 'H'

        // ─── Splitting ────────────────────────────────
        String csv = "Alice,Bob,Carol,Dave";
        String[] parts = csv.split(",");
        for (String name : parts) {
            System.out.println(name); // Alice, Bob, Carol, Dave
        }

        // ─── CRITICAL: comparing Strings ──────────────
        String a = "hello";
        String b = "hello";
        System.out.println(a == b);            // true (but don't rely on this!)
        System.out.println(a.equals(b));        // true — ALWAYS use .equals()
        System.out.println(a.equalsIgnoreCase("HELLO")); // true

        // ─── StringBuilder: for building strings in loops
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= 5; i++) {
            sb.append("Item ").append(i).append("\n");
        }
        System.out.print(sb.toString());
    }
}
🚫
Never use == to compare Strings

== checks if two variables point to the same memory location, not if they have the same content. Two different String objects with the same text can fail a == check. Always use .equals() to compare String content. This is one of the most common Java bugs in beginner code.

Your Turn

Username Validator

Write a method validateUsername(String username) that returns a message based on these rules:

  • Must be 3–20 characters long
  • Must not contain spaces
  • Must start with a letter (not a number or symbol)
  • If all rules pass: return "Username is valid!"
  • If a rule fails: return a specific message saying which rule failed

Test it with at least 5 different usernames — some valid, some not.

Quick Check
What's the correct way to check if two Strings contain the same text?
Lesson 11Object-Oriented

Classes & Objects

This is the big one. Object-Oriented Programming (OOP) is the foundation of professional Java. A class is a blueprint; an object is a thing built from that blueprint. Everything in Java is built this way.

Think of a class like an architectural drawing for a house. The drawing defines what every house built from it will have: rooms, doors, windows. An object is a specific house built from that drawing — your house, my house. Each is its own thing with its own data, but both were built the same way.

Defining a class

BankAccount.javaFields · Constructor · Methods · Encapsulation
public class BankAccount {

    // ─── Fields (instance variables) ──────────────
    // private = only this class can access them directly
    private String owner;
    private double balance;
    private String accountId;

    // ─── Constructor: called when you do "new BankAccount()" ──
    public BankAccount(String owner, double initialBalance) {
        this.owner   = owner;           // "this" refers to THIS object
        this.balance = initialBalance;
        this.accountId = "ACC-" + (int)(Math.random() * 9000 + 1000);
    }

    // ─── Methods (behaviours) ─────────────────────
    public void deposit(double amount) {
        if (amount <= 0) {
            System.out.println("Invalid deposit amount.");
            return;
        }
        balance += amount;
        System.out.printf("Deposited $%.2f. New balance: $%.2f%n", amount, balance);
    }

    public void withdraw(double amount) {
        if (amount > balance) {
            System.out.println("Insufficient funds.");
            return;
        }
        balance -= amount;
        System.out.printf("Withdrew $%.2f. New balance: $%.2f%n", amount, balance);
    }

    // ─── Getters: controlled read access ──────────
    public String getOwner()    { return owner;     }
    public double getBalance()  { return balance;   }
    public String getAccountId(){ return accountId; }

    // ─── toString: how to print the object ────────
    @Override
    public String toString() {
        return String.format("[%s] %s — $%.2f", accountId, owner, balance);
    }
}
Main.javaCreating and using objects
public class Main {
    public static void main(String[] args) {

        // Creating objects with "new" — calls the constructor
        BankAccount alice = new BankAccount("Alice", 1000.00);
        BankAccount bob   = new BankAccount("Bob",   500.00);

        // Calling methods on each object
        alice.deposit(250.00);
        alice.withdraw(100.00);
        bob.withdraw(700.00);  // Insufficient funds

        // Using getters
        System.out.println(alice.getOwner() + " has $" + alice.getBalance());

        // toString is called automatically when printing an object
        System.out.println(alice); // [ACC-4821] Alice — $1150.00
        System.out.println(bob);   // [ACC-7392] Bob — $500.00

        // Each object has its OWN data — they don't share balance
        ArrayList<BankAccount> accounts = new ArrayList<>();
        accounts.add(alice);
        accounts.add(bob);
        for (BankAccount acc : accounts) {
            System.out.println(acc);
        }
    }
}
💡
Encapsulation: why private fields?

Making fields private means nothing outside the class can directly change them. Someone can't write alice.balance = -99999 and corrupt your data. They must go through deposit() and withdraw() — which have your validation logic. This protection is called encapsulation and it's one of the most important ideas in OOP.

Your Turn

Build a Student class

Create a Student class with:

  • Private fields: name (String), studentId (int), grades (ArrayList of doubles)
  • Constructor that sets name and generates a random 5-digit student ID
  • Method addGrade(double grade) — validates grade is 0–100
  • Method getAverage() — returns average of all grades (return 0 if no grades yet)
  • Method getLetterGrade() — returns "A", "B", "C", "D", or "F" based on average
  • Override toString() to print all info nicely

In main, create 3 students, add grades to each, and print their reports.

Quick Check
In a class, what is the purpose of a constructor?
Lesson 12Object-Oriented

Inheritance

Inheritance lets one class build on top of another. If you have common features shared by multiple classes, put them in a parent class. Child classes inherit everything and add their own unique behavior — no copy-pasting.

Imagine you're building a zoo app. Animals all have a name, an age, and make a sound. But a Dog fetches, a Bird flies, and a Fish swims. Inheritance lets you put the shared stuff in Animal and the unique stuff in each subclass.

Animal.java + subclassesextends · super · @Override · polymorphism
// ─── Parent class (Superclass) ─────────────────
public class Animal {

    protected String name;   // protected = accessible in subclasses
    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.");
    }

    // This method will be OVERRIDDEN by subclasses
    public void makeSound() {
        System.out.println(name + " makes a sound.");
    }

    @Override
    public String toString() {
        return name + " (age " + age + ")";
    }
}

// ─── Child class (Subclass) ────────────────────
public class Dog extends Animal {   // "extends" = inherit from Animal

    private String breed;

    public Dog(String name, int age, String breed) {
        super(name, age);  // call Animal's constructor first
        this.breed = breed;
    }

    @Override              // tells Java we're replacing the parent's version
    public void makeSound() {
        System.out.println(name + " says: Woof!");
    }

    public void fetch() {  // Dog-specific method
        System.out.println(name + " fetches the ball!");
    }

    @Override
    public String toString() {
        return super.toString() + " — " + breed; // reuse parent's toString
    }
}

public class Cat extends Animal {
    private boolean isIndoor;

    public Cat(String name, int age, boolean isIndoor) {
        super(name, age);
        this.isIndoor = isIndoor;
    }

    @Override
    public void makeSound() {
        System.out.println(name + " says: Meow!");
    }
}

// ─── Polymorphism: the power of inheritance ────
public class Zoo {
    public static void main(String[] args) {

        // A Dog IS-AN Animal, so this is allowed:
        Animal[] animals = {
            new Dog("Rex", 3, "Labrador"),
            new Cat("Whiskers", 5, true),
            new Dog("Buddy", 2, "Poodle")
        };

        // Same method call, different behaviour — POLYMORPHISM
        for (Animal a : animals) {
            a.makeSound();  // calls the right version for each animal
            a.eat();        // inherited from Animal
        }

        // Casting back to subtype to access subclass methods
        if (animals[0] instanceof Dog dog) {
            dog.fetch(); // Rex fetches the ball!
        }
    }
}
💡
Is-a relationship

Inheritance should only be used when there's a genuine "is-a" relationship. A Dog IS AN Animal ✅. A Car is NOT an Engine ❌ — a car has an engine. When in doubt, ask: "Does this child truly IS-A parent?" If not, use composition (a class that contains another class as a field).

Your Turn

Shape Hierarchy

Create a parent class Shape with fields color and a method describe(). Create three subclasses:

  • Circle with radius — calculates area as π×r²
  • Rectangle with width and height — calculates area as w×h
  • Triangle with base and height — calculates area as ½×b×h

Each subclass overrides a getArea() method. In main, create an array of Shape objects, loop through them, print each shape's color, type, and area. Find which shape has the largest area.

Quick Check
What does super(name, age) do inside a subclass constructor?
Lesson 13Object-Oriented

Interfaces

An interface is a contract. It says "any class that implements me must provide these methods." Interfaces let unrelated classes share a common set of behaviours — and they're everywhere in professional Java code.

Think of an interface like a job description. "Software Developer" requires: writes code, reviews PRs, attends standups. Whether you're a junior dev at a startup or a senior at a bank, you both implement the same interface. The interface doesn't care how you do it — just that you can.

Interfaces.javainterface · implements · default methods
// ─── Define an interface ───────────────────────
public interface Printable {
    void print();            // no body — every class MUST implement this
    void printSummary();

    // Default method: provides a body, can be overridden
    default void printHeader() {
        System.out.println("=== DOCUMENT ===");
    }
}

public interface Saveable {
    void save(String filename);
    void load(String filename);
}

// ─── A class can implement MULTIPLE interfaces ─
public class Report implements Printable, Saveable {

    private String title;
    private String content;

    public Report(String title, String content) {
        this.title   = title;
        this.content = content;
    }

    @Override
    public void print() {
        printHeader();          // uses the default method
        System.out.println("Title: " + title);
        System.out.println(content);
    }

    @Override
    public void printSummary() {
        System.out.println("Report: " + title + " (" + content.length() + " chars)");
    }

    @Override
    public void save(String filename) {
        System.out.println("Saving '" + title + "' to " + filename);
    }

    @Override
    public void load(String filename) {
        System.out.println("Loading from " + filename);
    }
}

// ─── Real power: use the interface as a type ───
public class Printer {
    // This method works with ANY class that implements Printable
    public static void printAll(Printable[] items) {
        for (Printable item : items) {
            item.print();
            System.out.println("---");
        }
    }

    public static void main(String[] args) {
        Report r1 = new Report("Q1 Sales", "Revenue up 15%...");
        Report r2 = new Report("Annual Review", "Strong performance...");

        printAll(new Printable[] { r1, r2 });

        r1.save("q1_report.txt");
    }
}
InterfaceAbstract Class
Can have fields?Only constantsYes
Can have constructors?NoYes
How many can a class use?Many (implements A, B, C)One (extends)
Best forUnrelated classes sharing behaviourRelated classes sharing code
💼
Real World: Interfaces are Java's backbone

Every Java framework uses interfaces heavily. Spring uses them for services and repositories. Java's List, Map, and Collection are all interfaces. When you write ArrayList<String> list, you could also write List<String> list — because ArrayList implements List. This makes it trivially easy to swap implementations later.

Your Turn

Payment System

Create an interface PaymentMethod with methods: pay(double amount) and getBalance(). Implement three classes:

  • CreditCard — has a credit limit; pay() reduces available credit
  • DebitCard — has a bank balance; pay() reduces balance, rejects if insufficient
  • DigitalWallet — has a wallet balance; pay() reduces balance

Write a checkout(PaymentMethod method, double total) method that works with any payment type. Demonstrate all three.

Quick Check
What is the key advantage of using an interface as a method parameter type (like void print(Printable p))?
Lesson 14Real-World Skills

Exception Handling

Real programs crash. Users type letters where numbers are expected, files don't exist, network connections drop. Exception handling is how you write programs that deal with errors gracefully instead of crashing.

Exceptions.javatry · catch · finally · throw · custom exceptions
public class Exceptions {
    public static void main(String[] args) {

        // ─── Basic try-catch ──────────────────────────
        try {
            int[] nums = {1, 2, 3};
            System.out.println(nums[5]); // crashes! index out of bounds
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Error: invalid index — " + e.getMessage());
        }
        // Program continues after the catch block

        // ─── Multiple catch blocks ────────────────────
        try {
            String text = null;
            System.out.println(text.length());  // NullPointerException
            int result = 10 / 0;                 // ArithmeticException
        } catch (NullPointerException e) {
            System.out.println("Null reference! " + e.getMessage());
        } catch (ArithmeticException e) {
            System.out.println("Math error: " + e.getMessage());
        } catch (Exception e) {   // catches anything not caught above
            System.out.println("Unexpected error: " + e.getMessage());
        } finally {
            System.out.println("This ALWAYS runs — use for cleanup");
        }

        // ─── Parsing user input safely ────────────────
        String userInput = "abc";
        try {
            int number = Integer.parseInt(userInput);
            System.out.println("Parsed: " + number);
        } catch (NumberFormatException e) {
            System.out.println("'" + userInput + "' is not a valid number.");
        }
    }

    // ─── Throwing exceptions ──────────────────────
    public static double divide(double a, double b) {
        if (b == 0) {
            throw new IllegalArgumentException("Cannot divide by zero!");
        }
        return a / b;
    }
}

// ─── Custom exception ──────────────────────────
public class InsufficientFundsException extends Exception {
    private double shortfall;

    public InsufficientFundsException(double shortfall) {
        super("Insufficient funds. Short by $" + shortfall);
        this.shortfall = shortfall;
    }

    public double getShortfall() { return shortfall; }
}

// Using the custom exception
public void withdraw(double amount) throws InsufficientFundsException {
    if (amount > balance) {
        throw new InsufficientFundsException(amount - balance);
    }
    balance -= amount;
}
💡
Common exceptions you'll encounter

NullPointerException — called a method on a null reference. ArrayIndexOutOfBoundsException — accessed an invalid index. NumberFormatException — tried to parse "abc" as an integer. ClassCastException — invalid type cast. StackOverflowError — infinite recursion. Recognising these by name saves huge amounts of debugging time.

Your Turn

Safe Number Parser Utility

Write a class SafeParser with these static methods:

  • parseInt(String s) — returns the int, or -1 if invalid (never crashes)
  • parseDouble(String s) — returns the double, or -1.0 if invalid
  • parsePositiveInt(String s) — returns the int only if it's positive; throws a custom InvalidInputException if it's zero or negative or not a number

Test each method with valid input, non-numeric input, and edge cases (empty string, null, negative numbers).

Quick Check
What does the finally block in a try-catch-finally do?
Lesson 15Real-World Skills

File I/O

Programs that can't save data are toys. File I/O — reading from and writing to files — is what turns your programs into tools that remember things between runs. Every real application does this.

FileIO.javaWriting · Reading · Appending · try-with-resources
import java.io.*;
import java.nio.file.*;
import java.util.*;

public class FileIO {

    // ─── Writing to a file ────────────────────────
    public static void writeFile(String filename, String content) {
        // try-with-resources: automatically closes writer when done
        try (FileWriter writer = new FileWriter(filename)) {
            writer.write(content);
            System.out.println("Written to " + filename);
        } catch (IOException e) {
            System.out.println("Error writing file: " + e.getMessage());
        }
    }

    // ─── Appending to a file ──────────────────────
    public static void appendFile(String filename, String line) {
        try (FileWriter fw = new FileWriter(filename, true);  // true = append mode
             PrintWriter pw = new PrintWriter(fw)) {
            pw.println(line);
        } catch (IOException e) {
            System.out.println("Error appending: " + e.getMessage());
        }
    }

    // ─── Reading line by line ─────────────────────
    public static List<String> readLines(String filename) {
        List<String> lines = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
            String line;
            while ((line = reader.readLine()) != null) {
                lines.add(line);
            }
        } catch (FileNotFoundException e) {
            System.out.println("File not found: " + filename);
        } catch (IOException e) {
            System.out.println("Error reading: " + e.getMessage());
        }
        return lines;
    }

    // ─── Modern NIO approach (Java 11+) ───────────
    public static void modernReadWrite() throws IOException {
        Path path = Path.of("data.txt");

        // Write all at once
        Files.writeString(path, "Line 1\nLine 2\nLine 3");

        // Read all at once
        String content = Files.readString(path);
        System.out.println(content);

        // Read all lines as a List
        List<String> lines = Files.readAllLines(path);
        lines.forEach(System.out::println);

        // Check if file exists before reading
        if (Files.exists(path)) {
            System.out.println("File size: " + Files.size(path) + " bytes");
        }
    }

    public static void main(String[] args) {
        writeFile("tasks.txt", "Buy groceries\nWalk the dog\n");
        appendFile("tasks.txt", "Read a book");

        List<String> tasks = readLines("tasks.txt");
        System.out.println("Tasks loaded: " + tasks.size());
        for (String task : tasks) {
            System.out.println("  □ " + task);
        }
    }
}
💡
Always close your files

If you open a file and don't close it, you'll eventually run out of file handles and the program crashes. The try-with-resources block (try (FileWriter fw = ...)) closes the resource automatically when the block ends — even if an exception occurs. Always use this pattern for files, database connections, and network sockets.

Your Turn

Persistent To-Do List

Build a program that keeps a to-do list in a file called todo.txt:

  • addTask(String task) — appends to the file
  • listTasks() — reads and prints all tasks with numbers
  • completeTask(int number) — removes that task from the file (read all, remove the one, rewrite)
  • clearAll() — deletes the file or overwrites with empty content

This is a realistic mini-application. Make it handle the case where the file doesn't exist yet.

Quick Check
Why is try-with-resources preferred over a regular try-finally for file handling?
Lesson 16Capstone Project

Capstone: Task Manager App

You've learned everything you need. Now build it all together into a real, working application — the kind you'd show in a job interview or put on GitHub.

🎯
What you're building

A console-based Task Manager that persists to a file. Users can add tasks, list them, mark complete, delete, and filter by status. Every concept from this course appears in it.

Task.javaModel class — represents one task
public class Task {

    private static int nextId = 1;

    private int     id;
    private String  title;
    private String  priority;   // "HIGH", "MEDIUM", "LOW"
    private boolean completed;
    private String  createdDate;

    public Task(String title, String priority) {
        this.id          = nextId++;
        this.title       = title;
        this.priority    = priority.toUpperCase();
        this.completed   = false;
        this.createdDate = java.time.LocalDate.now().toString();
    }

    // Constructor for loading from file
    public Task(int id, String title, String priority, boolean completed, String date) {
        this.id          = id;
        this.title       = title;
        this.priority    = priority;
        this.completed   = completed;
        this.createdDate = date;
        if (id >= nextId) nextId = id + 1;
    }

    public void     complete()         { completed = true;  }
    public int      getId()            { return id;        }
    public String  getTitle()         { return title;     }
    public String  getPriority()      { return priority;  }
    public boolean isCompleted()       { return completed; }

    // Saves to CSV format: id,title,priority,completed,date
    public String toCsv() {
        return id + "," + title + "," + priority + "," + completed + "," + createdDate;
    }

    // Load from CSV format
    public static Task fromCsv(String csv) {
        String[] parts = csv.split(",");
        return new Task(
            Integer.parseInt(parts[0]),
            parts[1], parts[2],
            Boolean.parseBoolean(parts[3]),
            parts[4]
        );
    }

    @Override
    public String toString() {
        String status = completed ? "[✓]" : "[ ]";
        return String.format("%s #%d [%s] %s (%s)", status, id, priority, title, createdDate);
    }
}
TaskManager.javaCore logic — your main class to build
import java.util.*;
import java.io.*;
import java.nio.file.*;

public class TaskManager {

    private static final String FILE = "tasks.csv";
    private ArrayList<Task> tasks;

    public TaskManager() {
        tasks = new ArrayList<>();
        loadFromFile();  // restore saved tasks on startup
    }

    public void addTask(String title, String priority) {
        if (title.isBlank()) {
            System.out.println("Task title cannot be empty.");
            return;
        }
        Task task = new Task(title.trim(), priority);
        tasks.add(task);
        saveToFile();
        System.out.println("Added: " + task);
    }

    public void listAll() {
        if (tasks.isEmpty()) {
            System.out.println("No tasks yet. Add one!");
            return;
        }
        System.out.println("\n=== YOUR TASKS ===");
        for (Task t : tasks) {
            System.out.println(t);
        }
    }

    public void completeTask(int id) {
        for (Task t : tasks) {
            if (t.getId() == id) {
                t.complete();
                saveToFile();
                System.out.println("Completed: " + t.getTitle());
                return;
            }
        }
        System.out.println("Task #" + id + " not found.");
    }

    public void deleteTask(int id) {
        boolean removed = tasks.removeIf(t -> t.getId() == id);
        if (removed) { saveToFile(); System.out.println("Deleted task #" + id); }
        else          { System.out.println("Task #" + id + " not found."); }
    }

    public void filterByPriority(String priority) {
        System.out.println("\n=== " + priority.toUpperCase() + " PRIORITY ===");
        boolean found = false;
        for (Task t : tasks) {
            if (t.getPriority().equalsIgnoreCase(priority)) {
                System.out.println(t);
                found = true;
            }
        }
        if (!found) System.out.println("No tasks with that priority.");
    }

    private void saveToFile() {
        try (PrintWriter pw = new PrintWriter(new FileWriter(FILE))) {
            for (Task t : tasks) pw.println(t.toCsv());
        } catch (IOException e) {
            System.out.println("Could not save: " + e.getMessage());
        }
    }

    private void loadFromFile() {
        File file = new File(FILE);
        if (!file.exists()) return;
        try (BufferedReader br = new BufferedReader(new FileReader(file))) {
            String line;
            while ((line = br.readLine()) != null) {
                if (!line.isBlank()) tasks.add(Task.fromCsv(line));
            }
        } catch (IOException e) {
            System.out.println("Could not load tasks: " + e.getMessage());
        }
    }

    // ─── YOUR JOB: build the menu loop in main ────
    public static void main(String[] args) {
        TaskManager manager = new TaskManager();
        Scanner scanner = new Scanner(System.in);

        // TODO: build a while loop menu with these options:
        //   1. Add task       → prompt title + priority
        //   2. List all tasks
        //   3. Complete task  → prompt task ID
        //   4. Delete task    → prompt task ID
        //   5. Filter by priority
        //   6. Exit
        // Handle invalid input with try-catch
        // Use scanner.nextLine() to read user input
    }
}

Extensions to make it resume-worthy

Feature

Due dates

Add a dueDate field. Implement a listOverdue() method that shows tasks past their deadline.

Feature

Search

Add a searchTasks(String keyword) method that finds tasks whose titles contain the keyword (case-insensitive).

Feature

Statistics

Show total tasks, completed count, pending count, and completion percentage.

Feature

Sorting

Sort tasks by priority (HIGH first), by due date, or by completion status before displaying.

🎉
What comes next on your journey

You now know enough to be a productive Java developer. Your next steps: Git & GitHub (version control), Maven/Gradle (project build tools), JUnit (unit testing), Spring Boot (building REST APIs), and SQL + JDBC (databases). Every one of those builds directly on what you just learned.

Roadmap