Skip to main content
Zhimalab
中文
Java in 14 Days · Interactive
0%
Home Course overview
☕ 14 days · Interactive · Beginner-friendly

Truly learn Java in two weeks

This is not a dry textbook. It is a carefully designed interactive learning path — every lesson comes with runnable examples, instantly-feedback quizzes and hands-on exercises, taking you from zero to the Java level of a second-year computer science student.

14
days of lessons
40+
code examples
28
interactive quizzes
14
hands-on exercises
📖

Progressive learning

From variables to generics over 14 days, focusing on one core topic each day.

Instant feedback

Every topic comes with a code example and output preview, then a quiz to reinforce what you learned.

✏️

Hands-on exercises

Each lesson ends with fill-in-the-blank coding exercises to test your understanding in practice.

DAY 1

Java Intro & the First Program

Understand Java's features and write your first line of code.

What is Java

Java is an object-oriented, cross-platform programming language. Its core philosophy is "Write Once, Run Anywhere" — compiled bytecode runs on any platform equipped with a JVM (Java Virtual Machine).

💡
Why learn Java? Java is widely used in enterprise back-end development, Android apps, and big data processing (Hadoop/Spark). It is one of the most widely used languages in the world. Mastering Java is an essential skill for computer science students.

Your First Program: Hello World

Every programmer's starting point. Here is Hello World in Java:

HelloWorld.java
    public class HelloWorld {
    // The program entry point, the main method
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}
    
  
Output
Hello, World!
💡
Line by line:
public class HelloWorld — declare a public class; the class name must match the file name
public static void main(String[] args) — the program entry point, a fixed form
System.out.println(...) — prints a line to the console
// — a single-line comment, ignored by the compiler

Compile & Run

Java is a compiled language. A .java source file must first be compiled into .class bytecode, which is then executed by the JVM:

terminal
    # 1. Compile the source file
javac HelloWorld.java

# 2. Run the bytecode
java HelloWorld
    
  
Output
Hello, World!

Output Methods Compared

MethodEffectExample
System.out.println()Prints and then starts a new lineprintln("Hi") → Hi
System.out.print()Prints without a newlineprint("Hi") → Hi
System.out.printf()Formatted outputprintf("%.2f", 3.14)
?
Quiz
In the Hello World program, what is the name of the program entry method?
A start
B main
C run
D init
?
Quiz
What file does a Java source file produce after compilation?
A .class bytecode file
B .exe executable file
C .js file
D .py file
Hands-on
Complete the code so the program prints "Java is fun!"
public class MyFirst {
    public static void main(String[] args) {
        System.out.("Java is fun!");
    }
 }

Day 1 complete!

Congratulations on finishing this lesson. Click "Complete lesson" to record your progress, then continue with the next day.

DAY 2

Variables & Data Types

Variables are containers for data; data types determine the container's size and purpose.

Primitive Data Types

Java is a strongly typed language; every variable must declare its type first. Java has 8 primitive types:

TypeKeywordSizeRange / Example
Integerbyte1 byte-128 ~ 127
Integershort2 bytes-32768 ~ 32767
Integerint4 bytesabout ±2.1 billion (most common)
Integerlong8 bytesvery large; add L suffix
Floatfloat4 bytesadd f suffix
Floatdouble8 bytesdefault floating-point type
Charchar2 bytesa single Unicode character
Booleanboolean1 bittrue / false
Types.java
    public class Types {
    public static void main(String[] args) {
        int age = 20;
        double price = 19.99;
        char grade = 'A';
        boolean isStudent = true;
        long population = 7900000000L;
        float pi = 3.14f;

        System.out.println("Age: " + age);
        System.out.println("Price: " + price);
        System.out.println("Grade: " + grade);
        System.out.println("Is student: " + isStudent);
    }
}
    
  
Output
Age: 20
Price: 19.99
Grade: A
Is student: true
⚠️
Watch the suffixes! long values need an L, float values need an f, otherwise compilation fails. Use single quotes 'A' for characters and double quotes "Hi" for strings.

Variable Naming Rules

Java variable naming follows camelCase:

RuleValid exampleInvalid example
letters/digits/underscore/$; cannot start with a digituserName2name
cannot be a keywordmyClassclass
case-sensitiveage ≠ Age
camelCase, meaning from its namestudentCountsc

Type Conversion

A small type can be automatically converted to a larger type (implicit); converting a larger type to a smaller one requires an explicit cast (which may lose precision):

Cast.java
    public class Cast {
    public static void main(String[] args) {
        // automatic conversion: int → double
        int i = 100;
        double d = i;
        System.out.println("Automatic: " + d);

        // explicit cast: double → int
        double pi = 3.99;
        int n = (int) pi;  // truncates the fractional part
        System.out.println("Explicit: " + n);
    }
}
    
  
Output
Automatic: 100.0
Explicit: 3

The String Type

String is not a primitive type but a reference type (a class), although it is used extremely often:

StringDemo.java
    public class StringDemo {
    public static void main(String[] args) {
        String name = "Alice";
        String greeting = "Hello";
        // string concatenation uses +
        System.out.println(greeting + ", " + name + "!");

        // common methods
        System.out.println("Length: " + name.length());
        System.out.println("Uppercase: " + "hello".toUpperCase());
        System.out.println("Concat: " + "a".concat("b"));
    }
}
    
  
Output
Hello, Alice!
Length: 5
Uppercase: HELLO
Concat: ab
?
Quiz
Which of the following is a legal variable name?
A 2ndPlace
B class
C _score
D public
?
Quiz
How many bytes does a double variable occupy?
A 4 bytes
B 8 bytes
C 2 bytes
D 16 bytes
Hands-on
Declare an int variable count assigned 100, and a boolean variable active assigned true
public class Vars {
    public static void main(String[] args) {
         count = 100;
         active = true;
        System.out.println(count + " " + active);
    }
 }

Day 2 complete!

Congratulations on finishing this lesson. Click "Complete lesson" to record your progress, then continue with the next day.

DAY 3

Operators & Expressions

Operators are the symbols that operate on data; mastering them is basic programming skill.

Arithmetic Operators

Arithmetic.java
    public class Arithmetic {
    public static void main(String[] args) {
        int a = 17, b = 5;
        System.out.println("Add: " + (a + b));    // 22
        System.out.println("Sub: " + (a - b));    // 12
        System.out.println("Mul: " + (a * b));    // 85
        System.out.println("Div: " + (a / b));    // 3 (integer division, truncated)
        System.out.println("Mod: " + (a % b));    // 2
    }
}
    
  
Output
Add: 22
Sub: 12
Mul: 85
Div: 3
Mod: 2
⚠️
Integer division trap: 17 / 5 gives 3, not 3.4! Dividing two integers produces an integer (truncated). To get a decimal, cast one to double: (double)a / b.

Increment & Decrement

IncDec.java
    public class IncDec {
    public static void main(String[] args) {
        int i = 5;
        System.out.println(i++);  // 5 (uses, then +1)
        System.out.println(i);    // 6
        System.out.println(++i);  // 7 (+1, then uses)
    }
}
    
  
Output
5
6
7
💡

i++ uses then increments; ++i increments then uses. To avoid confusion in real code, prefer writing i++; on its own line.

Relational & Logical Operators

CategoryOperatorMeaning
Relational==equal to
!=not equal to
> <greater than / less than
>= <=greater/less than or equal
returns a boolean value
== compares values for primitives and addresses for objects
Logical&&AND (short-circuit): true only if both are true
||OR (short-circuit): true if either is true
!NOT: inverts
Logic.java
    public class Logic {
    public static void main(String[] args) {
        int score = 75;
        // logical AND
        boolean pass = score >= 60 && score <= 100;
        System.out.println("Pass: " + pass);

        // logical OR
        boolean holiday = false;
        boolean weekend = true;
        boolean canRest = holiday || weekend;
        System.out.println("Can rest: " + canRest);

        // short-circuit behavior
        int x = 0;
        if (x != 0 && 10 / x > 1) {
            System.out.println("this line never runs");
        }
        System.out.println("Short-circuit avoided division by zero");
    }
}
    
  
Output
Pass: true
Can rest: true
Short-circuit avoided division by zero
💡
Short-circuit evaluation: with &&, if the left side is false the right side is not evaluated; with ||, if the left side is true the right side is not evaluated. This can avoid errors like null pointers.

Assignment & Ternary Operator

Assign.java
    public class Assign {
    public static void main(String[] args) {
        int n = 10;
        n += 5;  // equivalent to n = n + 5
        System.out.println("After +=: " + n);  // 15

        // ternary: condition ? trueValue : falseValue
        int age = 17;
        String status = age >= 18 ? "Adult" : "Underage";
        System.out.println(status);
    }
}
    
  
Output
After +=: 15
Underage
?
Quiz
What is the result of 17 % 5?
A 2
B 3
C 3.4
D 12
?
Quiz
int a = 5; System.out.println(a++ + ++a); What is printed?
A 11
B 12
C 13
D 10
Hands-on
Use the ternary operator to decide whether the score passes (>=60)
public class Ternary {
    public static void main(String[] args) {
        int score = 85;
        String result = score  60 ? "Pass" : "Fail";
        System.out.println(result);
    }
 }

Day 3 complete!

Congratulations on finishing this lesson. Click "Complete lesson" to record your progress, then continue with the next day.

DAY 4

Conditional Statements

Let the program learn to "make choices" — run different code under different conditions.

if-else Statements

Conditions are evaluated from top to bottom; once one matches, its branch runs and the rest are not checked.

IfElse.java
    public class IfElse {
    public static void main(String[] args) {
        int score = 85;

        if (score >= 90) {
            System.out.println("Excellent");
        } else if (score >= 80) {
            System.out.println("Good");
        } else if (score >= 60) {
            System.out.println("Pass");
        } else {
            System.out.println("Fail");
        }
    }
}
    
  
Output
Good

Nested if

Nested.java
    public class Nested {
    public static void main(String[] args) {
        int age = 20;
        boolean hasLicense = true;

        if (age >= 18) {
            if (hasLicense) {
                System.out.println("Can drive");
            } else {
                System.out.println("Adult, but no license");
            }
        } else {
            System.out.println("Underage, cannot drive");
        }
    }
}
    
  
Output
Can drive

The switch Statement

When comparing a variable against several fixed values, switch is clearer than if-else:

Switch.java
    public class Switch {
    public static void main(String[] args) {
        int day = 3;
        switch (day) {
            case 1:
                System.out.println("Monday");
                break;
            case 2:
                System.out.println("Tuesday");
                break;
            case 3:
                System.out.println("Wednesday");
                break;
            default:
                System.out.println("Other");
        }
    }
}
    
  
Output
Wednesday
⚠️
Don't forget break! If you leave out break, the program "falls through" and keeps executing the following cases until a break or the end of the switch. This is usually a bug.

New switch Syntax (Java 14+)

Java 14 introduced an enhanced switch using arrow syntax that does not fall through automatically:

SwitchNew.java
    public class SwitchNew {
    public static void main(String[] args) {
        int day = 3;
        String name = switch (day) {
            case 1 -> "Monday";
            case 2 -> "Tuesday";
            case 3 -> "Wednesday";
            default -> "Other";
        };
        System.out.println(name);
    }
}
    
  
Output
Wednesday
?
Quiz
In if-else statements, which if does else match?
A The nearest unpaired if
B The farthest if
C The first if
D All ifs
?
Quiz
What happens if you omit break in a switch statement?
A A compile error
B Keeps running the following cases (fall-through)
C Jumps to default
D The program crashes
Hands-on
Complete the switch so that when color is 1 it prints "Red"
public class ColorSwitch {
    public static void main(String[] args) {
        int color = 1;
        switch (color) {
             1:
                System.out.println("Red");
                ;
            default:
                System.out.println("Unknown");
        }
    }
 }

Day 4 complete!

Congratulations on finishing this lesson. Click "Complete lesson" to record your progress, then continue with the next day.

DAY 5

Loops

Let the program repeat automatically and say goodbye to copy-paste.

The for Loop

When you know the number of iterations, the for loop is the first choice:

for (init; condition; update) — three parts separated by semicolons. The loop ends when the condition is false.

ForLoop.java
    public class ForLoop {
    public static void main(String[] args) {
        // print 1 to 5
        for (int i = 1; i <= 5; i++) {
            System.out.println("Iteration " + i);
        }
    }
}
    
  
Output
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Iteration 5

The while Loop

When you care only about a condition rather than a fixed count, use while:

WhileLoop.java
    public class WhileLoop {
    public static void main(String[] args) {
        int n = 1024;
        int count = 0;
        // keep dividing by 2 until n becomes 0
        while (n > 0) {
            n = n / 2;
            count++;
        }
        System.out.println("Looped " + count + " times");
    }
}
    
  
Output
Looped 11 times

The do-while Loop

do-while executes once before checking the condition, guaranteeing at least one execution:

DoWhile.java
    public class DoWhile {
    public static void main(String[] args) {
        int i = 10;
        do {
            System.out.println("i = " + i);
            i++;
        } while (i < 5);  // condition is false, but the body ran once
    }
}
    
  
Output
i = 10

break & continue

BreakContinue.java
    public class BreakContinue {
    public static void main(String[] args) {
        // break: exit the whole loop
        System.out.println("=== break example ===");
        for (int i = 1; i <= 10; i++) {
            if (i == 5) break;  // stop at 5
            System.out.println(i);
        }

        // continue: skip this iteration, move to the next
        System.out.println("=== continue example ===");
        for (int i = 1; i <= 10; i++) {
            if (i % 2 == 0) continue;  // skip even numbers
            System.out.println(i);
        }
    }
}
    
  
Output
=== break example ===
1
2
3
4
=== continue example ===
1
3
5
7
9

Nested Loops: the 9×9 Multiplication Table

Multiplication.java
    public class Multiplication {
    public static void main(String[] args) {
        for (int i = 1; i <= 9; i++) {
            for (int j = 1; j <= i; j++) {
                System.out.printf("%d×%d=%-4d", j, i, i * j);
            }
            System.out.println();
        }
    }
}
    
  
Output
1×1=1
1×2=2 2×2=4
1×3=3 2×3=6 3×3=9
1×4=4 2×4=8 3×4=12 4×4=16
1×5=5 2×5=10 3×5=15 4×5=20 5×5=25
1×6=6 2×6=12 3×6=18 4×6=24 5×6=30 6×6=36
1×7=7 2×7=14 3×7=21 4×7=28 5×7=35 6×7=42 7×7=49
1×8=8 2×8=16 3×8=24 4×8=32 5×8=40 6×8=48 7×8=56 8×8=64
1×9=9 2×9=18 3×9=27 4×9=36 5×9=45 6×9=54 7×9=63 8×9=72 9×9=81
💡
printf formatting: %d integer, %-4d left-aligned over 4 chars, %n newline. This is a classic interview question — make sure you understand the execution order of nested loops.
?
Quiz
How many times does a do-while loop run at least?
A 0 times
B 1 time
C 2 times
D Depends on the condition
?
Quiz
What does the continue statement do?
A Ends the whole loop
B Ends the current method
C Skips this iteration and moves on
D Jumps to the loop start and re-runs it
Hands-on
Use a for loop to sum 1+2+3+...+100
public class Sum {
    public static void main(String[] args) {
        int sum = 0;
        for (int i = 1; i  100; i++) {
            sum  i;
        }
        System.out.println("Total: " + sum);
    }
 }

Day 5 complete!

Congratulations on finishing this lesson. Click "Complete lesson" to record your progress, then continue with the next day.

DAY 6

Methods

Methods are the basic unit for organizing code — wrap repeated logic, then call it by just a name.

Defining and Calling Methods

MethodBasic.java
    public class MethodBasic {
    // method definition: modifier returnType methodName(parameters)
    public static int add(int a, int b) {
        return a + b;
    }

    // void for no return value
    public static void greet(String name) {
        System.out.println("Hello, " + name + "!");
    }

    public static void main(String[] args) {
        int result = add(3, 5);    // call a method with a return value
        System.out.println("3 + 5 = " + result);

        greet("Alice");            // call a method with no return value
    }
}
    
  
Output
3 + 5 = 8
Hello, Alice!
💡
The four parts of a method: return type (int/void etc.), method name, parameter list (may be empty), and method body. return returns a result and ends the method; a void method needs no return.

Method Overloading

A class can have several methods with the same name as long as their parameter lists differ (count, types, order). The compiler picks the right one from the call arguments:

Overload.java
    public class Overload {
    // two ints added
    public static int add(int a, int b) {
        return a + b;
    }
    // three ints added
    public static int add(int a, int b, int c) {
        return a + b + c;
    }
    // two doubles added
    public static double add(double a, double b) {
        return a + b;
    }

    public static void main(String[] args) {
        System.out.println(add(1, 2));        // calls the first
        System.out.println(add(1, 2, 3));     // calls the second
        System.out.println(add(1.5, 2.5));    // calls the third
    }
}
    
  
Output
3
6
4.0

Pass by Value

Java method parameters are passed by value — changing a primitive parameter inside a method does not affect the outside:

PassByValue.java
    public class PassByValue {
    public static void change(int x) {
        x = 100;  // only changes the copy
    }

    public static void main(String[] args) {
        int n = 10;
        change(n);
        System.out.println("n = " + n);  // still 10
    }
}
    
  
Output
n = 10

Variable Scope

A variable is valid only within the {} where it is declared:

Scope.java
    public class Scope {
    public static void main(String[] args) {
        int x = 10;  // main method scope
        if (x > 5) {
            int y = 20;  // if-block scope
            System.out.println(x + y);  // 30
        }
        // System.out.println(y);  // compile error! y is not visible here
    }
}
    
  
Output
30

Recursion

A method calling itself is recursion. It must have a termination condition, otherwise the stack overflows. A classic example — factorial:

Recursion.java
    public class Recursion {
    // n! = n * (n-1)!
    public static int factorial(int n) {
        if (n <= 1) return 1;        // base case
        return n * factorial(n - 1); // recursive call
    }

    public static void main(String[] args) {
        System.out.println("5! = " + factorial(5));  // 120
    }
}
    
  
Output
5! = 120
⚠️
The three rules of recursion: ① a big problem can be split into similar smaller ones; ② there is a base case; ③ each call shrinks the problem size. Otherwise it recurses forever and throws StackOverflowError.
?
Quiz
What is required for method overloading?
A Same name, different return type
B Same name, different parameter list
C Different names, same parameters
D Different parameter names
?
Quiz
How are Java method parameters passed?
A By reference
B By value
C By pointer
D Either way
Hands-on
Define a method max that returns the larger of two integers
public class MaxMethod {
    public static  max(int a, int b) {
        if (a > b) {
             a;
        }
        return b;
    }
    public static void main(String[] args) {
        System.out.println(max(3, 7));
    }
 }

Day 6 complete!

Congratulations on finishing this lesson. Click "Complete lesson" to record your progress, then continue with the next day.

DAY 7

Arrays

An array is a container storing multiple values of the same type — the most basic data structure.

Creating & Accessing

ArrayBasic.java
    public class ArrayBasic {
    public static void main(String[] args) {
        // declare and initialize
        int[] scores = {90, 85, 78, 92, 88};

        // access elements (index starts at 0)
        System.out.println("First: " + scores[0]);
        System.out.println("Third: " + scores[2]);

        // array length
        System.out.println("Length: " + scores.length);

        // modify an element
        scores[1] = 95;
        System.out.println("After change: " + scores[1]);
    }
}
    
  
Output
First: 90
Third: 78
Length: 5
After change: 95
⚠️
Indexes start at 0! An array of length 5 has indexes 0~4. Accessing scores[5] throws ArrayIndexOutOfBoundsException.

Other Ways to Create

ArrayCreate.java
    public class ArrayCreate {
    public static void main(String[] args) {
        // way 1: declare, then allocate space
        int[] a = new int[5];  // all default to 0
        a[0] = 10;

        // way 2: direct initialization
        int[] b = {1, 2, 3};

        // way 3: new + initialization
        String[] names = new String[]{"Alice", "Bob", "Carol"};

        System.out.println(a[0] + " " + a[1]);  // 10 0
        System.out.println(names.length);
    }
}
    
  
Output
10 0
3

Iterating Arrays

for-each is more concise, but it does not give you the index and cannot modify elements.

ArrayLoop.java
    public class ArrayLoop {
    public static void main(String[] args) {
        int[] nums = {10, 20, 30, 40, 50};

        // way 1: a normal for loop
        System.out.print("for: ");
        for (int i = 0; i < nums.length; i++) {
            System.out.print(nums[i] + " ");
        }
        System.out.println();

        // way 2: the enhanced for (for-each)
        System.out.print("for-each: ");
        for (int num : nums) {
            System.out.print(num + " ");
        }
        System.out.println();
    }
}
    
  
Output
for: 10 20 30 40 50
for-each: 10 20 30 40 50

In Practice: Finding the Max

ArrayMax.java
    public class ArrayMax {
    public static void main(String[] args) {
        int[] nums = {3, 7, 2, 9, 5, 1, 8};
        int max = nums[0];  // assume the first is the largest

        for (int i = 1; i < nums.length; i++) {
            if (nums[i] > max) {
                max = nums[i];
            }
        }
        System.out.println("Max: " + max);
    }
}
    
  
Output
Max: 9

2D Arrays

TwoD.java
    public class TwoD {
    public static void main(String[] args) {
        int[][] matrix = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        };

        // iterate the 2D array
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                System.out.print(matrix[i][j] + " ");
            }
            System.out.println();
        }
    }
}
    
  
Output
1 2 3
4 5 6
7 8 9
?
Quiz
In int[] arr = new int[5]; what is the default value of arr[3]?
A 0
B null
C Undefined
D 5
?
Quiz
For an array of length 5, what is the index of the last element?
A 5
B 4
C 0
D 6
Hands-on
Use for-each to iterate the array and sum the values
public class ArraySum {
    public static void main(String[] args) {
        int[] nums = {10, 20, 30};
        int sum = 0;
        for (int n  nums) {
            sum  n;
        }
        System.out.println("Total: " + sum);
    }
 }

Day 7 complete!

Congratulations on finishing this lesson. Click "Complete lesson" to record your progress, then continue with the next day.

DAY 8

OOP Basics: Classes & Objects

Object-oriented programming (OOP) is the soul of Java — describe things with "classes" and operate instances as "objects".

Classes & Objects

💡
Class = a template / blueprint that defines attributes (fields) and behavior (methods)
Object = an instance of a class, created with new
For example: Dog is a class; the dog in my home called "Buddy" is an object.

Defining Your First Class

Student.java
    // Student class
public class Student {
    // attributes (fields / member variables)
    String name;
    int age;
    double score;

    // methods (behavior)
    public void study() {
        System.out.println(name + " is studying");
    }

    public void introduce() {
        System.out.println("I am " + name + ", " + age + " years old");
    }
}
    
  
Output
// Student class definition; no output needed
Main.java
    public class Main {
    public static void main(String[] args) {
        // create an object
        Student s1 = new Student();
        s1.name = "Alice";
        s1.age = 20;
        s1.score = 92.5;

        // call methods
        s1.introduce();
        s1.study();

        // create another object
        Student s2 = new Student();
        s2.name = "Bob";
        s2.age = 21;
        s2.introduce();
    }
}
    
  
Output
I am Alice, 20 years old
Alice is studying
I am Bob, 21 years old

Member Variables vs Local Variables

AspectMember variable (field)Local variable
PositionInside the class, outside methodsInside a method
Default valueYes (int→0, reference→null)None; must initialize
ScopeThe whole classInside the method
StorageHeap (with the object)Stack (with the method)

The this Keyword

this refers to the current object and is often used to distinguish a member variable from a parameter with the same name:

ThisDemo.java
    public class Book {
    String title;
    double price;

    public void setInfo(String title, double price) {
        this.title = title;    // this.title is the member variable
        this.price = price;    // the right-side title is the parameter
    }

    public void show() {
        System.out.println(title + " price: " + price);
    }

    public static void main(String[] args) {
        Book b = new Book();
        b.setInfo("Java Basics", 59.9);
        b.show();
    }
}
    
  
Output
Java Basics price: 59.9

The static Modifier

Members marked static belong to the class itself and can be used without creating an object:

StaticDemo.java
    public class MathUtil {
    // static variable: shared by all objects
    static int count = 0;

    // static method: called directly with the class name
    public static int square(int n) {
        return n * n;
    }

    public MathUtil() {
        count++;  // each object created increments count
    }

    public static void main(String[] args) {
        System.out.println("Square of 3: " + MathUtil.square(3));

        new MathUtil();
        new MathUtil();
        System.out.println("Created " + MathUtil.count + " objects");
    }
}
    
  
Output
Square of 3: 9
Created 2 objects
?
Quiz
Which keyword creates an object?
A create
B new
C make
D class
?
Quiz
What does the this keyword refer to?
A The parent object
B The current object
C The class itself
D Static members
?
Quiz
What is special about a method marked static?
A It can only be called by objects
B It belongs to the class and can be called by the class name
C It cannot have a return value
D It cannot have parameters
Hands-on
Create a Student object and set its name property
public class Test {
    public static void main(String[] args) {
         s = new Student();
        s. = "Carol";
    }
 }

Day 8 complete!

Congratulations on finishing this lesson. Click "Complete lesson" to record your progress, then continue with the next day.

DAY 9

Encapsulation & Constructors

Encapsulation is one of the three pillars of OOP; constructors are the entry point for creating objects.

Encapsulation: Hide Details, Expose Interfaces

The core of encapsulation: make fields private and control access through public getters/setters. The benefit is that you can add data validation in the setter:

Encapsulation.java
    public class Account {
    private String owner;
    private double balance;

    // getter
    public double getBalance() {
        return balance;
    }

    // setter: adds validation logic
    public void setBalance(double balance) {
        if (balance < 0) {
            System.out.println("Balance cannot be negative!");
            return;
        }
        this.balance = balance;
    }

    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        this.owner = owner;
    }
}
    
  
Output
// Class definition; no output needed
AccountTest.java
    public class AccountTest {
    public static void main(String[] args) {
        Account acc = new Account();
        acc.setOwner("Alice");
        acc.setBalance(1000);
        System.out.println(acc.getOwner() + " balance: " + acc.getBalance());

        acc.setBalance(-500);  // will be rejected
        System.out.println(acc.getOwner() + " balance: " + acc.getBalance());
    }
}
    
  
Output
Alice balance: 1000.0
Balance cannot be negative!
Alice balance: 1000.0
💡
Access modifiers: public (anyone can access), private (this class only), protected (same package + subclasses), default (same package). Fields are usually private; methods are usually public.

Constructors

A constructor is a special method called automatically when new creates an object, used to initialize fields. Its traits: same name as the class, no return type.

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

    // no-arg constructor
    public Person() {
        this.name = "Unknown";
        this.age = 0;
    }

    // parameterized constructor (overload)
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void show() {
        System.out.println(name + ", " + age + " years old");
    }

    public static void main(String[] args) {
        Person p1 = new Person();           // uses the no-arg constructor
        Person p2 = new Person("Bob", 25);  // uses the parameterized one
        p1.show();
        p2.show();
    }
}
    
  
Output
Unknown, 0 years old
Bob, 25 years old
⚠️
Note: if you write no constructor at all, the compiler generates a no-arg constructor automatically. But once you write a parameterized one, the no-arg constructor is no longer generated — you must write it by hand if you need it.

Constructor Chaining: this()

Inside one constructor you can call another with this(...) to avoid duplicated code:

ThisCall.java
    public class Car {
    String brand;
    String color;
    int year;

    public Car(String brand) {
        this(brand, "White", 2024);  // calls the three-arg constructor
    }

    public Car(String brand, String color, int year) {
        this.brand = brand;
        this.color = color;
        this.year = year;
    }

    public void info() {
        System.out.println(year + " " + color + " " + brand);
    }

    public static void main(String[] args) {
        Car c = new Car("Toyota");
        c.info();
    }
}
    
  
Output
2024 White Toyota

The Standard JavaBean Pattern

In practice, entity classes usually follow the JavaBean convention: private fields + no-arg constructor + getters/setters:

JavaBean.java
    public class Product {
    private int id;
    private String name;
    private double price;

    // no-arg constructor (required)
    public Product() {}

    // all-args constructor (recommended)
    public Product(int id, String name, double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }

    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public double getPrice() { return price; }
    public void setPrice(double price) { this.price = price; }
}
    
  
Output
// Standard JavaBean; no output needed
?
Quiz
What is the core practice of encapsulation?
A Make all fields public
B Make fields private, access them via getters/setters
C Make all methods private
D Use no fields
?
Quiz
What is special about a constructor?
A It has a return type
B Same name as the class, no return type
C Can have any name
D Only one is allowed
?
Quiz
If only a parameterized constructor is written, can you still create an object with new Person() (no args)?
A Yes, the compiler generates it automatically
B No, you must write a no-arg constructor by hand
C Yes, Java handles it
D Not sure
Hands-on
Complete the constructor to initialize the name field
public class Dog {
    private String name;

    public Dog(String name) {
        .name = ;
    }
 }

Day 9 complete!

Congratulations on finishing this lesson. Click "Complete lesson" to record your progress, then continue with the next day.

DAY 10

Inheritance

Inheritance lets a subclass reuse its parent's code — the second pillar of OOP.

Basic Inheritance Syntax

Inherit a class with the extends keyword. A subclass automatically gets the parent's non-private members:

Inheritance.java
    // parent class (base class)
class Animal {
    String name;

    public void eat() {
        System.out.println(name + " is eating");
    }

    public void sleep() {
        System.out.println(name + " is sleeping");
    }
}

// subclass inheriting Animal
class Dog extends Animal {
    public void bark() {
        System.out.println(name + " barks!");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog d = new Dog();
        d.name = "Buddy";
        d.eat();    // inherited from the parent
        d.bark();   // the subclass's own method
    }
}
    
  
Output
Buddy is eating
Buddy barks!
💡
Key points about inheritance: ① Java is single-inheritance; a class can extend only one parent; ② the root superclass of all classes is Object; ③ a subclass cannot inherit the parent's private members or constructors.

Method Overriding

A subclass can redefine a parent method; this is called overriding. The method name and parameter list must match the parent's:

Override.java
    class Shape {
    public double area() {
        return 0;
    }
}

class Circle extends Shape {
    double radius;
    public Circle(double r) { this.radius = r; }

    @Override  // override annotation; helps the compiler check
    public double area() {
        return Math.PI * radius * radius;
    }
}

class Rectangle extends Shape {
    double width, height;
    public Rectangle(double w, double h) {
        this.width = w;
        this.height = h;
    }

    @Override
    public double area() {
        return width * height;
    }
}

public class Main {
    public static void main(String[] args) {
        Circle c = new Circle(5);
        Rectangle r = new Rectangle(4, 6);
        System.out.printf("Circle area: %.2f%n", c.area());
        System.out.printf("Rect area: %.2f%n", r.area());
    }
}
    
  
Output
Circle area: 78.54
Rect area: 24.00
💡
The @Override annotation: adding @Override lets the compiler verify that you are overriding correctly. If you misspell a method name, the compiler reports an error. Strongly recommended on every override.

The super Keyword

super accesses the parent's members, including calling the parent constructor:

Super.java
    class Vehicle {
    String brand;
    public Vehicle(String brand) {
        this.brand = brand;
        System.out.println("Vehicle constructed");
    }
    public void run() {
        System.out.println(brand + " is driving");
    }
}

class Car extends Vehicle {
    int wheels;
    public Car(String brand, int wheels) {
        super(brand);      // calls the parent constructor; must be the first line
        this.wheels = wheels;
        System.out.println("Car constructed");
    }

    @Override
    public void run() {
        super.run();       // calls the parent method
        System.out.println(brand + " has " + wheels + " wheels");
    }
}

public class Main {
    public static void main(String[] args) {
        Car car = new Car("Mercedes", 4);
        car.run();
    }
}
    
  
Output
Vehicle constructed
Car constructed
Mercedes is driving
Mercedes has 4 wheels
⚠️
Construction order: when creating a subclass object, the parent constructor runs first, then the subclass constructor. super(...) must be the first statement of the subclass constructor.

The final Keyword

TargetEffect
final classcannot be inherited (e.g. String)
final methodcannot be overridden
final variablecan be assigned only once (a constant)
?
Quiz
How many parent classes can one Java class inherit?
A 1
B 2
C Multiple
D Unlimited
?
Quiz
What is required for method overriding?
A Same method name and parameter list
B The return type can differ
C Access can be more restrictive
D The parameter list can differ
?
Quiz
When creating a subclass object, what order do the constructors run in?
A Subclass first, then parent
B Parent first, then subclass
C Only the subclass runs
D Random order
Hands-on
Make the Cat class inherit Animal and override the sound method
class Cat  Animal {
    @Override
    public void () {
        System.out.println("Meow!");
    }
 }

Day 10 complete!

Congratulations on finishing this lesson. Click "Complete lesson" to record your progress, then continue with the next day.

DAY 11

Polymorphism & Interfaces

Polymorphism is the most powerful trait of OOP; interfaces are the key to flexible design.

Polymorphism: One Call, Different Behaviors

Three prerequisites for polymorphism: ① an inheritance/implementation relationship; ② method overriding; ③ a parent reference pointing to a subclass object.

Polymorphism.java
    class Animal {
    public void speak() {
        System.out.println("Animal sound");
    }
}
class Dog extends Animal {
    public void speak() { System.out.println("Woof!"); }
}
class Cat extends Animal {
    public void speak() { System.out.println("Meow!"); }
}

public class Main {
    // a parent reference as a parameter — the power of polymorphism
    public static void makeSpeak(Animal a) {
        a.speak();  // the runtime decides which subclass method runs
    }

    public static void main(String[] args) {
        // parent reference pointing to subclass objects
        Animal a1 = new Dog();  // upcasting
        Animal a2 = new Cat();

        makeSpeak(a1);  // Woof!
        makeSpeak(a2);  // Meow!
    }
}
    
  
Output
Woof!
Meow!
💡
The essence of polymorphism: compile-time looks at the left (the parent type decides which methods can be called); runtime looks at the right (the actual object type decides which implementation runs). This is the separation of "compile-time type" and "runtime type".

Downcasting & instanceof

Turning a parent reference back into a subclass type is called downcasting, and should be guarded by instanceof:

Java 16+ supports instanceof pattern matching all in one step:

Downcast.java
    Animal a = new Dog();  // upcasting
// a.bark();  // compile error! Animal has no bark method

if (a instanceof Dog) {
    Dog d = (Dog) a;  // downcasting
    d.bark();         // now Dog-specific methods can be called
}
    
  
Output
// example snippet
Pattern.java
    if (a instanceof Dog d) {
    d.bark();  // d is automatically declared and cast
}
    
  
Output
// example snippet

Abstract Classes

A class marked abstract cannot be instantiated and may contain abstract methods (declaration only, no implementation). Subclasses must implement all abstract methods:

Abstract.java
    abstract class Shape {
    // abstract method: declaration only, no implementation
    public abstract double area();

    // a concrete method: allowed in an abstract class
    public void display() {
        System.out.printf("Area = %.2f%n", area());
    }
}

class Triangle extends Shape {
    double base, height;
    public Triangle(double b, double h) {
        this.base = b; this.height = h;
    }
    @Override
    public double area() {
        return 0.5 * base * height;
    }
}

public class Main {
    public static void main(String[] args) {
        Shape s = new Triangle(3, 4);
        s.display();
    }
}
    
  
Output
Area = 6.00

Interfaces

An interface is a collection of method contracts, defined with interface. A class implements it with implements, and one class can implement multiple interfaces:

AspectAbstract classInterface
Keywordabstract classinterface
Inheritancesingle inheritance (extends)multiple implementation (implements)
Fieldsany typeimplicitly public static final
Methodscan have concrete methodsabstract by default (Java 8+ may have default)
Constructoryesno
Usageexpresses a "is-a" relationshipexpresses a "can-do" capability
Interface.java
    // define an interface
interface Flyable {
    void fly();  // public abstract by default
}

interface Swimmable {
    void swim();
}

// implement multiple interfaces
class Duck implements Flyable, Swimmable {
    public void fly() {
        System.out.println("Duck flies");
    }
    public void swim() {
        System.out.println("Duck swims");
    }
}

public class Main {
    public static void main(String[] args) {
        Duck d = new Duck();
        d.fly();
        d.swim();

        // interface references can also be polymorphic
        Flyable f = new Duck();
        f.fly();
    }
}
    
  
Output
Duck flies
Duck swims
Duck flies
?
Quiz
What are the three prerequisites for polymorphism?
A Inheritance, overriding, parent reference to a subclass object
B An interface, an abstract method, a subclass
C final, static, this
D public, private, protected
?
Quiz
How many interfaces can a class implement?
A 1
B 2
C Multiple
D At most 3
?
Quiz
What access modifier do a field's default in an interface?
A private
B default
C public static final
D protected
Hands-on
Define an interface Comparable with one compareTo method
 Comparable {
    int (Comparable other);
 }

Day 11 complete!

Congratulations on finishing this lesson. Click "Complete lesson" to record your progress, then continue with the next day.

DAY 12

Exception Handling

Programs will inevitably fail at runtime; exception handling lets a program cope with surprises gracefully.

The Exception Hierarchy

The inheritance structure of Java exceptions:

CategoryBase classFeatureExamples
Checked exceptionExceptioncompiler enforces handling (try-catch or throws)IOException, SQLException
Unchecked exceptionRuntimeExceptioncompiler does not enforce handlingNullPointerException, ArrayIndexOutOfBounds
ErrorErrorserious problems, should not be caughtOutOfMemoryError, StackOverflowError

try-catch-finally

TryCatch.java
    public class TryCatch {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        try {
            System.out.println(arr[5]);  // out of bounds!
            System.out.println("this line never runs");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Caught exception: " + e.getMessage());
        } finally {
            System.out.println("Finally always runs");
        }
        System.out.println("Program continues");
    }
}
    
  
Output
Caught exception: Index 5 out of bounds for length 3
Finally always runs
Program continues
💡
Execution flow: after an exception, remaining code in try is skipped → it jumps to a matching catch → regardless of any exception, finally runs → the program keeps running downward (it does not crash).

Multiple catch

Note: catch blocks must be ordered from subclass to superclass, otherwise there is a compile error (a superclass first would "swallow" all exceptions).

MultiCatch.java
    try {
    String s = null;
    System.out.println(s.length());  // a null pointer
} catch (NullPointerException e) {
    System.out.println("NullPointerException");
} catch (Exception e) {       // parent exceptions go last
    System.out.println("Other exception");
}
    
  
Output
NullPointerException

throws & throw

Aspectthrowsthrow
Positionafter the method signatureinside the method body
Roledeclares exceptions that may be thrownactually throws an exception object
Countcan declare several (comma-separated)throws one per statement
Throws.java
    public class ThrowsDemo {
    // throws declares the exceptions a method may throw
    public static int divide(int a, int b) throws ArithmeticException {
        if (b == 0) {
            // throw actively throws an exception
            throw new ArithmeticException("divisor cannot be zero");
        }
        return a / b;
    }

    public static void main(String[] args) {
        try {
            System.out.println(divide(10, 0));
        } catch (ArithmeticException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}
    
  
Output
Error: divisor cannot be zero

Custom Exceptions

CustomException.java
    // inherit Exception to create a checked exception
class AgeInvalidException extends Exception {
    public AgeInvalidException(String msg) {
        super(msg);
    }
}

public class CustomException {
    public static void checkAge(int age) throws AgeInvalidException {
        if (age < 0 || age > 150) {
            throw new AgeInvalidException("Invalid age: " + age);
        }
        System.out.println("Valid age: " + age);
    }

    public static void main(String[] args) {
        try {
            checkAge(200);
        } catch (AgeInvalidException e) {
            System.out.println("Caught: " + e.getMessage());
        }
    }
}
    
  
Output
Caught: Invalid age: 200
?
Quiz
When does the finally block execute?
A Only when an exception occurs
B Only when there is no exception
C Whether or not an exception occurs
D Not after a catch
?
Quiz
Which category does NullPointerException belong to?
A Checked exception
B Unchecked (RuntimeException)
C Error
D None of these
?
Quiz
What is the difference between throw and throws?
A No difference
B throw throws an exception, throws declares it
C throws throws an exception, throw declares it
D throw is used in the method signature
Hands-on
Catch the array-out-of-bounds exception and print a message
public class Safe {
    public static void main(String[] args) {
        int[] a = {1, 2};
         {
            System.out.println(a[10]);
        }  (ArrayIndexOutOfBoundsException e) {
            System.out.println("Array out of bounds");
        }
    }
 }

Day 12 complete!

Congratulations on finishing this lesson. Click "Complete lesson" to record your progress, then continue with the next day.

DAY 13

The Collections Framework

Arrays have a fixed size; the collections framework provides flexible data containers.

Collections Overview

InterfaceCommon implementationsFeatures
ListArrayList, LinkedListordered, allows duplicates, indexed
SetHashSet, TreeSetunordered, no duplicates
MapHashMap, TreeMapkey-value pairs, keys unique

ArrayList

ArrayList is the most common list; underneath it is an array that grows dynamically:

ArrayListDemo.java
    import java.util.ArrayList;
import java.util.List;

public class ArrayListDemo {
    public static void main(String[] args) {
        // create an ArrayList (generics specify the element type)
        List<String> fruits = new ArrayList<>();

        // add elements
        fruits.add("Apple");
        fruits.add("Banana");
        fruits.add("Orange");

        // get an element
        System.out.println("Second: " + fruits.get(1));

        // modify an element
        fruits.set(0, "Fuji");

        // iterate
        for (String f : fruits) {
            System.out.println(f);
        }

        // remove
        fruits.remove("Orange");
        System.out.println("Size after remove: " + fruits.size());
        System.out.println("Contains Banana? " + fruits.contains("Banana"));
    }
}
    
  
Output
Second: Banana
Fuji
Banana
Orange
Size after remove: 2
Contains Banana? true

HashMap

HashMap stores key-value pairs and looks up a value quickly by key:

HashMapDemo.java
    import java.util.HashMap;
import java.util.Map;

public class HashMapDemo {
    public static void main(String[] args) {
        // create a HashMap
        Map<String, Integer> scores = new HashMap<>();

        // add key-value pairs
        scores.put("Zhang San", 90);
        scores.put("Li Si", 85);
        scores.put("Wang Wu", 95);

        // get a value by key
        System.out.println("Li Si score: " + scores.get("Li Si"));

        // modify: putting the same key overwrites
        scores.put("Zhang San", 88);
        System.out.println("Zhang San new score: " + scores.get("Zhang San"));

        // iterate
        for (Map.Entry<String, Integer> entry : scores.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

        // check whether a key exists
        System.out.println("Has Wang Wu? " + scores.containsKey("Wang Wu"));
        System.out.println("Size: " + scores.size());
    }
}
    
  
Output
Li Si score: 85
Zhang San new score: 88
Li Si: 85
Zhang San: 88
Wang Wu: 95
Has Wang Wu? true
Size: 3
⚠️
HashMap iteration order is not guaranteed! HashMap does not preserve order. For insertion order, use LinkedHashMap; for key-sorted order, use TreeMap.

HashSet

HashSet stores unique elements, often used for deduplication:

HashSetDemo.java
    import java.util.HashSet;
import java.util.Set;

public class HashSetDemo {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("Java");
        set.add("Python");
        set.add("Java");  // duplicate; will not be added
        set.add("C++");

        System.out.println("Size: " + set.size());  // 3
        for (String s : set) {
            System.out.println(s);
        }

        // remove
        set.remove("C++");
        System.out.println("Has C++ after remove? " + set.contains("C++"));
    }
}
    
  
Output
Size: 3
Java
C++
Python
Has C++ after remove? false

In Practice: Counting Word Occurrences

WordCount.java
    import java.util.HashMap;
import java.util.Map;

public class WordCount {
    public static void main(String[] args) {
        String[] words = {"java", "python", "java", "c++", "python", "java"};

        Map<String, Integer> count = new HashMap<>();
        for (String w : words) {
            // getOrDefault: returns a default when the key is absent
            count.put(w, count.getOrDefault(w, 0) + 1);
        }

        for (Map.Entry<String, Integer> e : count.entrySet()) {
            System.out.println(e.getKey() + " appears " + e.getValue() + " times");
        }
    }
}
    
  
Output
python appears 2 times
c++ appears 1 time
java appears 3 times
?
Quiz
Compared with arrays, what is the main advantage of ArrayList?
A Faster
B Can resize dynamically
C Can store any type
D Uses less memory
?
Quiz
Can the keys of a HashMap be duplicated?
A Yes
B No
C Yes, but ignored
D Depends on the implementation
?
Quiz
Which collection does not allow duplicate elements?
A ArrayList
B LinkedList
C HashSet
D An array
Hands-on
Create an ArrayList storing String and add elements
import java.util.ArrayList;
import java.util.List;

public class Demo {
    public static void main(String[] args) {
        List<> list = new ArrayList<>();
        list.("hello");
        System.out.println(list.get(0));
    }
 }

Day 13 complete!

Congratulations on finishing this lesson. Click "Complete lesson" to record your progress, then continue with the next day.

DAY 14

Generics & a Capstone Project

Generics make code safer and more reusable. Today you tie together all the knowledge of the previous 13 days with one project.

Why We Need Generics

Without generics, a collection can store any type (Object), and you must cast when retrieving, which is error-prone:

NoGeneric.java
    import java.util.ArrayList;
import java.util.List;

public class NoGeneric {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("hello");
        list.add(123);       // can mix different types!

        String s = (String) list.get(1);  // runtime ClassCastException
        System.out.println(s);
    }
}
    
  
Output
// throws ClassCastException at runtime

Generics Basics

Generics use angle brackets <T> to specify a type parameter, catching type errors at compile time:

Generic.java
    import java.util.ArrayList;
import java.util.List;

public class Generic {
    public static void main(String[] args) {
        // the collection can only store String
        List<String> list = new ArrayList<>();
        list.add("hello");
        // list.add(123);  // compile error! blocked at compile time

        String s = list.get(0);  // no cast needed
        System.out.println(s);
    }
}
    
  
Output
hello

Custom Generic Classes

GenericClass.java
    // a generic class: T is the type parameter
public class Box<T> {
    private T item;

    public void put(T item) {
        this.item = item;
    }

    public T get() {
        return item;
    }

    public static void main(String[] args) {
        Box<String> strBox = new Box<>();
        strBox.put("gift");
        System.out.println(strBox.get());

        Box<Integer> intBox = new Box<>();
        intBox.put(42);
        System.out.println(intBox.get());
    }
}
    
  
Output
gift
42

Generic Methods

GenericMethod.java
    public class GenericMethod {
    // a generic method: <T> declares the type parameter
    public static <T> void printArray(T[] arr) {
        for (T item : arr) {
            System.out.print(item + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        Integer[] nums = {1, 2, 3};
        String[] strs = {"A", "B", "C"};

        printArray(nums);  // infers T = Integer
        printArray(strs);  // infers T = String
    }
}
    
  
Output
1 2 3
A B C

Wildcards

FormMeaningUsage
<?>any type (unbounded wildcard)read-only; cannot add
<? extends Number>Number and its subclasses (upper bound)read; cannot write
<? super Integer>Integer and its supertypes (lower bound)write; read as Object

Capstone: a Student Management System

This project combines encapsulation, inheritance, collections, generics, and exception handling:

StudentSystem.java
    import java.util.ArrayList;
import java.util.List;

// Student class (encapsulation)
class Student {
    private int id;
    private String name;
    private double score;

    public Student(int id, String name, double score) {
        this.id = id;
        this.name = name;
        this.score = score;
    }

    public int getId() { return id; }
    public String getName() { return name; }
    public double getScore() { return score; }

    public String toString() {
        return String.format("ID:%d  Name:%s  Score:%.1f", id, name, score);
    }
}

// Student management system
class StudentManager {
    private List<Student> students = new ArrayList<>();

    // add a student
    public void add(Student s) {
        students.add(s);
        System.out.println("Added: " + s.getName());
    }

    // delete by id
    public void removeById(int id) {
        students.removeIf(s -> s.getId() == id);
        System.out.println("Deleted ID " + id);
    }

    // list all
    public void listAll() {
        if (students.isEmpty()) {
            System.out.println("No students yet");
            return;
        }
        for (Student s : students) {
            System.out.println(s);
        }
    }

    // compute the average score
    public double average() {
        if (students.isEmpty()) return 0;
        double sum = 0;
        for (Student s : students) sum += s.getScore();
        return sum / students.size();
    }
}

public class StudentSystem {
    public static void main(String[] args) {
        StudentManager mgr = new StudentManager();

        mgr.add(new Student(1, "Alice", 90));
        mgr.add(new Student(2, "Bob", 85));
        mgr.add(new Student(3, "Carol", 78));

        System.out.println("\n--- All students ---");
        mgr.listAll();

        System.out.println("\n--- Delete Bob ---");
        mgr.removeById(2);

        System.out.println("\n--- After deletion ---");
        mgr.listAll();

        System.out.printf("%nAverage: %.1f%n", mgr.average());
    }
}
    
  
Output
Added: Alice
Added: Bob
Added: Carol

--- All students ---
ID:1 Name:Alice Score:90.0
ID:2 Name:Bob Score:85.0
ID:3 Name:Carol Score:78.0

--- Delete Bob ---
Deleted ID 2

--- After deletion ---
ID:1 Name:Alice Score:90.0
ID:3 Name:Carol Score:78.0

Average: 84.0
💡
Congratulations! This system uses: classes & objects (Student), encapsulation (private + getters), constructors, collections (ArrayList), generics (List<Student>), methods, loops, conditionals, and string formatting. You have mastered the core of Java!

Your Next Steps

DirectionContent
Advanced syntaxEnums, annotations, Lambda expressions, the Stream API
I/O and filesByte/char streams, File, NIO
MultithreadingThread, Runnable, the concurrency package
Network programmingSocket, HTTP, TCP/UDP
DatabasesJDBC, connection pools, SQL
FrameworksSpring Boot, MyBatis
?
Quiz
What is the main benefit of generics?
A The program runs faster
B Compile-time type checks avoid runtime cast exceptions
C Fewer lines of code
D Makes code harder to read
?
Quiz
In List, what is String?
A A variable name
B A generic type parameter
C A method
D An annotation
?
Quiz
In the capstone, which collection does StudentManager use to store students?
A An array
B HashMap
C ArrayList
D HashSet
Hands-on
Create a generic class Box that can store an item of any type
public class Box<> {
    private T item;
    public void put( item) { this.item = item; }
    public T get() { return item; }
 }

Day 14 complete!

Congratulations on finishing this lesson. Click "Complete lesson" to record your progress, then continue with the next day.