LW Leon Learn

Writing Java on paper

Write Java with a pen: no compiler, no autocomplete, no running. Five short lessons: run a program in your head, draw the stack and heap, memorise the skeletons, catch the slips a compiler would catch, and mark your own work.

What you need first

  • You have compiled and run one small Java program before. If not, read the Java first-steps guide first. This guide starts at the point where no computer is there to help.
  • A pen or pencil, an eraser, and squared or lined paper. Tables are easier on squared paper.
  • About 20 minutes for each lesson. Do the check questions on paper, not in your head.
01

Read like a computer

Goal You can run a short Java program by hand, one line at a time, and write down every value it uses.

When you write code on paper, nothing tells you that a line is wrong. Your only check is to run the program yourself, half in your head and half on the page. This is called a dry run: you act as the computer, with a pen, one line at a time.

A computer has no idea what your program is for. It does exactly what each line says, in order, and it remembers only what is stored in variables. A variable is a named place that holds a value. While you trace, do the same. Do what the line says, not what you meant it to say.

The tool is a trace table. A trace table has one column for each variable and one row for each line that runs. Build it in four steps.

1. Number the lines of the program in the margin, starting at 1.
2. Draw the table. The first column is Line. Then one column for each variable. The last column is Note, where you write what happened, for example "test is true", or what the program printed.
3. Run the program. Execution starts at the first line inside main. Each time a line runs, add a row. Write the line number, then the value of every variable after that line has finished. Copy unchanged values down from the row above, so that every row can be read on its own.
4. Use two marks for a variable with no value. A dash (–) means the variable has not been declared yet. A question mark (?) means it is declared but nothing has been stored in it. Java refuses to compile a program that reads a question-mark variable, so on paper, reading one is a mistake you must catch yourself.

For now, write a String in its column as its text in double quotes, such as "data". The next lesson draws what really happens behind it.

A loop line runs many times, so it gets many rows. For a for loop, add one row each time the computer reaches the test. Write the values after the update part (such as i++), and write the result of the test in Note. The last row of a loop is the visit where the test is false. The body does not run after it. A variable declared inside the for line, such as i, stops existing when the loop ends, so write a dash for it afterwards.

An if line gets one row. If its test is false, the next row is the first line after the closing brace of the if.

If the program calls a method, draw a second small table for that call, with columns for its parameters and its own variables. Number the calls (f#1, f#2). When the method returns, write the returned value in the Note column of the line that made the call.

New words

dry run
Running a program by hand, with a pen, without a computer.
trace table
A grid with one column per variable and one row per line that runs.
variable
A named place that holds one value, such as int count.
test
The yes-or-no question in an if or a for, such as i < 4. Its answer is true or false.
method
A named block of code that you can call, such as length() or charAt(i).

Worked example

Java 8+
public class Main {
    public static void main(String[] args) {
        int count = 0;
        String word = "data";
        for (int i = 0; i < word.length(); i++) {
            if (word.charAt(i) == 'a') {
                count++;
            }
        }
        System.out.println(count);
    }
}
Predict the output first, then show it

Expected output

2

Step by step

Set up first. Number the lines 1 to 12: public class Main { is 1, public static void main is 2, int count = 0; is 3, and the last } is 12. The page does not print numbers, so count them yourself. Draw five columns: LinewordicountNote. The computer starts at the first line inside main, which is line 3.
Line 3word = –i = –count = 0count is created with 0. word and i do not exist yet.
Line 4word = "data"i = –count = 0word is created and holds the text "data".
Line 5word = "data"i = 0count = 0The for line creates i with 0. word.length() is 4, so the test 0 < 4 is true.
Line 6i = 0count = 0word.charAt(0) is d, not a, so the test is false. The next row is the for line again.
Line 5i = 1count = 0i++ makes i 1. The test 1 < 4 is true.
Line 6i = 1count = 0word.charAt(1) is a, so the test is true. Go into the body.
Line 7i = 1count = 1count++ adds 1 to count.
Line 5i = 2count = 1i++ makes i 2. The test 2 < 4 is true.
Line 6i = 2count = 1word.charAt(2) is t, so the test is false.
Line 5i = 3count = 1i++ makes i 3. The test 3 < 4 is true.
Line 6i = 3count = 1word.charAt(3) is a, so the test is true.
Line 7i = 3count = 2count++ makes count 2.
Line 5i = 4count = 2i++ makes i 4. The test 4 < 4 is false, so the loop ends.
Line 10i = –count = 2System.out.println writes 2 and a new line. i stopped existing when the loop ended, so it gets a dash. The program prints 2.

Common mistakes

Wrong
double half = 7 / 2;

Both 7 and 2 are ints, so Java does integer division first. 7 / 2 is 3, and Java then stores 3 as 3.0. The decimal part is already gone. Trace with the types the program has, not with the numbers you hoped for. Make at least one side a double: 7 / 2.0 is 3.5.

Right
double half = 7 / 2.0;
Wrong
String word = "data";
for (int i = 0; i <= word.length(); i++) {
    System.out.println(word.charAt(i));
}

The test uses <=, so the loop also runs with i equal to 4. But a String of length 4 has positions 0 to 3. word.charAt(4) stops the program with a StringIndexOutOfBoundsException. The last row of your trace table shows it: the position is outside the word. To visit every position, use < with length().

Right
String word = "data";
for (int i = 0; i < word.length(); i++) {
    System.out.println(word.charAt(i));
}
Wrong
System.out.println('a' + 1);

A char takes part in arithmetic as its number code. 'a' is 97, so 'a' + 1 is the int 98, and 98 is what is printed. To get the next letter, cast the sum back to char.

Right
System.out.println((char) ('a' + 1));
Wrong
System.out.println("sum: " + 1 + 2);

Java reads + from left to right. "sum: " + 1 is the text sum: 1, and then + 2 appends another text. The program prints sum: 12. Put the addition in round brackets so it happens first: "sum: " + (1 + 2) prints sum: 3.

Right
System.out.println("sum: " + (1 + 2));

Check yourself

  1. Make a trace table for these four lines, numbered 1 to 4: int a = 6; int b = 4; a = a % b; b = b + a; What are a and b at the end?

    Try it first, then show the answer

    Line 1: a = 6, b = –. Line 2: a = 6, b = 4. Line 3: a = 2, b = 4, because 6 % 4 is 2. Line 4: a = 2, b = 6. At the end a is 2 and b is 6. Line 4 uses the new value of a.

  2. Predict, without running: what does for (int i = 1; i < 10; i = i * 3) System.out.print(i + " "); print?

    Try it first, then show the answer

    It prints 1 3 9, each followed by a space. After 9 the update makes i 27, the test 27 < 10 is false, and the loop ends. The step does not have to be +1.

  3. In the worked program, what value goes in the i column on the row for line 10, and why?

    Try it first, then show the answer

    A dash. i was declared in the for line, so it exists only while the loop runs. After the loop it is gone, and the code on line 10 could not use it.

02

Draw memory

Goal You can draw the stack and the heap for a Java program at any line, with one fixed convention, and say which objects have become unreachable.

Java programs work with objects, and several variables can lead to the same object. You cannot see that in the code. You have to draw it. Use the same picture every time.

Java keeps values in two places. Draw two columns, each with a heading: Stack and Heap.

The stack holds one frame for each method that is running right now. A frame is a box that holds that call's parameters and local variables. Draw main's frame first, at the top of the Stack column. Each call adds a new frame below it. When a method returns, cross its frame out. Everything inside it is gone.

The heap holds every object and every array. They are made with new. You never free them: Java does that for you.

Now the rules for drawing things.

1. A variable of a primitive type (int, double, boolean, char) is a box with its value inside, such as int n and 5.
2. A variable of any other type holds a reference. A reference is an arrow to an object, or the value null. Draw an arrow, and also write the target's label inside the box, so you can still read it when arrows cross.
3. Give every object a label: H1, H2, H3, in the order new is run. Write the label and the class on top, such as H1 (Box). Draw the fields of the object as boxes inside it, with their names. Draw an array as H2 (int[3]) with cells [0], [1], [2]. The cells of a new array start at 0 (numbers), false, or null (references). They are never ?.
4. An array of objects is an array of references. new Box[2] makes one array whose two cells hold null. It does not make any Box. Each Box needs its own new.
5. A String is an object. Draw it in the heap with its text, such as H3 (String) "hi". Two literals with the same text are the same object. new String("hi") makes a different one.
6. null is a reference that points at nothing. Draw a slash through the box and write null. Calling a method through null stops the program with a NullPointerException.

What = does. a = b copies what is in b's box into a's box. For a primitive, that is the number. For a reference, that is the arrow. After Box c = a; both c and a lead to the same object, and the object is not copied. == between references asks whether the two arrows end at the same object. It does not look inside. To compare the text of two Strings, use equals.

What a method call does. It copies each argument into a new parameter box in the new frame. Java always copies. For a reference, the copy is an arrow. So the method can change the object through the arrow, but it cannot make the caller's variable point somewhere else.

What becomes unreachable. An object is reachable if you can get to it by following arrows from a variable in a frame that still exists. An object that no arrow leads to is unreachable. The garbage collector, the part of Java that reclaims memory, may remove it at some moment. On paper, cross the object out at the moment it becomes unreachable: when the last arrow to it is changed, or when the frame that held the last arrow is crossed out.

New words

stack and frame
The stack holds one frame for each running method. A frame holds that call's parameters and local variables and is gone when the call returns.
heap
Where every object and array lives. You make them with new, and Java removes them for you.
reference
The content of a variable of an object type: an arrow to an object, or null.
null
A reference that points at nothing. Calling a method through it is an error.
label
A name such as H1 that you give to an object in your drawing.
unreachable
An object that no arrow from a live frame leads to. The garbage collector may remove it.
garbage collector
The part of Java that finds unreachable objects and reclaims their memory.

Worked example

Java 8+
public class Main {
    static class Box {
        int value;

        Box(int value) {
            this.value = value;
        }
    }

    static void bump(Box b, int n) {
        b.value = b.value + n;
        n = 0;
        b = null;
        // A: draw the memory here, inside bump
    }

    public static void main(String[] args) {
        int n = 5;
        Box a = new Box(1);
        Box c = a;
        Box[] row = new Box[2];
        row[0] = new Box(7);
        bump(a, n);
        System.out.println(c.value + " " + n + " " + row[0].value + " " + row[1]);
        a = new Box(40);
        c = null;
        // B: draw the memory here
        System.out.println(a.value);
    }
}
Predict the output first, then show it

Expected output

6 5 7 null
40

Step by step

  1. Draw two columns with headings: Stack and Heap. Leave out the args parameter of main, since this program does not use it. Draw main’s frame at the top of Stack.

  2. int n = 5; Draw a box int n holding 5. It is a primitive, so the value sits inside the box.

  3. Box a = new Box(1); new makes an object in the heap: H1 (Box) with one field box, value. The constructor runs and sets value to 1. The variable a holds an arrow to H1; write H1 in its box.

  4. Box c = a; copies the arrow. c holds H1 too. There is still one Box.

  5. Box[] row = new Box[2]; makes one array object, H2 (Box[2]), with cells [0] and [1] both holding null. No Box is made. row holds an arrow to H2.

  6. row[0] = new Box(7); makes a second Box, H3 (Box) with value 7, and puts an arrow to H3 into cell [0] of H2. Cell [1] still holds null.

  7. bump(a, n); Draw a new frame below main’s. Its parameter b is a copy of the arrow in a, so b holds H1. Its parameter n is a copy of 5, a new box with its own 5. Then b.value = b.value + n follows the arrow: H1.value becomes 1 + 5 = 6. n = 0 changes only bump’s own n. b = null puts null into bump’s own b; main’s a still holds H1. At marker A: bump has b = null and n = 0, and H1 holds 6. Then bump returns: cross out its frame.

  8. The println reads c.value (c leads to H1, which holds 6), n (main’s n is still 5), row[0].value (7) and row[1] (null). It prints 6 5 7 null.

  9. a = new Box(40); makes H4 (Box) with value 40, and the arrow in a moves to H4. H1 is still reachable, because c leads to it. Then c = null; puts a slash through c. Now no arrow leads to H1: it is unreachable, so cross it out. At marker B: main has n = 5, a to H4, c = null, and row to H2. H2 has [0] to H3 and [1] null. H3 holds 7, H4 holds 40, and H1 (holding 6) is crossed out.

  10. The last println prints 40.

Common mistakes

Wrong
String a = new String("hi");
String b = new String("hi");
boolean same = (a == b);

Each new String("hi") makes its own object, so a and b hold arrows to two different objects, H1 and H2. == compares the arrows, so same is false even though both objects say hi. To compare the text, use a.equals(b).

Right
String a = new String("hi");
String b = new String("hi");
boolean same = a.equals(b);
Wrong
int[] a = {1, 2, 3};
int[] b = a;
b[0] = 9;

int[] b = a; copies the arrow, not the array. There is one array H1 and two arrows into it, so b[0] = 9 also changes a[0]. To get a separate array, make a new object: b = a.clone() draws a second array H2 with its own cells.

Right
int[] a = {1, 2, 3};
int[] b = a.clone();
b[0] = 9;
Wrong
Box[] row = new Box[2];
row[0].value = 3;

new Box[2] only makes the array. Its cells hold null, and null has no value field. Following the arrow from row[0] stops the program with a NullPointerException. Make the Box first.

Right
Box[] row = new Box[2];
row[0] = new Box(0);
row[0].value = 3;
Wrong
static void reset(Box b) {
    b = new Box(0);
}

The parameter b is a copy of the caller’s arrow. b = new Box(0) points the copy at a new object H2 and leaves the caller’s arrow alone. When reset returns, nothing has changed for the caller. Go through the arrow to change the object that the caller sees.

Right
static void reset(Box b) {
    b.value = 0;
}

Check yourself

  1. Draw it: int[] a = {1, 2}; int[] b = a; b[0] = 7; a = new int[]{5}; What does each variable lead to? Is the first array unreachable?

    Try it first, then show the answer

    The first array is H1 (int[2]). After int[] b = a, both a and b lead to H1. b[0] = 7 makes H1 hold 7 and 2. Then new int[]{5} makes H2 (int[1]) holding 5, and a moves to H2. b still leads to H1, so H1 is reachable and not crossed out. Now a[0] is 5 and b[0] is 7.

  2. Draw it: String s = null; and then the call s.length(). What does the drawing show, and what happens?

    Try it first, then show the answer

    The box s holds null: a slash and no arrow. The call must follow an arrow to an object, and there is none. The program stops with a NullPointerException.

  3. Draw it: Box x = new Box(1); Box y = new Box(1); Box z = x; Which of x == y and x == z are true? (Box has no equals method of its own.)

    Try it first, then show the answer

    There are two objects, H1 and H2, both holding 1. x and z lead to H1, and y leads to H2. x == y is false: the arrows end at different objects, even though the contents match. x == z is true: the arrows end at the same object.

03

Syntax to memorise

Goal You can write the eleven Java skeletons below from memory, shortest first, with every bracket and semicolon in place.

Some code cannot be worked out on the spot. You must know its shape without looking. The eleven shapes in this lesson are the ones Java needs again and again. They are short enough to learn by heart. Learn them in this order, shortest first, and add the next one only when the last is solid.

1. A class. 2. main. 3. A constructor. 4. for-each. 5. length, length() and size(). 6. String methods. 7. ArrayList. 8. HashMap. 9. try and catch. 10. Generics. 11. interface and extends.

How to memorise: cover the page and write the shape on paper. Compare it with the page, character by character. Tick it only when it is right with no peeking. Do it again tomorrow. A shape that you write correctly three days in a row is learned.

The program below contains all eleven shapes, each marked with a comment such as (4). Read it once. Then cover it and write each shape on its own, in a few lines of your own paper.

Three spellings of "how many". An array has a field, so there are no brackets: nums.length. A String has a method, so there are brackets: word.length(). An ArrayList or a HashMap has a method called size(): names.size(). A way to remember: only the plain array has no brackets.

String methods. Positions start at 0, and a String never changes: every method gives you a new value. length() is the number of characters. charAt(i) is one char. substring(from, to) takes the characters from position from up to, but not including, position to. indexOf("x") is the position of the first x, or -1 when there is none. equals(other) compares the text. toUpperCase() gives a new String in capitals. contains("x") says whether x occurs.

ArrayList: ArrayList<String> names = new ArrayList<>(); with add(x), get(i), set(i, x), remove(i), size(), contains(x). HashMap: HashMap<String, Integer> ages = new HashMap<>(); with put(key, value), get(key), containsKey(key), remove(key), size(). The types inside the angle brackets must be classes: Integer, not int. Both need an import line at the top.

New words

skeleton
The fixed shape of a piece of code, with the details left to fill in.
constructor
A special method with the class's name and no return type. It runs when new makes an object.
generic
A class or method with a type parameter such as T, which is filled in when you use it.
exception
An error that stops normal flow. A catch block can handle it.
interface
A list of method names with no bodies. A class that implements it must supply them.

Worked example

Java 8+
import java.util.ArrayList;
import java.util.HashMap;

/* (11) interface and extends */
interface Shape {
    double area();
}

class Rect implements Shape {
    private double width;
    private double height;

    /* (3) constructor */
    Rect(double width, double height) {
        this.width = width;
        this.height = height;
    }

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

class Square extends Rect {
    Square(double side) {
        super(side, side);
    }
}

/* (10) generics */
class Box<T> {
    private T item;

    Box(T item) {
        this.item = item;
    }

    T get() {
        return item;
    }
}

/* (1) a class */
public class Main {
    static <T extends Comparable<T>> T larger(T a, T b) {
        if (a.compareTo(b) >= 0) {
            return a;
        }
        return b;
    }

    /* (2) main */
    public static void main(String[] args) {
        /* (7) ArrayList */
        ArrayList<String> names = new ArrayList<>();
        names.add("Leon");
        names.add("Mia");
        names.add("Emma");

        /* (8) HashMap */
        HashMap<String, Integer> ages = new HashMap<>();
        ages.put("Leon", 20);
        ages.put("Mia", 22);

        /* (4) for-each */
        for (String name : names) {
            if (ages.containsKey(name)) {
                System.out.println(name + " is " + ages.get(name));
            } else {
                System.out.println(name + " has no age");
            }
        }

        /* (5) length, length() and size() */
        int[] nums = new int[3];
        String word = "banana";
        System.out.println(nums.length + " " + word.length() + " " + names.size());

        /* (6) String methods */
        System.out.println(word.substring(1, 3) + " " + word.indexOf("n") + " " + word.charAt(0) + " " + word.toUpperCase() + " " + word.equals("banana"));

        Shape s = new Square(3);
        System.out.println(s.area());
        System.out.println(larger("pear", "apple"));
        Box<Integer> box = new Box<>(7);
        System.out.println(box.get());

        /* (9) try and catch */
        try {
            int x = Integer.parseInt("12a");
            System.out.println(x);
        } catch (NumberFormatException e) {
            System.out.println("not a number");
        }
    }
}
Predict the output first, then show it

Expected output

Leon is 20
Mia is 22
Emma has no age
3 6 3
an 2 b BANANA true
9.0
pear
7
not a number

Step by step

  1. (1) A class. public class Name { ... } The word public, then class, then the name, then braces. The name must match the file name: Main goes in Main.java. A file has at most one public class. The other classes (Shape, Rect, Square, Box) have no public in front, which is fine.

  2. (2) main. public static void main(String[] args) { ... } Write it exactly, word for word. public so the system can call it, static so no object is needed, void because it returns nothing, and String[] args for the command-line words.

  3. (3) A constructor. ClassName(type param) { this.field = param; } It has the same name as the class and no return type, not even void. this.width = width; stores the parameter in the field of the object being built. The parameter and the field may share a name; this. picks the field.

  4. (4) for-each. for (Type item : collection) { ... } The type of one item, a colon, then the array or collection. Here: for (String name : names). Read the colon as "in".

  5. (5) length, length() and size(). nums.length is 3 (an array, no brackets). word.length() is 6 (a String, brackets). names.size() is 3 (an ArrayList, size with brackets). The line prints 3 6 3.

  6. (6) String methods. word.substring(1, 3) is an (positions 1 and 2). word.indexOf("n") is 2. word.charAt(0) is b. word.toUpperCase() is BANANA. word.equals("banana") is true. The line prints an 2 b BANANA true.

  7. (7) ArrayList. import java.util.ArrayList; at the top. ArrayList<String> names = new ArrayList<>(); then names.add("Leon"). The type goes on both sides of the equals sign, but the diamond <> on the right may stay empty.

  8. (8) HashMap. import java.util.HashMap; at the top. HashMap<String, Integer> ages = new HashMap<>(); then ages.put("Leon", 20). Two types, key first and value second. ages.get("Emma") would give null, because Emma was never put in; that is why the program tests containsKey first.

  9. (9) try and catch. try { risky code } catch (ExceptionType e) { what to do }. Integer.parseInt("12a") cannot read the text as a number, so it throws a NumberFormatException, the rest of the try block is skipped, and the catch block runs. The program prints not a number.

  10. (10) Generics. A class with a type parameter: class Box<T> { private T item; ... T get() ... } T stands for any class, and Box<Integer> fills it in. A method with a bound: static <T extends Comparable<T>> T larger(T a, T b). The part in angle brackets comes before the return type. The program prints 7 and pear.

  11. (11) interface and extends. interface Shape { double area(); } lists methods with no body. class Rect implements Shape must supply area() and mark it public. class Square extends Rect inherits the methods of Rect, and its constructor starts with super(side, side); to run Rect’s constructor. Shape s = new Square(3); is allowed, and s.area() prints 9.0.

Common mistakes

Wrong
int a = nums.length();
int b = word.length;
int c = names.length();

Each line uses the wrong one of the three spellings. An array has the field length, with no brackets. A String has the method length(), with brackets. An ArrayList has the method size(). None of these three names works on the other types.

Right
int a = nums.length;
int b = word.length();
int c = names.size();
Wrong
String word = "banana";
String part = word.substring(1, 3);

substring(1, 3) stops before position 3, so it gives two characters, an, and not three. If you want three characters starting at position 1, the second number is 1 + 3 = 4. Count the second number as the position where you stop.

Right
String word = "banana";
String part = word.substring(1, 4);
Wrong
for (String name in names) {
    System.out.println(name);
}

Java has no in. The colon is the separator, and the type of the item is not optional. Write the three parts in order: type, name, colon, collection.

Right
for (String name : names) {
    System.out.println(name);
}
Wrong
class Rect {
    private double width;

    public void Rect(double width) {
        this.width = width;
    }
}

A constructor has no return type at all. With void in front, this is an ordinary method that happens to be called Rect, and the class has no constructor with a parameter. A call such as new Rect(2.0) then fails to compile. Write the name straight after any access word.

Right
class Rect {
    private double width;

    public Rect(double width) {
        this.width = width;
    }
}

Check yourself

  1. On paper, write from memory a class Counter with one int field count, a constructor that sets it to 0, and a method increment() that adds 1. Then compare it with the skeletons.

    Try it first, then show the answer

    public class Counter { private int count; public Counter() { count = 0; } public void increment() { count++; } } Check: no return type on the constructor; void on increment; braces match; a semicolon after count++ and after the field.

  2. Write the three lines that declare an ArrayList of Strings called items, add "pen" to it, and print how many items it holds. Do not forget the line at the top of the file.

    Try it first, then show the answer

    import java.util.ArrayList; at the top, then ArrayList<String> items = new ArrayList<>(); items.add("pen"); System.out.println(items.size()); It prints 1.

  3. Write a try and catch that turns the String text into an int called n, and sets n to -1 when text is not a number.

    Try it first, then show the answer

    int n; try { n = Integer.parseInt(text); } catch (NumberFormatException e) { n = -1; } The compiler is satisfied because n gets a value on both paths.

04

Common handwriting mistakes

Goal You can read your own Java answer a second time, as the compiler would, and catch the slips that would otherwise cost marks.

On a computer, the compiler stops you at the first error and tells you the line. On paper, nobody stops you, and every one of these slips costs marks even though it has nothing to do with whether you understand the problem. So you must be your own compiler.

When your answer is finished, read it a second time. This time do not ask whether the logic is right. Ask only whether the form is right. Use this list, and tick each item with your pen.

1. Every statement ends with a semicolon. There is none after a closing brace of a class, method, if or for.
2. Every ( [ { has its partner. Count the braces in pairs.
3. Every variable has a type before its name. String starts with a capital S.
4. Every class from java.util has an import line.
5. = stores a value. == asks a question about numbers and chars. For Strings, use equals.
6. Strings use double quotes. A single char uses single quotes.
7. A method that is not void returns a value on every path.
8. Putting a double into an int needs a cast.
9. Collections hold classes: Integer, not int.
10. A local variable has a value before you read it.
11. A method that implements an interface method is marked public.
12. The class name matches the file name, and main is written exactly.

Below are four pairs that cost the most marks. In each pair the first block is what people write, and the second block is the repair.

Common mistakes

Wrong
ArrayList<int> nums = new ArrayList<>();
nums.add(5);
int first = nums[0];

Two slips. The type inside the angle brackets must be a class, so int becomes Integer. And an ArrayList is not an array: square brackets do not work on it, so the first item is nums.get(0).

Right
ArrayList<Integer> nums = new ArrayList<>();
nums.add(5);
int first = nums.get(0);
Wrong
class Rect implements Shape {
    double area() {
        return 1.0;
    }
}

The methods of an interface are public automatically. A method that implements one cannot be less open, so it must also say public. Without it the compiler says it is attempting to assign weaker access privileges.

Right
class Rect implements Shape {
    public double area() {
        return 1.0;
    }
}
Wrong
double x = 7 / 2.0;
int half = x;
String s = 'hi';

Two slips. Java will not put a double into an int by itself, because the fraction would be lost; write a cast, (int) x, if you want that. And single quotes are for one char. A String needs double quotes.

Right
double x = 7 / 2.0;
int half = (int) x;
String s = "hi";
Wrong
static int sign(int x) {
    if (x > 0) {
        return 1;
    } else if (x < 0) {
        return -1;
    }
}

static int bump() {
    int n;
    n++;
    return n;
}

Two slips. In sign, the compiler cannot see that x could be 0, and then nothing is returned, so it reports a missing return statement. Add a return at the end. In bump, n has no value when n++ reads it, so Java reports that the variable might not have been initialized. Give it a value where you declare it.

Right
static int sign(int x) {
    if (x > 0) {
        return 1;
    } else if (x < 0) {
        return -1;
    }
    return 0;
}

static int bump() {
    int n = 0;
    n++;
    return n;
}

Check yourself

  1. Fix every slip (there are four): string name = 'Leon'; if (name == "Leon") { System.out.println(name) }

    Try it first, then show the answer

    1. The type is String, with a capital S. 2. Leon is a String, so it needs double quotes: "Leon". 3. Compare text with name.equals("Leon"), not ==. 4. System.out.println(name) needs a semicolon. Correct: String name = "Leon"; if (name.equals("Leon")) { System.out.println(name); }

  2. Fix the collection: ArrayList<int> xs = new ArrayList<int>(); xs.add(1); System.out.println(xs.length);

    Try it first, then show the answer

    Use a class inside the angle brackets, and ask for the count with size(): ArrayList<Integer> xs = new ArrayList<>(); xs.add(1); System.out.println(xs.size()); Also write import java.util.ArrayList; at the top of the file.

  3. Fix the class: public class Dog { public void Dog(String name) { this.name = name; } }

    Try it first, then show the answer

    Two slips. The constructor must not have void, so it is public Dog(String name). And the class has no field name, so add private String name; inside the braces, above the constructor.

05

Mark yourself

Goal You can mark your own paper answer honestly against a rubric, find where the marks went, plan tomorrow's practice, and write under time.

On paper, a page of writing can look finished and still be wrong. Marking yourself fixes that. You compare your answer with a rubric. A rubric is a list of lines, each worth a number of marks, each describing something that you can see in the answer. Be strict. The mark that counts is the one a stranger would give.

How to read a rubric, line by line.

1. Read one line. Ask: what exactly would I see in a correct answer?
2. Find that thing in your answer and touch it with the tip of your pen. If you can point to it, tick the line and write its marks. If you cannot point to it, the line is worth zero.
3. Do not give half marks for "nearly". Give part of a line only if the rubric says how it splits.
4. Check every path. A line such as "handles the empty list" is zero if only the normal case works.
5. Add up the lines. Write the total as marks out of the maximum on the page.

How to find where the marks went. Put each lost mark into one of four boxes. Idea: I did not know how to solve it. Recall: I knew, but could not remember the syntax or the method name. Slip: a small mistake in something I did know, such as a missing semicolon or a wrong sign. Time: I ran out of time. Count each box. The biggest box tells you what to work on. Idea: reread the lesson. Recall: more cover-and-write practice. Slip: the compiler pass from the last lesson and a tiny test. Time: practise planning.

What to do the next day. First, rewrite the same answer on a blank page without looking. Second, do a different question of the same kind. Third, mark both again. Repeat after three days and after a week. When the biggest box changes, you are improving.

How to write under time.

1. Plan first. Use the first tenth of the time. Write the method header and three or four comment lines that name the steps. Do not skip this.
2. Leave a blank line between lines of code. You will need room to insert a line you forgot.
3. Name variables clearly: count, not c; longest, not l. Clear names stop you from mixing up two variables.
4. Test with a tiny input, by hand. When the code is written, run it on the smallest case: one element, an empty list, a word of length 1. Then run one case that sits exactly on a boundary. This takes a minute and finds most boundary errors.
5. Spend about one minute per mark. If you are stuck on a part, write a comment saying what you would do, move on, and come back.
6. Leave two minutes for the compiler pass.

On the Practice page, the questions of kind Write code have a Paper mode with a timer, a model answer and a rubric with tick boxes. Use it exactly like this lesson: write on paper, then mark.

New words

rubric
A list of lines, each worth some marks, each describing something you can point to in the answer.
boundary case
A test input that sits exactly on an edge, such as a length equal to min, one element, or an empty list.
compiler pass
A second reading of your answer that checks only the form: semicolons, brackets, types, imports.

Worked example

Java 8+
import java.util.ArrayList;

public class Main {
    /* A student's answer. It has one bug. */
    static int countLong(ArrayList<String> words, int min) {
        int count = 0;
        for (String w : words) {
            if (w.length() > min) {
                count++;
            }
        }
        return count;
    }

    public static void main(String[] args) {
        ArrayList<String> words = new ArrayList<>();
        words.add("a");
        words.add("bb");
        words.add("cc");
        words.add("ddd");
        System.out.println(countLong(words, 2) + " (expected 3)");
    }
}
Predict the output first, then show it

Expected output

1 (expected 3)

Step by step

  1. The task: write static int countLong(ArrayList<String> words, int min), which returns how many words have at least min characters. The rubric has four lines, one mark each. (1) The header is right: static, returns int, takes the list and the number. (2) A counter starts at 0. (3) Every word is visited, and its length is read with length(). (4) The test means "at least min", and the counter is returned.

  2. Mark line 1. static int countLong(ArrayList<String> words, int min) is right. Put the pen on it. +1.

  3. Mark line 2. int count = 0; is there. +1.

  4. Mark line 3. for (String w : words) visits every word, and w.length() reads the length. +1.

  5. Mark line 4. The test is w.length() > min. That means more than min, not at least min. The pen has nothing to point at for "at least". The counter is returned, but the line asks for both. 0 marks.

  6. Score: 3 out of 4. Now the tiny test on the boundary: min is 2 and one word has exactly 2 characters. bb and cc have length 2 and are not counted, so the method returns 1 instead of 3. The program prints 1 (expected 3). The lost mark goes in the Slip box: the idea was right, the sign was wrong. The fix is >=.

Common mistakes

Wrong
for (int i = 0; i <= words.size(); i++) {
    System.out.println(words.get(i));
}

The test i <= words.size() lets i reach words.size(), but the last valid position is words.size() - 1. The call words.get(i) then stops the program with an IndexOutOfBoundsException. A tiny test with one word finds it: size is 1, and position 1 does not exist.

Right
for (int i = 0; i < words.size(); i++) {
    System.out.println(words.get(i));
}
Wrong
static int total(ArrayList<Integer> xs) {
    for (int x : xs) {
        int sum = 0;
        sum = sum + x;
    }
    return sum;
}

sum is declared inside the loop body, so it is created again on every round and is gone after the loop. The return line cannot see it, and the compiler says it cannot find the symbol. Declare the counter before the loop. Naming the plan first (counter, loop, return) would have placed it correctly.

Right
static int total(ArrayList<Integer> xs) {
    int sum = 0;
    for (int x : xs) {
        sum = sum + x;
    }
    return sum;
}
Wrong
static void total(ArrayList<Integer> xs) {
    int sum = 0;
    for (int x : xs) {
        sum = sum + x;
    }
    System.out.println(sum);
}

The task said the method returns the total, but this one prints it and returns nothing. The caller gets no value to use. This is a reading slip: before you write, find the words returns, prints and reads in the task and underline them.

Right
static int total(ArrayList<Integer> xs) {
    int sum = 0;
    for (int x : xs) {
        sum = sum + x;
    }
    return sum;
}

Check yourself

  1. A rubric line says "uses equals to compare the Strings". You wrote ==, and it worked when you traced it with two literals. How many marks does that line get, and why?

    Try it first, then show the answer

    Zero. The line asks for equals, and the pen has nothing to point at. == compares arrows. It worked only because two literals with the same text are the same object. With new String("hi") it would fail.

  2. You have 12 minutes for a 12-mark task: write a class with a constructor and one method, and a main that tests it. Write a plan with times.

    Try it first, then show the answer

    1 minute: write the class header, the field, and the two method headers as comments. 4 minutes: write the class. 2 minutes: write main with one tiny test. 3 minutes: run the tiny test by hand with a trace table. 2 minutes: the compiler pass.

  3. Write the tiny tests that you would run by hand on a method that returns the longest word in an ArrayList<String>.

    Try it first, then show the answer

    An empty list (decide what to return and check that the code does it), a list with one word, two words of equal length (which one wins?), and a list where the longest word is last. Run each through your code with a trace table.

What next

Practise on paper: open the Write code questions for INFO1113, set a timer, and mark yourself against the rubric. →

Independent study notes, not official course material.