LW Leon Learn

Java First Steps

Eight small lessons for people who have never programmed: what compiling and running mean, variables, if and for, methods, arrays, strings, your first class, and how to read error messages. After them, the first chapter of the Java course makes sense.

What you need first

  • A computer where you may install software: Windows 10 or 11, macOS or Linux. You only need it for the Set up section. Every example also runs on this page with nothing installed.
  • No programming experience. You do need to create and save a plain text file, and to type exactly what is shown. Java treats capital and small letters as different, and one missing bracket can stop everything.
  • About 30 minutes for the first setup, then 15 to 25 minutes per lesson. Type every example yourself instead of only reading it.
  • A recent browser for the Run button (Chrome or Edge 119, Firefox 120, Safari 18.2, or newer), and pen and paper. Writing small programs by hand is a good way to learn them.

Set up your computer

Optional at first: the examples below also run on this page.

A JDK (Java Development Kit) is the free toolbox that builds and runs Java programs on your own computer. It holds two programs you will use all the time: javac, the compiler, and java, the program that runs your code.

You can skip this section for now. Press Run under any example on this page and it runs in your browser. Be clear about what that is: the Run button uses a reduced copy of Java's standard library. It is enough for these lessons, but some programs behave differently there, and some do not run at all. A real JDK is still worth installing, and your course expects you to use one.

We use Eclipse Temurin from Adoptium, a free and widely used build of the open-source OpenJDK. Choose the latest LTS version. LTS means long-term support: that version keeps getting updates for years. In November 2026 the latest LTS is Java 25. Java 21 and Java 17 are also LTS and work for everything in this guide. Check adoptium.net for the current number.

Two words you will meet below. A terminal is a window where you type commands as text. It is called Command Prompt or PowerShell on Windows, and Terminal on macOS. PATH is the list of folders the terminal searches when you type a command name. If the folder that holds java and javac is not on that list, the terminal says it cannot find them.

If your computer is set to another language, Java may print its messages in that language. To get English messages, put -J-Duser.language=en after javac, as in: javac -J-Duser.language=en Main.java

Windows
  1. Open https://adoptium.net/temurin/releases/ in your browser.

  2. The page lets you pick the version, the package type, the operating system and the architecture. Pick the version marked LTS (25 at the time of writing), the type JDK, the operating system Windows and the architecture x64. Download the file that ends in .msi. Most Windows PCs are x64. If yours has an Arm processor (Settings, System, About, System type), use the Linux steps inside WSL instead.

  3. Double-click the .msi file. Accept the license. On the Custom Setup screen, check that Add to PATH is switched on. It is on by default. Setting JAVA_HOME is optional and not needed for this guide.

  4. Click Next, then Install. Allow the permission prompt. Click Finish.

  5. Open a new Command Prompt: press the Windows key, type cmd, press Enter. A window that was already open during the install cannot see the change, so always open a new one.

  6. Shortcut for people who like commands: instead of steps 1 to 4, run winget install EclipseAdoptium.Temurin.25.JDK in a terminal, then continue at step 5.

Check it worked

java -version
javac -version

You should see Two commands, four lines in all. java -version prints three lines. The first starts with openjdk version and the number you installed, and the next two mention Temurin. javac -version prints one line such as javac 25.0.1. Your exact numbers will differ.

macOS
  1. Find out which chip your Mac has: open the Apple menu, then About This Mac. A name such as Apple M1, M2 or M3 means Apple Silicon. A name that includes Intel means an Intel Mac.

  2. Open https://adoptium.net/temurin/releases/ . Pick the version marked LTS, the type JDK, the operating system macOS, and the architecture: aarch64 for Apple Silicon, or x64 for Intel. Download the file that ends in .pkg.

  3. Open the .pkg file. Click Continue and accept the license. Click Install and type your Mac password when asked. The JDK is placed in /Library/Java/JavaVirtualMachines/.

  4. Open Terminal: press Command and Space, type Terminal, press Enter. If a Terminal window was already open, open a new one. On macOS you do not edit PATH yourself, because the java and javac commands already look for installed JDKs.

  5. Shortcut for people who use Homebrew: run brew install --cask temurin@25 instead of steps 2 and 3.

Check it worked

java -version
javac -version

You should see Four lines in all, as on Windows: three lines from java -version that start with openjdk version and mention Temurin, and one line from javac -version such as javac 25.0.1.

Linux (Debian, Ubuntu, Linux Mint)
  1. Open a terminal. On many desktops the shortcut is Ctrl+Alt+T.

  2. Install the small tools that the next commands use:
    sudo apt install -y wget apt-transport-https gpg

  3. Download Adoptium's signing key, which lets apt check the packages:
    wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/adoptium.gpg > /dev/null

  4. Add the Adoptium package repository (one long line):
    echo "deb https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | sudo tee /etc/apt/sources.list.d/adoptium.list
    On Linux Mint, write UBUNTU_CODENAME in place of VERSION_CODENAME.

  5. Update the package list, then install the JDK:
    sudo apt update
    sudo apt install temurin-25-jdk
    If the latest LTS is no longer 25, use that number instead. Install the jdk package, not the jre one, because the jre package has no javac.

  6. Other systems: Fedora, RHEL and CentOS use dnf with Adoptium's own repository, described at adoptium.net/installation/linux. On Windows you can also use WSL (Windows Subsystem for Linux): run wsl --install in an administrator PowerShell, restart, and follow these steps inside Ubuntu.

Check it worked

java -version
javac -version

You should see Four lines in all: three from java -version that start with openjdk version and mention Temurin, and one from javac -version such as javac 25.0.1.

If you see an error

  • 'javac' is not recognized as an internal or external command, operable program or batch file.

    Windows cannot find javac. Check these three causes in order. First, you opened the window before the install finished: close it and open a new one. Second, Java was not added to PATH: open Settings, then Apps, find Eclipse Temurin JDK, choose Modify, and switch on Add to PATH. Third, you installed a JRE, which can run programs but has no javac: install the JDK. PowerShell says it differently: The term 'javac' is not recognized as the name of a cmdlet, function, script file, or operable program. The meaning is the same.

  • javac: command not found

    Your terminal cannot find javac. The zsh shell on macOS words it as zsh: command not found: javac, and macOS may instead say Unable to locate a Java Runtime. Install the JDK as shown above. If you just installed it, close the terminal and open a new one. If java -version works but javac does not, you installed a JRE instead of a JDK. On Linux, install temurin-25-jdk, not temurin-25-jre. Ubuntu may print Command 'javac' not found, but can be installed with: and a list of packages. You can follow the Temurin steps above instead of that list.

  • javac: file not found: Main.java

    javac cannot see a file with that name in the folder your terminal is in. List the files with dir (Windows) or ls (macOS and Linux). Move into the folder that holds the file with cd, for example cd Desktop. Check the exact name. Windows can hide file extensions, so a file you named Main.java may really be Main.java.txt.

  • error: class Hello is public, should be declared in a file named Hello.java

    A public class must live in a file with exactly the same name, capital letters included. Here the class is Hello, so the file must be Hello.java. Rename the file, or rename the class to match the file. On a computer set to Chinese the message may read 错误: 类Hello是公共的, 应在名为 Hello.java 的文件中声明.

  • Error: Could not find or load main class Main

    The java command cannot find Main.class. Check these causes. You have not compiled yet: run javac Main.java first. You are in a different folder from Main.class: use cd. You typed java Main.class: leave out the .class and type java Main. The capital letters differ: java main is not java Main. After an edit you must compile again. A second line, Caused by: java.lang.ClassNotFoundException: Main, may follow. On a Chinese-language system the first line may read 错误: 找不到或无法加载主类 Main.

  • error: reached end of file while parsing

    Java reached the end of the file while it was still waiting for something. Nearly always it is a closing brace }. Every { needs its own }. Look at the last lines of the file, indent the code neatly, and count the braces. Many editors highlight the partner of a brace when the cursor is next to it. On a Chinese-language system the message may read 错误: 解析时已到达文件结尾.

  • error: cannot find symbol

    Java does not know a name you used. The lines under the message tell you which name (symbol) and where Java looked (location). Usual causes: a typo (totl instead of total), wrong capital letters (string instead of String, system instead of System), using a variable outside the braces where it was created, or a missing import line for a library class such as Arrays. Lesson 8 shows how to read these messages. On a Chinese-language system the message may read 错误: 找不到符号.

01

Source, compile, run: your first program

Goal You can say what source code, compile and run mean, and you can write, compile and run a program that prints text.

A program is a list of instructions for a computer. You write the list as text in a file. That text is called source code. A Java source file has a name that ends in .java.

A computer cannot follow Java text directly. A program called the compiler translates it first. The compiler for Java is javac. It reads Main.java and writes a new file, Main.class, which holds bytecode. Bytecode is a compact list of instructions made for Java's own virtual machine. The translating step is called compiling.

Then the Java Virtual Machine, or JVM, reads the bytecode and carries out the instructions. Starting it is called running the program. You start it with the command java.

So there are always two steps. You type them in a terminal, which is a window for typing commands (see Set up your computer):

javac Main.java
java Main

The first command compiles. The second one runs. Notice that the second command is just Main, with no .java and no .class, because you give the name of a class, not of a file. Newer JDKs (version 11 and later) also accept java Main.java, which does both steps at once. This guide uses two steps so you can see each of them.

If you change the source file, the old Main.class stays exactly as it was. Compile again before you run again. Forgetting this is the most common mistake on the first day.

Now the shape of a program. Java keeps all code inside classes. A class is a named container for code. The public class must have the same name as its file, so a class called Main goes in Main.java. Inside the class we write a method called main. A method is a named block of code that does one job. When you run a program, the JVM looks for main and starts there.

Inside a method, each instruction is a statement. Most statements end with a semicolon. Curly braces { } mark where a class or a method begins and ends. The statement System.out.println prints text and then moves to a new line. System.out.print does the same without moving to a new line. Text must be inside double quotation marks.

Java 25 also allows a much shorter program form. Your course uses the classic form shown here, which works on every version. To run an example on this page, press Run under it. You can press Edit code, change something, and press Run again.

New words

source code
The text of a program, written by a person, in a file ending in .java.
compiler (javac)
The program that translates source code into bytecode and reports mistakes it finds.
bytecode
Compact instructions saved in a .class file, made for the JVM.
JVM
The program, started with the command java, that carries out bytecode.
class and method
A class is a named container for code. A method is a named block of code inside it that does one job.
statement
One instruction. Most statements end with a semicolon.

Worked example

Java 8+
public class Main {
    public static void main(String[] args) {
        System.out.println("Hello, Java!");
        System.out.println("My name is Leon.");
        System.out.println(2 + 3);
    }
}
Predict the output first, then show it

Expected output

Hello, Java!
My name is Leon.
5

Step by step

  1. public class Main { starts the class. Its name is Main, so the file must be Main.java. The brace opens the class; the last line of the file closes it.

  2. public static void main(String[] args) { starts the method that the JVM runs first. Type it exactly as shown. For now, read the words like this: public means the JVM is allowed to call it; static means it belongs to the class itself; void means it gives nothing back; String[] args is a list of words typed after the program name, which we do not use yet.

  3. System.out.println("Hello, Java!"); prints the text between the quotation marks. The quotation marks themselves are not printed. println then moves to a new line.

  4. The next println prints a second line. Every statement ends with a semicolon.

  5. System.out.println(2 + 3); has no quotation marks, so Java works out 2 + 3 and prints the answer, 5. Quotation marks mean text. No quotation marks mean Java should calculate.

  6. The two closing braces end main and then the class. Every { needs its own }.

Common mistakes

Wrong
Main.java.txt

A text editor may add .txt to the name, and Windows can hide file extensions, so you do not see it. javac then reports: javac: file not found: Main.java. In File Explorer, open the View menu and turn on File name extensions to see the real name. In Notepad's Save dialog, set Save as type to All files before you type Main.java. On a Mac, TextEdit saves rich text by default: choose Format, then Make Plain Text, before you save, or use another editor such as VS Code.

Right
Main.java
Wrong
java Main    (typed after editing Main.java, without compiling again)

java runs Main.class, the old compiled copy. Your edit is not in it, so the program behaves as before and you wonder why nothing changed. Compile again first.

Right
javac Main.java
java Main
Wrong
system.out.println("Hi");

Java treats capital and small letters as different letters, so system and System are different names. javac says: error: package system does not exist. The class is called System, with a capital S.

Right
System.out.println("Hi");

Check yourself

  1. Write the two commands that compile and then run a program whose public class is called Greeting.

    Try it first, then show the answer

    javac Greeting.java, and then java Greeting. The file must be named Greeting.java, and the run command has no extension.

  2. Change the example so that its first line prints your own name and its second line prints the result of 4 * 5.

    Try it first, then show the answer

    For example: System.out.println("My name is Noah."); and then System.out.println(4 * 5); The second line prints 20, because there are no quotation marks and Java calculates.

  3. Predict what System.out.print("A"); System.out.print("B"); System.out.println("C"); prints.

    Try it first, then show the answer

    ABC on a single line. print does not move to a new line, so the letters stay together. Only println ends the line.

02

Variables and types

Goal You can declare variables of type int, double, boolean, char and String, change their values, and predict what division and + give.

A variable is a named place in the computer's memory that holds one value. You choose the name. Java also needs to know what kind of value the variable may hold. That kind is called the variable's type.

These are the five types to learn first:
int: a whole number, such as 7 or -3.
double: a number with a decimal part, such as 1.5.
boolean: either true or false.
char: one character in single quotation marks, such as 'A'.
String: text in double quotation marks, such as "Mia". Write String with a capital S.

You declare a variable by writing its type, its name and a first value: int apples = 7; The = sign means "store the value on the right into the name on the left". It does not mean "is equal to", as in maths. Later you can store a new value: apples = apples + 1; Read it as "take the old value of apples, add 1, and store the result back in apples". You do not write the type again, because the variable already exists.

Java checks types before the program runs. It will not store 2.5 in an int, because the .5 would be lost. It also wants every variable to have a value before you use it.

For arithmetic you have + - * / and %, where % gives the remainder after division. If both sides of / are int, the answer is an int and the decimal part is cut off: 7 / 2 is 3, not 3.5. If one side is a double, the answer is a double: 7 / 2.0 is 3.5. Java does * and / before + and -, and otherwise works from left to right.

The + sign also joins text. Joining text is called concatenation. "Mia" + " is " + 7 gives the text Mia is 7. Because Java works from left to right, the order matters, as the mistakes below show.

New words

variable
A named place in memory that holds one value.
type
The kind of value a variable may hold, such as int or String.
declare and assign
To declare is to create a variable with its type. To assign is to store a value in it with =.
integer division
Dividing an int by an int. The answer is an int and the decimal part is dropped.
concatenation
Joining pieces of text into one text with +.

Worked example

Java 8+
public class Main {
    public static void main(String[] args) {
        String name = "Mia";
        int apples = 7;
        int people = 2;
        double price = 1.5;
        boolean cheap = price < 2.0;
        char grade = 'A';

        System.out.println(name + " has grade " + grade);
        System.out.println("whole share: " + apples / people);
        System.out.println("left over: " + apples % people);
        System.out.println("exact share: " + apples / 2.0);
        System.out.println("total cost: " + apples * price);
        System.out.println("cheap? " + cheap);

        apples = apples + 1;
        System.out.println("apples now: " + apples);
    }
}
Predict the output first, then show it

Expected output

Mia has grade A
whole share: 3
left over: 1
exact share: 3.5
total cost: 10.5
cheap? true
apples now: 8

Step by step

  1. String name = "Mia"; creates a variable called name of type String and stores the text Mia.

  2. int apples = 7; and int people = 2; store whole numbers. double price = 1.5; stores a number with a decimal part.

  3. boolean cheap = price < 2.0; first works out the comparison price < 2.0, which is true, and stores that answer. char grade = 'A'; stores one character, written in single quotation marks.

  4. In "whole share: " + apples / people, Java divides first, because / comes before +. 7 / 2 with two ints gives 3. The .5 is cut off.

  5. apples % people is the remainder when 7 is divided by 2, which is 1.

  6. apples / 2.0 has a double on the right, so the answer is the double 3.5. apples * price multiplies an int by a double and gives the double 10.5.

  7. apples = apples + 1; stores 8 in apples. There is no type word, because the variable already exists.

Common mistakes

Wrong
int half = 7 / 2.0;

7 / 2.0 is the double 3.5. An int cannot hold the .5, so javac refuses: error: incompatible types: possible lossy conversion from double to int. Choose a type that fits the value.

Right
double half = 7 / 2.0;
Wrong
System.out.println("Sum: " + 2 + 3);

Java works from left to right. "Sum: " + 2 gives the text Sum: 2, and then + 3 joins one more character. The line prints Sum: 23. Put the arithmetic in brackets.

Right
System.out.println("Sum: " + (2 + 3));    // prints Sum: 5
Wrong
int total;
System.out.println(total);

total has no value yet. javac says: error: variable total might not have been initialized. Give a variable a value when you declare it.

Right
int total = 0;
System.out.println(total);
Wrong
char grade = "A";

Double quotation marks make a String, and single quotation marks make a char. javac says: error: incompatible types: String cannot be converted to char.

Right
char grade = 'A';

Check yourself

  1. Predict the three lines printed by: int a = 9; System.out.println(a / 4); System.out.println(a % 4); System.out.println(a / 4.0);

    Try it first, then show the answer

    2, then 1, then 2.25. The first two lines use only ints, so 9 / 4 drops the decimals, and 9 % 4 is the remainder. The third line has a double, so the decimals stay.

  2. Write two lines: one that declares a double called temperature holding 21.5, and one that declares a String called owner holding Emma.

    Try it first, then show the answer

    double temperature = 21.5;
    String owner = "Emma";

  3. Predict the two lines printed by: System.out.println("Total: " + 4 + 1); and System.out.println(4 + 1 + " total");

    Try it first, then show the answer

    Total: 41 and then 5 total. In the first line the text comes first, so each number is joined as text. In the second line 4 + 1 is calculated first, giving 5, and then the text is joined on.

03

Decisions and repeats: if and for

Goal You can make a program choose between two paths with if and else, and repeat work a set number of times with for.

Normally Java runs the statements from top to bottom, once each. Two tools change that. An if statement chooses whether to run some statements. A for statement repeats some statements.

An if statement has a condition in brackets, followed by a block. A block is a group of statements inside curly braces. The condition must be a boolean, which means true or false. If it is true, Java runs the block. If it is false, Java skips the block, or runs the block after else when there is one. The else part is optional. To test several cases in order, write another if after else. Java takes the first block whose condition is true and skips the rest.

Conditions usually come from comparisons: < (less than), <= (less than or equal), > (greater than), >= (greater than or equal), == (equal) and != (not equal). You can combine conditions: && means and, || means or, and ! means not. Watch the difference: two equals signs, ==, compare two values. One equals sign, =, stores a value.

A for loop repeats a block. A loop is any statement that runs a block again and again. The brackets of a for hold three parts separated by semicolons, as in for (int i = 1; i <= 5; i++). The start, int i = 1, creates a counter named i and sets it to 1. The test, i <= 5, is checked before every round. When it is false, the loop ends. The step, i++, adds 1 to i after every round. It is short for i = i + 1. Likewise i-- subtracts 1.

Tracing means following a program by hand, writing down the value of each variable after each step. It is how you find out what code does without running it, and it is a skill worth practising on paper.

New words

condition
A test whose answer is true or false. Both if and for use one.
block
A group of statements inside curly braces.
loop
A statement that runs a block again and again.
trace
Following a program by hand and writing down each variable after each step.

Worked example

Java 8+
public class Main {
    public static void main(String[] args) {
        int total = 0;
        for (int i = 1; i <= 5; i++) {
            total = total + i;
            if (i % 2 == 0) {
                System.out.println(i + " is even, total " + total);
            } else {
                System.out.println(i + " is odd, total " + total);
            }
        }
        System.out.println("final total " + total);
    }
}
Predict the output first, then show it

Expected output

1 is odd, total 1
2 is even, total 3
3 is odd, total 6
4 is even, total 10
5 is odd, total 15
final total 15

Step by step

  1. int total = 0; creates the running total before the loop starts.

  2. for (int i = 1; i <= 5; i++) starts the loop. i begins at 1, and the block runs as long as i <= 5.

  3. Round 1: i is 1, so total becomes 0 + 1 = 1. The remainder 1 % 2 is 1, not 0, so the condition i % 2 == 0 is false. The else block prints 1 is odd, total 1.

  4. Round 2: i is 2, and total becomes 3. 2 % 2 is 0, so the condition is true and the first block prints 2 is even, total 3.

  5. Rounds 3, 4 and 5 add 3, 4 and 5, so the totals are 6, 10 and 15.

  6. After round 5, i++ makes i equal to 6. The test 6 <= 5 is false, so the loop ends. The last println is outside the braces of the loop, so it runs once, after the loop.

Common mistakes

Wrong
if (score = 50) {

One equals sign stores a value. It does not compare. javac says: error: incompatible types: int cannot be converted to boolean, because the condition must be true or false. To compare, use two equals signs.

Right
if (score == 50) {
Wrong
for (int i = 0; i < 3; i++);
{
    System.out.println("hi");
}

The semicolon right after the brackets is an empty statement. It is the whole body of the loop, so the loop repeats nothing three times. The block below it is not part of the loop, so it runs once. The program prints hi one time, not three times.

Right
for (int i = 0; i < 3; i++) {
    System.out.println("hi");
}
Wrong
for (int i = 1; i < 5; i++) {
    System.out.println(i);
}    // meant to print 1 to 5

The test i < 5 becomes false when i reaches 5, so the loop stops before printing 5. It prints 1, 2, 3 and 4. Decide whether the last value should be included. If it should, use <=.

Right
for (int i = 1; i <= 5; i++) {
    System.out.println(i);
}
Wrong
if (x > 0)
    System.out.println("positive");
    System.out.println("done");

Without braces, only the first statement after if belongs to it. The line that prints done is outside the if and always runs, even when x is not positive, although the indentation suggests otherwise. Java ignores indentation. Always use braces.

Right
if (x > 0) {
    System.out.println("positive");
}
System.out.println("done");

Check yourself

  1. Write a for loop that prints 10, 20, 30, 40 and 50, each on its own line.

    Try it first, then show the answer

    for (int i = 1; i <= 5; i++) {
    System.out.println(i * 10);
    }

  2. Predict what this prints: for (int i = 3; i > 0; i--) { System.out.println(i); }

    Try it first, then show the answer

    3, then 2, then 1, each on its own line. i starts at 3 and goes down by 1 each round. When i is 0 the test i > 0 is false, so 0 is not printed.

  3. Write a condition that is true when age is at least 18 and below 65.

    Try it first, then show the answer

    age >= 18 && age < 65. Both parts must be true, so they are joined with &&. Writing 18 <= age < 65 does not work in Java.

04

Methods: naming a piece of work

Goal You can write a method with parameters and a return value, and call it from main.

A method is a named block of code. You write it once and use it as many times as you like. Using it is called calling it. Methods save you from copying the same lines again and again, and a good name tells the reader what the lines do.

A method has four parts. In static int square(int n) { return n * n; } they are these. The return type, int, says what kind of value comes back. The name is square. The parameter list, (int n), declares the inputs: a parameter is a variable that receives an input. The body, in braces, is the code that runs. The word static is needed for now because main is static. Leave it in for the time being. Lesson 7 shows methods without it.

To call a method, write its name and, in brackets, the values to give it: square(6). These values are the arguments. Java copies each argument into the matching parameter. The statement return sends a value back and ends the method. The call square(6) then stands for the value 36, and you can use it anywhere a value fits, for example inside println, or on the right of an = sign.

Some methods give nothing back. Their return type is void, and they need no return statement. In the example, greet is like this.

A variable created inside a method, parameters included, is local. It exists only while the method runs, and no other method can see it. Because arguments are copied, changing a parameter inside a method does not change the variable in the code that called the method, which is known as the caller. The example shows this with n.

New words

method and call
A method is a named block of code. To call it is to run it by writing its name with brackets.
parameter and argument
A parameter is a variable in the method's header that receives an input. An argument is the value you pass in a call.
return value
The value a method sends back with return. Its kind is the return type; void means nothing comes back.
local variable
A variable that exists only inside one method while it runs.

Worked example

Java 8+
public class Main {
    static void greet(String name) {
        System.out.println("Hello, " + name + "!");
    }

    static int square(int n) {
        return n * n;
    }

    static int addOne(int n) {
        n = n + 1;
        return n;
    }

    public static void main(String[] args) {
        greet("Iris");
        int result = square(6);
        System.out.println("6 squared is " + result);
        System.out.println(square(square(2)));

        int n = 5;
        int bigger = addOne(n);
        System.out.println("n is " + n + ", bigger is " + bigger);
    }
}
Predict the output first, then show it

Expected output

Hello, Iris!
6 squared is 36
16
n is 5, bigger is 6

Step by step

  1. greet is a void method. It prints and gives nothing back. Its parameter name receives the text it is given.

  2. square takes an int and returns an int. return n * n; sends the product back to the caller.

  3. addOne changes its own copy of n, and then returns it.

  4. In main, greet("Iris") copies the argument "Iris" into name, and Hello, Iris! is printed.

  5. square(6) runs the body with n set to 6 and gives back 36, which is stored in result.

  6. In square(square(2)) the inner call runs first and gives 4. Then the outer call squares 4 and gives 16.

  7. main has its own n, which is 5. addOne(n) copies 5 into a separate n inside addOne and changes that copy to 6. main's n is still 5. The returned 6 is stored in bigger.

Common mistakes

Wrong
static int square(int n) {
    int result = n * n;
}

The method promises to give back an int but never does. javac says: error: missing return statement. Every path through a method that has a return type must end in a return.

Right
static int square(int n) {
    int result = n * n;
    return result;
}
Wrong
int n = 5;
addOne(n);
System.out.println(n);    // expecting 6

The call works out 6 and returns it, but nothing stores the answer. The parameter was a copy, so n in main is still 5. The program prints 5. Store the returned value.

Right
int n = 5;
n = addOne(n);
System.out.println(n);    // prints 6
Wrong
int square(int n) {
    return n * n;
}

main is static, so it can call only other static methods of the same class without an object. javac says: error: non-static method square(int) cannot be referenced from a static context. For now, add static. Lesson 7 explains methods without it.

Right
static int square(int n) {
    return n * n;
}

Check yourself

  1. Write a method max2 that takes two ints and returns the larger one.

    Try it first, then show the answer

    static int max2(int a, int b) {
    if (a > b) {
    return a;
    }
    return b;
    }
    If a is larger it returns a at once. Otherwise it reaches the last line and returns b.

  2. Predict what is printed: static int twice(int x) { x = x * 2; return x; } and then, in main, int a = 4; twice(a); System.out.println(a);

    Try it first, then show the answer

    4. The parameter x is a copy of a. Doubling x does not touch a, and the returned 8 was never stored.

  3. Fix this method: static void add(int a, int b) { return a + b; }

    Try it first, then show the answer

    javac says: error: incompatible types: unexpected return value. A void method cannot return a value. Change void to int: static int add(int a, int b) { return a + b; }

05

Arrays: many values in a row

Goal You can create an array, read and change its elements by index, loop over it, and print it.

An array holds a fixed number of values of the same type, stored in a row. Picture a row of numbered boxes. Each box is an element. The number of a box is its index. Indexes start at 0, not at 1. An array with 3 elements has the indexes 0, 1 and 2.

You can create an array with values: int[] scores = {80, 95, 70}; The type int[] means "array of int". Or you can create one of a given size: int[] blanks = new int[3]; Every element then starts with a default value: 0 for numbers, false for boolean, and null for String. null means "no object".

Read an element with scores[0]. Change one with scores[1] = 100; The size is scores.length. Notice that length has no brackets after it. The size can never change after the array is created.

The last valid index is length - 1. An index outside 0 to length - 1 is not a compile error. The program stops when it reaches that line, with an ArrayIndexOutOfBoundsException. An exception is Java's way of reporting a problem while the program runs. Lesson 8 shows how to read one.

To visit every element, loop over the indexes: for (int i = 0; i < scores.length; i++). There is also a shorter form, the for-each loop: for (int s : scores) { ... } Read it as "for each int s in scores". The variable s takes each value in turn. Use it when you do not need the index.

Printing an array directly shows a code such as [I@6d06d69c, not the values. To see the values, put import java.util.Arrays; above the class and print Arrays.toString(scores). An import line tells Java which library class you mean. The library is the large collection of ready-made classes that comes with Java.

New words

array
A fixed number of values of one type, stored in a row.
element and index
An element is one value in the array. Its index is its position, counted from 0.
exception
A report that something went wrong while the program was running.
import
A line at the top of a file that names a library class you want to use.

Worked example

Java 8+
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] scores = {80, 95, 70};
        System.out.println("first = " + scores[0]);
        System.out.println("length = " + scores.length);

        scores[1] = 100;
        System.out.println(Arrays.toString(scores));

        int total = 0;
        for (int i = 0; i < scores.length; i++) {
            total = total + scores[i];
        }
        System.out.println("total = " + total);

        for (int s : scores) {
            System.out.println("score: " + s);
        }

        int[] blanks = new int[3];
        System.out.println(Arrays.toString(blanks));
    }
}
Predict the output first, then show it

Expected output

first = 80
length = 3
[80, 100, 70]
total = 250
score: 80
score: 100
score: 70
[0, 0, 0]

Step by step

  1. import java.util.Arrays; makes the library class Arrays available. It goes above the class.

  2. int[] scores = {80, 95, 70}; creates an array of three ints. scores[0] is 80, scores[1] is 95 and scores[2] is 70. scores.length is 3.

  3. scores[1] = 100; replaces the element at index 1. Arrays.toString turns the array into readable text: [80, 100, 70].

  4. The first loop uses i as the index, from 0 up to 2. When i becomes 3, the test i < scores.length is false, so index 3 is never touched. It adds 80, 100 and 70 and gets 250.

  5. The for-each loop visits the three values in order. s holds one value at a time.

  6. new int[3] makes three elements, and all of them start as 0.

Common mistakes

Wrong
for (int i = 0; i <= scores.length; i++) {
    System.out.println(scores[i]);
}

With 3 elements the last index is 2, but i reaches 3. The program prints the three scores and then stops with: Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3. The line after it names your file and line number. Java 8 prints only the number 3 after the colon. Use < with length.

Right
for (int i = 0; i < scores.length; i++) {
    System.out.println(scores[i]);
}
Wrong
int[] copy = scores;
copy[0] = 0;

This does not copy the boxes. It gives the same array a second name. Changing copy[0] also changes scores[0], so printing scores now shows [0, 95, 70]. To make a real copy, use Arrays.copyOf.

Right
int[] copy = Arrays.copyOf(scores, scores.length);
copy[0] = 0;    // scores is unchanged
Wrong
System.out.println(scores.length());

For an array, length is a value, not a method, so it takes no brackets. javac says: error: cannot find symbol, followed by symbol: method length(). Strings are different, as lesson 6 shows: they do use brackets.

Right
System.out.println(scores.length);

Check yourself

  1. Predict what is printed: int[] a = {5, 6, 7}; a[0] = a[2]; System.out.println(Arrays.toString(a));

    Try it first, then show the answer

    [7, 6, 7]. The statement copies the value at index 2 into index 0. The 5 is overwritten.

  2. Write a loop that prints the elements of scores from last to first.

    Try it first, then show the answer

    for (int i = scores.length - 1; i >= 0; i--) {
    System.out.println(scores[i]);
    }
    The loop starts at the last index and counts down to 0.

  3. How many elements does new int[5] have, and what is the largest valid index?

    Try it first, then show the answer

    It has 5 elements, all 0. The valid indexes are 0 to 4, so the largest is 4. Using index 5 stops the program with an ArrayIndexOutOfBoundsException.

06

Strings, and == versus equals

Goal You can use common String methods and compare two strings correctly.

A String is a piece of text, made of characters in order. You write a string value in double quotation marks. String is not a simple type like int. It is a class, and every string value is an object. An object is a value created from a class, and it lives somewhere in memory. A variable of type String does not hold the text itself. It holds a reference, which says where the object is. Think of a reference as a label that points to the object.

You use an object's methods with a dot, as in word.length(). These are useful: length() gives the number of characters. charAt(i) gives the character at index i, counted from 0, like an array. substring(a, b) gives the characters from index a up to, but not including, index b. indexOf("l") gives the index of the first match, or -1 if there is none. toUpperCase() and toLowerCase() change capital letters. Note that length() for a String has brackets, unlike length for an array.

A String can never be changed after it is made. Methods such as toUpperCase() do not change the original. They return a new String. To keep the result, store it: word = word.toUpperCase();

Now == and equals. For references, == asks: do these two names point at the very same object? It does not ask whether the text matches. Two different objects can hold the same letters. The method equals asks: do these two strings contain the same characters? That is nearly always what you mean. So compare strings with a.equals(b), never with a == b. Use equalsIgnoreCase to ignore capital letters.

== sometimes seems to work. Java stores identical string literals (text typed directly in the code) only once, so two literals with the same text are the same object. But as soon as the text comes from somewhere else, such as the keyboard, a file or a calculation, == gives false. A bug that works in your tests and fails later is the worst kind. Always use equals.

New words

String
Text made of characters in order. In Java it is a class, and each value is an object.
object and reference
An object is a value created from a class. A reference tells where the object is.
literal
A value written directly in the code, such as "hello" or 42.
immutable
Cannot be changed after it is made. A String is immutable.

Worked example

Java 8+
public class Main {
    public static void main(String[] args) {
        String word = "hello";
        String typed = new String("hello");    // pretend this text came from the keyboard

        System.out.println("same text? " + word.equals(typed));
        System.out.println("same object? " + (word == typed));

        String shout = word.toUpperCase();
        System.out.println(shout + " " + word);
        System.out.println(word.length());
        System.out.println(word.charAt(1));
        System.out.println(word.substring(1, 3));
        System.out.println(word.indexOf("l"));
        System.out.println("ignoring case? " + word.equalsIgnoreCase("HELLO"));
    }
}
Predict the output first, then show it

Expected output

same text? true
same object? false
HELLO hello
5
e
el
2
ignoring case? true

Step by step

  1. word is a literal. new String("hello") builds a second, separate object with the same letters. We pretend that this text came from the keyboard.

  2. equals compares the letters, so it gives true.

  3. == asks whether word and typed are the same object. They are two objects, so it gives false. The brackets around word == typed are needed, because otherwise + would be done first.

  4. toUpperCase() returns a new string, HELLO. word itself is unchanged, as the same line shows.

  5. length() is 5. charAt(1) is e, because counting starts at 0 and h is index 0.

  6. substring(1, 3) takes the characters at indexes 1 and 2, but not 3. The result is el.

  7. indexOf("l") finds the first l at index 2. equalsIgnoreCase treats hello and HELLO as the same.

Common mistakes

Wrong
if (answer == "yes") {

This asks whether answer is the same object as the literal. If the text in answer was typed or built, the result is false even when it says yes. Use equals. Writing the literal first, "yes".equals(answer), also avoids a crash when answer is null, which means "no object at all", because calling a method on null stops the program.

Right
if ("yes".equals(answer)) {
Wrong
name.toUpperCase();
System.out.println(name);

toUpperCase returns a new String and leaves name alone. Nothing stored the new String, so name is unchanged and the capital letters are lost.

Right
name = name.toUpperCase();
System.out.println(name);
Wrong
String part = "hello".substring(1, 3);    // expecting "ell"

The end index is not included. substring(1, 3) takes indexes 1 and 2 and gives el. To include index 3, the end must be 4.

Right
String part = "hello".substring(1, 4);    // ell

Check yourself

  1. Predict what is printed: String s = "Java"; System.out.println(s.substring(1, 3)); System.out.println(s.charAt(0)); System.out.println(s.length());

    Try it first, then show the answer

    av, then J, then 4. substring(1, 3) takes the letters at indexes 1 and 2, which are a and v. charAt(0) is the first letter. The string has 4 characters.

  2. Fix this line so that it compares the text: if (command == "stop") {

    Try it first, then show the answer

    if ("stop".equals(command)) { or if (command.equals("stop")) { The first form is safer, because it still works when command is null.

  3. Write one line that prints true when the String name equals Leon, ignoring capital letters.

    Try it first, then show the answer

    System.out.println(name.equalsIgnoreCase("Leon"));

07

Your first class, with a constructor

Goal You can write a small class with fields, a constructor and methods, create objects from it with new, and call their methods.

So far you used types that Java provides. A class lets you make your own type. A class is a blueprint: it says what data each object has and what each object can do. An object is one thing built from the blueprint. One class, such as Player, can make many objects, and each object has its own data.

The data are called fields. A field is a variable that belongs to an object. You declare it inside the class but outside every method. We mark fields private. private means that only code inside the same class may use the field. This protects the data, and other code must go through the class's methods.

A method without the word static works on one particular object. Calling addPoints(5) on Leon's object changes Leon's score and nobody else's. Inside the class, such a method uses the fields by their names.

A constructor is a special method that runs when an object is created with new. It has the same name as the class and no return type, not even void. Its job is to give the fields their first values. new Player("Leon") creates the object, runs the constructor with "Leon" as the argument, and gives you a reference to the new object.

Inside the constructor, the parameter and the field may both be called name. Writing this.name means "the name field of this object". Writing name alone means the parameter. So this.name = name; stores the argument in the field.

Where does a class go? In real projects each class gets its own file, named after it. To keep this example in one file, the extra class goes above public class Main. Only the class called Main may be public there.

New words

class and object
A class is a blueprint. An object is one thing built from it with new.
field
A variable that belongs to an object, declared inside the class and outside every method.
constructor
A special method with the class's name and no return type. It runs when new creates an object and sets up the fields.
this
A name for the object whose method is running. this.name is that object's field.
private
Only code inside the same class may use it.

Worked example

Java 8+
class Player {
    private String name;
    private int score;

    Player(String name) {
        this.name = name;
        this.score = 0;
    }

    void addPoints(int points) {
        score = score + points;
    }

    String describe() {
        return name + " has " + score + " points";
    }
}

public class Main {
    public static void main(String[] args) {
        Player leon = new Player("Leon");
        Player mia = new Player("Mia");

        leon.addPoints(5);
        leon.addPoints(3);
        mia.addPoints(10);

        System.out.println(leon.describe());
        System.out.println(mia.describe());
    }
}
Predict the output first, then show it

Expected output

Leon has 8 points
Mia has 10 points

Step by step

  1. class Player { starts the blueprint. It is not public, so the same file may also hold public class Main.

  2. private String name; and private int score; are the fields. Every Player object gets its own pair.

  3. Player(String name) is the constructor: the class's name and no return type. this.name = name; stores the argument in the field. this.score = 0; starts the score at zero.

  4. addPoints(int points) adds to this object's score. It is not static, so it works on whichever object it is called on.

  5. describe() builds a String from the fields and returns it.

  6. In main, new Player("Leon") creates one object and new Player("Mia") creates another. leon and mia are variables that refer to them.

  7. leon.addPoints(5) changes only Leon's object. After 5 and 3, Leon has 8. Mia's object is untouched and gets its own 10. describe() is called on each object, so each prints its own data.

Common mistakes

Wrong
Player leon = Player("Leon");

Without new, Java looks for an ordinary method called Player and cannot find one. javac says: error: cannot find symbol, with symbol: method Player(String). Create objects with new.

Right
Player leon = new Player("Leon");
Wrong
void Player(String name) {
    this.name = name;
}

A return type, even void, turns it into an ordinary method that happens to be called Player. The class then has no constructor that takes a String, and new Player("Leon") fails with: error: constructor Player in class Player cannot be applied to given types; and required: no arguments, found: String.

Right
Player(String name) {
    this.name = name;
}
Wrong
Player(String name) {
    name = name;
    this.score = 0;
}

Both sides are the parameter, so this assigns the parameter to itself. The field name is never set and keeps its default value, null. No error appears. describe() then prints null has 0 points. Put this. in front of the field.

Right
Player(String name) {
    this.name = name;
    this.score = 0;
}

Check yourself

  1. Add a method getScore() to Player that returns the score as an int. Then write the line in main that prints Mia's score.

    Try it first, then show the answer

    Inside Player:
    int getScore() {
    return score;
    }
    In main:
    System.out.println(mia.getScore());
    It prints 10.

  2. Predict what is printed after these lines are added at the end of main: Player noah = new Player("Noah"); System.out.println(noah.describe());

    Try it first, then show the answer

    Noah has 0 points. The constructor set score to 0 and addPoints was never called on this object.

  3. What does javac say if main contains leon.score = 100; and why?

    Try it first, then show the answer

    error: score has private access in Player. The field is private, so code outside Player may not use it. Change the score through a method such as addPoints instead.

08

Reading a compile error and a stack trace

Goal You can find the line and the cause in a javac error message and in an exception stack trace.

Programs go wrong in three ways. A compile error means javac refuses to build the program, and nothing runs. A runtime error means the program starts, and then Java stops it at some line and prints a stack trace. A wrong answer means the program runs to the end but prints the wrong thing, and Java says nothing. The first two come with a message. This lesson is about reading those messages. The mistakes below show real ones.

Read a compile error from the top. The first line has three parts: the file and line number (such as Main.java:4), the word error, and what is wrong (such as cannot find symbol). The next line repeats the code from that line, with a caret ^ under the spot where Java got stuck. Some errors add detail lines, such as symbol: variable totl and location: class Main, which say which name Java does not know and where it looked. The last line counts the errors.

Three habits help. Fix the first error first, because later ones are often caused by it. A single missing brace can produce a dozen. Compile again after each fix. Next, the line number is where Java noticed the problem, and the real mistake can be a line above. Finally, treat the message as a hint, not a diagnosis. cannot find symbol only says that Java does not know a name.

A stack trace appears when an exception is not handled. An exception is an event that means something went wrong while the program was running, such as dividing by zero or using an index outside an array. The program stops at that point. The trace begins with a line that names the exception and gives a message. Then come lines that start with at. Each one is a method that was running, with the file name and line number in brackets. The top at line is where the exception happened. The line below it is the method that called it, and so on down to main. Find the first line that names your own file, such as Main.java, and open that file at that line. Lines that mention java.base or java.util are Java's own code. The cause is almost always in yours.

You can handle an exception instead of letting it stop the program. Put the risky statements in a try block. If an exception of the named kind happens there, Java skips the rest of the try block and runs the catch block. The program then carries on after the catch. The example works this way.

New words

compile error
A mistake javac finds before the program runs. Nothing runs until it is fixed.
runtime error
A problem that appears while the program runs and stops it.
exception
An event that signals a problem at run time, such as ArithmeticException.
stack trace
The list of methods that were running when an exception happened, newest first, each with a file name and line number.
try and catch
try holds statements that might throw an exception. catch runs if one of the named kind happens.

Worked example

Java 8+
public class Main {
    static int readAge(String text) {
        return Integer.parseInt(text);
    }

    public static void main(String[] args) {
        System.out.println("start");
        try {
            System.out.println("age is " + readAge("12"));
            System.out.println("age is " + readAge("twelve"));
            System.out.println("this line is skipped");
        } catch (NumberFormatException e) {
            System.out.println("not a whole number");
        }
        System.out.println("done");
    }
}
Predict the output first, then show it

Expected output

start
age is 12
not a whole number
done

Step by step

  1. readAge calls Integer.parseInt, a Java method that turns text such as 12 into the int 12. It throws a NumberFormatException when the text is not a whole number.

  2. main prints start and then enters the try block.

  3. readAge("12") works and gives 12, so age is 12 is printed.

  4. readAge("twelve") throws a NumberFormatException. Java leaves the try block at once, so the line this line is skipped never runs.

  5. The catch (NumberFormatException e) block matches the kind of exception, so it runs and prints not a whole number.

  6. The program carries on after the catch and prints done. Without the try and catch, the exception would stop the program at step 4. Java would print Exception in thread "main" java.lang.NumberFormatException: For input string: "twelve", then some at lines, and done would never appear.

Common mistakes

Wrong
public class Main {
    public static void main(String[] args) {
        int total = 5
        System.out.println(total);
    }
}

javac prints Main.java:3: error: ';' expected, then the line int total = 5 with the caret just after the 5. A statement is missing its semicolon. The line number points at the statement that lacks it. Add the semicolon where the caret is.

Right
public class Main {
    public static void main(String[] args) {
        int total = 5;
        System.out.println(total);
    }
}
Wrong
public class Main {
    public static void main(String[] args) {
        int total = 5;
        System.out.println(totl);
    }
}

javac prints four lines: Main.java:4: error: cannot find symbol, then the code line, then symbol: variable totl, then location: class Main. Read them like this. The mistake is on line 4 of Main.java. The unknown name is totl, a variable. Java looked in class Main and did not find it. The variable was declared as total, so this is a typo.

Right
public class Main {
    public static void main(String[] args) {
        int total = 5;
        System.out.println(total);
    }
}
Wrong
public class Main {
    static int average(int total, int count) {
        return total / count;
    }

    public static void main(String[] args) {
        System.out.println(average(10, 0));
    }
}

This compiles, but when it runs Java prints:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Main.average(Main.java:3)
at Main.main(Main.java:7)
Read it from the top. The first line says what happened: an integer was divided by zero. The first at line is where: method average, line 3 of Main.java, the line return total / count;. The next at line says who called it: main, at line 7. So the division is on line 3, and the 0 came from the call on line 7. Decide which one to fix. Here the method should check for a zero count.

Right
static int average(int total, int count) {
    if (count == 0) {
        return 0;
    }
    return total / count;
}

Check yourself

  1. javac prints: Main.java:12: error: cannot find symbol, then symbol: variable cnt, then location: class Main. Which line do you open, and what do you suspect?

    Try it first, then show the answer

    Open line 12 of Main.java. Java does not know a variable called cnt there. Suspect a typo (perhaps the variable is called count), a variable that was never declared, or a variable declared inside other braces, so that it is out of reach on line 12.

  2. A program stops with this output: Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3, then at Main.last(Main.java:9), then at Main.main(Main.java:15). Which method failed, on which line, and what does the message tell you?

    Try it first, then show the answer

    The method last failed, on line 9 of Main.java. main called it from line 15. The message says that index 3 was used on an array of length 3, whose valid indexes are 0 to 2. Open line 9 and look at the index. It is probably length instead of length - 1, or a loop that uses <= instead of <.

  3. Write the try and catch lines that print bad number instead of stopping the program when Integer.parseInt(text) fails.

    Try it first, then show the answer

    try {
    int n = Integer.parseInt(text);
    System.out.println(n);
    } catch (NumberFormatException e) {
    System.out.println("bad number");
    }

What next

Go on to the Java course (INFO1113) →

Independent study notes, not official course material.