University College of Applied SciencesProgramming 1
العربية
Lecture 16 · 240 min

Final Project and Review

Bring everything you learned into a real program, then test yourself

By the end of this lecture you will be able to

  1. Split a program into several files inside one package
  2. Build a grades manager with a menu, a loop, arrays and methods, one stage at a time
  3. Move your project from the Java Lab to NetBeans through a ZIP file
  4. Avoid the classic traps met in each lecture
  5. Solve a mock practical exam within a time limit
In this lecture
  1. You will build a program that manages a whole class's marks, using everything you learned
  2. Each file has one job, and the grades package holds them together
  3. The Java Lab keeps your project and hands it over as a ZIP to open in NetBeans
  4. Stage 1: Stats calculates the average, the highest and the lowest mark
  5. Stage 2: Report prints a table with straight columns
  6. Stage 3: a do-while loop and a switch make the menu
  7. Stage 5: the pieces come together in a complete program
  8. Printing, variables and input: lectures 1 to 6
  9. Conditions and decisions: lectures 7 and 8
  10. Loops: lectures 9 and 10
  11. Methods: lectures 11 and 12
  12. Arrays and strings: lectures 13 to 15
  13. The exam: 120 minutes and six questions that get harder
  14. Questions 3 and 4: arrays and methods, then strings
  15. Questions 5 and 6: a 2D array, then a program combining everything
  16. Last review questions, and see you in Programming 2
Part one: the grades manager project

You will build a program that manages a whole class's marks, using everything you learned

IdeaWhere it appears in the project
Scanner and nextLineReading names and marks
do-while and switchThe menu that repeats until exit
while for input validationRejecting a mark outside 0 to 100
Arrays and 2D arraysString[] for names and int[][] for marks
Methods and parametersEach task in a method, each group of methods in a class
printf and equalsIgnoreCaseA formatted table and a search by name

Each file has one job, and the grades package holds them together

FileIts jobIts methods
grades/GradeBook.javamain, the menu and reading from the usermain, addStudent, readMark, findStudent
grades/Stats.javaCalculation only, no printingaverage, max, min
grades/Report.javaFormatted printing onlyprintTable, printSummary
  • The first line of every file is package grades;, and the file sits in a folder of the same name.
  • These classes only group static methods; we create no objects from them.
  • Inside the same package no import is needed: we just write Stats.average(row).

The start: two files working together

Switch between the two files at the top of the editor. Press "Debug line by line" and step into Stats.average to see execution move to another file and come back. Change the marks and predict the result.

grades/GradeBook.java
package grades;

public class GradeBook {
    public static void main(String[] args) {
        int[] marks = {70, 85, 90};
        System.out.printf("Average: %.2f%n", Stats.average(marks));
        System.out.println("Best: " + Stats.max(marks));
        System.out.println("Lowest: " + Stats.min(marks));
    }
}
grades/Stats.java
package grades;

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

    public static int max(int[] values) {
        int best = values[0];
        for (int value : values) {
            if (value > best) {
                best = value;
            }
        }
        return best;
    }

    public static int min(int[] values) {
        int worst = values[0];
        for (int value : values) {
            if (value < worst) {
                worst = value;
            }
        }
        return worst;
    }
}
Output
Average: 81.67
Best: 90
Lowest: 70

Why does the Stats class print nothing and only return values?

The Java Lab keeps your project and hands it over as a ZIP to open in NetBeans

The Java Lab at the top of the site holds a full project with several files and packages, with nothing to install.

  1. Open "Java Lab" from the top of the siteFrom the "+ New project…" menu choose "Empty program", then press "Rename" and call it Grades.
  2. Add the files with the + button above the editorType the name with its folder: grades/GradeBook.java, then grades/Stats.java and grades/Report.java. The lab adds the package line itself. Delete Main.java with the × button.
  3. Write the code, or copy it from the labs on this pageEverything saves in this browser automatically. Press Ctrl+Enter to run.
  4. Press "Download ZIP"Then extract it into a new folder, such as GradesSource. You should find the grades folder inside it.
  5. Open it in NetBeans 8.2Choose File, New Project, Java, then Java Project with Existing Sources. Name the project, and at Source Package Folders press Add Folder and choose GradesSource, not grades.
  6. Run it with F6If NetBeans asks for the main class, choose grades.GradeBook.

Stage 1: Stats calculates the average, the highest and the lowest mark

Stage 1: complete Stats

main is ready: it reads how many marks there are, then the marks. Complete the three methods in Stats, without printing anything from them.

import java.util.Scanner;

class Stats {
    static double average(int[] values) {
        return 0; // Replace this
    }

    static int max(int[] values) {
        return 0; // Replace this
    }

    static int min(int[] values) {
        return 0; // Replace this
    }
}

public class StageStats {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        int[] marks = new int[n];
        for (int i = 0; i < n; i++) {
            marks[i] = input.nextInt();
        }
        System.out.printf("Average: %.2f%n", Stats.average(marks));
        System.out.println("Max: " + Stats.max(marks));
        System.out.println("Min: " + Stats.min(marks));
    }
}
stdin
3
70 85 90
Expected output
Average: 81.67
Max: 90
Min: 70
Hint 1

Dividing sum / values.length between whole numbers drops the decimal. Use (double) sum.

Hint 2

Start max and min from values[0], not 0: why does min fail if it starts from zero?

Solution
import java.util.Scanner;

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

    static int max(int[] values) {
        int best = values[0];
        for (int value : values) {
            if (value > best) {
                best = value;
            }
        }
        return best;
    }

    static int min(int[] values) {
        int worst = values[0];
        for (int value : values) {
            if (value < worst) {
                worst = value;
            }
        }
        return worst;
    }
}

public class StageStats {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        int[] marks = new int[n];
        for (int i = 0; i < n; i++) {
            marks[i] = input.nextInt();
        }
        System.out.printf("Average: %.2f%n", Stats.average(marks));
        System.out.println("Max: " + Stats.max(marks));
        System.out.println("Min: " + Stats.min(marks));
    }
}

Stage 2: Report prints a table with straight columns

The marks live in a 2D array: each row is a student and each column an exam. marks[i] is a whole row of type int[], so we pass it straight to Stats.average.

Three files and a table

Open Report.java and change %-12s to %12s: what happens to the names column? Then add a third student in GradeBook.java.

grades/GradeBook.java
package grades;

public class GradeBook {
    public static void main(String[] args) {
        String[] names = {"Sara Ali", "Omar"};
        int[][] marks = {
            {80, 90, 100},
            {55, 60, 70}
        };
        Report.printTable(names, marks, 2);
        Report.printSummary(marks, 2);
    }
}
grades/Stats.java
package grades;

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

    public static int max(int[] values) {
        int best = values[0];
        for (int value : values) {
            if (value > best) {
                best = value;
            }
        }
        return best;
    }

    public static int min(int[] values) {
        int worst = values[0];
        for (int value : values) {
            if (value < worst) {
                worst = value;
            }
        }
        return worst;
    }
}
grades/Report.java
package grades;

public class Report {
    public static void printTable(String[] names, int[][] marks, int count) {
        if (count == 0) {
            System.out.println("No students yet.");
            return;
        }
        System.out.printf("%-12s%5s%5s%5s%8s%6s%6s%n", "Name", "M1", "M2", "M3", "Avg", "Best", "Low");
        for (int i = 0; i < count; i++) {
            int[] row = marks[i];
            System.out.printf("%-12s%5d%5d%5d%8.2f%6d%6d%n", names[i], row[0], row[1], row[2],
                    Stats.average(row), Stats.max(row), Stats.min(row));
        }
    }

    public static void printSummary(int[][] marks, int count) {
        if (count == 0) {
            System.out.println("No students yet.");
            return;
        }
        double total = 0;
        for (int i = 0; i < count; i++) {
            total += Stats.average(marks[i]);
        }
        System.out.printf("Students: %d%n", count);
        System.out.printf("Class average: %.2f%n", total / count);
    }
}
Output
Name           M1   M2   M3     Avg  Best   Low
Sara Ali       80   90  100   90.00   100    80
Omar           55   60   70   61.67    70    55
Students: 2
Class average: 75.83

Stage 2: complete Report

main reads the number of students, then for each one a one-word name and three marks. Complete printRow to print a line in the format %-10s%5d%5d%5d%8.2f%n ending in the average, and printPassed to print Passed: with how many students average 60 or more.

import java.util.Scanner;

class Report {
    static void printRow(String name, int[] row) {
        // printf with %-10s%5d%5d%5d%8.2f%n
    }

    static void printPassed(int[][] marks) {
        // Count the rows whose average is 60 or more
    }
}

public class StageReport {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        int[][] marks = new int[n][3];
        for (int i = 0; i < n; i++) {
            String name = input.next();
            for (int k = 0; k < 3; k++) {
                marks[i][k] = input.nextInt();
            }
            Report.printRow(name, marks[i]);
        }
        Report.printPassed(marks);
    }
}
stdin
2
Sara 80 90 100
Omar 55 60 70
Expected output
Sara         80   90  100   90.00
Omar         55   60   70   61.67
Passed: 2
Hint 1

The average is (row[0] + row[1] + row[2]) / 3.0; dividing by 3.0 keeps the decimal.

Hint 2

marks[i] is a whole row, so the loop in printPassed just needs marks.length.

Solution
import java.util.Scanner;

class Report {
    static double average(int[] row) {
        return (row[0] + row[1] + row[2]) / 3.0;
    }

    static void printRow(String name, int[] row) {
        System.out.printf("%-10s%5d%5d%5d%8.2f%n", name, row[0], row[1], row[2], average(row));
    }

    static void printPassed(int[][] marks) {
        int passed = 0;
        for (int i = 0; i < marks.length; i++) {
            if (average(marks[i]) >= 60) {
                passed++;
            }
        }
        System.out.println("Passed: " + passed);
    }
}

public class StageReport {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        int[][] marks = new int[n][3];
        for (int i = 0; i < n; i++) {
            String name = input.next();
            for (int k = 0; k < 3; k++) {
                marks[i][k] = input.nextInt();
            }
            Report.printRow(name, marks[i]);
        }
        Report.printPassed(marks);
    }
}

Stage 3: a do-while loop and a switch make the menu

The count variable

The array size is fixed, but the number of students grows. We add at position count then increase it, and only visit i < count.

names[count] = name;
count++;
The nextInt trap

nextInt leaves the end of the line behind. If you do not pass it, the next nextLine reads an empty name.

choice = input.nextInt();
input.nextLine();

Stage 3: the menu

Write a loop that reads a choice number on a line, until it is 0. Choice 1: read a name on the next line (it may contain spaces), store it and print Added with the name. Choice 2: print each name on a line as 1. Sara, or No students if there are none. Choice 0: print Goodbye. Any other number: Unknown. Store up to 30 names. Do not print any input prompts.

import java.util.Scanner;

public class StageMenu {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        // A String array, a count, then a do-while loop with a switch inside

    }
}
stdin
1
Sara Ali
1
Omar
2
0
Expected output
Added Sara Ali
Added Omar
1. Sara Ali
2. Omar
Goodbye
Hint 1

Did you forget break in each case? Without it the program also runs the next case.

Hint 2

After nextInt, call nextLine to pass the end of the line, even for choices that read no name.

Solution
import java.util.Scanner;

public class StageMenu {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String[] names = new String[30];
        int count = 0;
        int choice;
        do {
            choice = input.nextInt();
            input.nextLine();
            switch (choice) {
                case 1:
                    names[count] = input.nextLine();
                    System.out.println("Added " + names[count]);
                    count++;
                    break;
                case 2:
                    if (count == 0) {
                        System.out.println("No students");
                    }
                    for (int i = 0; i < count; i++) {
                        System.out.println((i + 1) + ". " + names[i]);
                    }
                    break;
                case 0:
                    System.out.println("Goodbye");
                    break;
                default:
                    System.out.println("Unknown");
            }
        } while (choice != 0);
    }
}

Stage 4: find by name

The first line is the number of students, then for each student a line with the name and a line with the mark, then a line with the name to look for. Write a method find that returns the student's position or -1, ignoring capitals and extra spaces. Print Sara Ali: 80, or Not found.

import java.util.Scanner;

public class StageFind {
    static int find(String[] names, String target) {
        // Return the position of target, or -1
        return -1;
    }

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        // Read the students, read the target, call find, print

    }
}
stdin
2
Sara Ali
80
Omar
75
sara ali
Expected output
Sara Ali: 80
Hint 1

Compare with equalsIgnoreCase, not with ==.

Hint 2

Integer.parseInt(input.nextLine().trim()) reads a number from a whole line and avoids the nextInt trap.

Solution
import java.util.Scanner;

public class StageFind {
    static int find(String[] names, String target) {
        for (int i = 0; i < names.length; i++) {
            if (names[i].equalsIgnoreCase(target.trim())) {
                return i;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = Integer.parseInt(input.nextLine().trim());
        String[] names = new String[n];
        int[] marks = new int[n];
        for (int i = 0; i < n; i++) {
            names[i] = input.nextLine();
            marks[i] = Integer.parseInt(input.nextLine().trim());
        }
        int at = find(names, input.nextLine());
        if (at == -1) {
            System.out.println("Not found");
        } else {
            System.out.println(names[at] + ": " + marks[at]);
        }
    }
}

Stage 5: the pieces come together in a complete program

The grades manager: try it yourself

The file with main comes first. Add two students, ask for the table, and try entering the mark 150. Then copy the three files into your project in the Java Lab, download the ZIP and open it in NetBeans.

grades/GradeBook.java
package grades;

import java.util.Scanner;

public class GradeBook {
    static final int MAX_STUDENTS = 30;
    static final int EXAMS = 3;

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String[] names = new String[MAX_STUDENTS];
        int[][] marks = new int[MAX_STUDENTS][EXAMS];
        int count = 0;
        int choice;
        do {
            System.out.println("1 Add  2 Table  3 Summary  4 Find  0 Exit");
            System.out.print("Choice: ");
            choice = input.nextInt();
            input.nextLine(); // nextInt leaves the end of the line behind
            switch (choice) {
                case 1:
                    count = addStudent(input, names, marks, count);
                    break;
                case 2:
                    Report.printTable(names, marks, count);
                    break;
                case 3:
                    Report.printSummary(marks, count);
                    break;
                case 4:
                    findStudent(input, names, marks, count);
                    break;
                case 0:
                    System.out.println("Goodbye.");
                    break;
                default:
                    System.out.println("Unknown choice.");
            }
        } while (choice != 0);
    }

    static int addStudent(Scanner input, String[] names, int[][] marks, int count) {
        if (count == MAX_STUDENTS) {
            System.out.println("The class is full.");
            return count;
        }
        System.out.print("Name: ");
        names[count] = input.nextLine().trim();
        for (int exam = 0; exam < EXAMS; exam++) {
            marks[count][exam] = readMark(input, exam + 1);
        }
        input.nextLine();
        System.out.println("Added " + names[count] + ".");
        return count + 1;
    }

    static int readMark(Scanner input, int number) {
        System.out.print("Mark " + number + ": ");
        int mark = input.nextInt();
        while (mark < 0 || mark > 100) {
            System.out.print("Enter 0 to 100: ");
            mark = input.nextInt();
        }
        return mark;
    }

    static void findStudent(Scanner input, String[] names, int[][] marks, int count) {
        System.out.print("Name to find: ");
        String target = input.nextLine().trim();
        for (int i = 0; i < count; i++) {
            if (names[i].equalsIgnoreCase(target)) {
                System.out.printf("%s: average %.2f%n", names[i], Stats.average(marks[i]));
                return;
            }
        }
        System.out.println("Not found.");
    }
}
grades/Stats.java
package grades;

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

    public static int max(int[] values) {
        int best = values[0];
        for (int value : values) {
            if (value > best) {
                best = value;
            }
        }
        return best;
    }

    public static int min(int[] values) {
        int worst = values[0];
        for (int value : values) {
            if (value < worst) {
                worst = value;
            }
        }
        return worst;
    }
}
grades/Report.java
package grades;

public class Report {
    public static void printTable(String[] names, int[][] marks, int count) {
        if (count == 0) {
            System.out.println("No students yet.");
            return;
        }
        System.out.printf("%-12s%5s%5s%5s%8s%6s%6s%n", "Name", "M1", "M2", "M3", "Avg", "Best", "Low");
        for (int i = 0; i < count; i++) {
            int[] row = marks[i];
            System.out.printf("%-12s%5d%5d%5d%8.2f%6d%6d%n", names[i], row[0], row[1], row[2],
                    Stats.average(row), Stats.max(row), Stats.min(row));
        }
    }

    public static void printSummary(int[][] marks, int count) {
        if (count == 0) {
            System.out.println("No students yet.");
            return;
        }
        double total = 0;
        for (int i = 0; i < count; i++) {
            total += Stats.average(marks[i]);
        }
        System.out.printf("Students: %d%n", count);
        System.out.printf("Class average: %.2f%n", total / count);
    }
}

A full session with ready input

This copy reads ready input, which does not appear in the output, so the prompts run together. Press "Debug line by line" and watch count and names.

grades/GradeBook.java
package grades;

import java.util.Scanner;

public class GradeBook {
    static final int MAX_STUDENTS = 30;
    static final int EXAMS = 3;

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String[] names = new String[MAX_STUDENTS];
        int[][] marks = new int[MAX_STUDENTS][EXAMS];
        int count = 0;
        int choice;
        do {
            System.out.println("1 Add  2 Table  3 Summary  4 Find  0 Exit");
            System.out.print("Choice: ");
            choice = input.nextInt();
            input.nextLine(); // nextInt leaves the end of the line behind
            switch (choice) {
                case 1:
                    count = addStudent(input, names, marks, count);
                    break;
                case 2:
                    Report.printTable(names, marks, count);
                    break;
                case 3:
                    Report.printSummary(marks, count);
                    break;
                case 4:
                    findStudent(input, names, marks, count);
                    break;
                case 0:
                    System.out.println("Goodbye.");
                    break;
                default:
                    System.out.println("Unknown choice.");
            }
        } while (choice != 0);
    }

    static int addStudent(Scanner input, String[] names, int[][] marks, int count) {
        if (count == MAX_STUDENTS) {
            System.out.println("The class is full.");
            return count;
        }
        System.out.print("Name: ");
        names[count] = input.nextLine().trim();
        for (int exam = 0; exam < EXAMS; exam++) {
            marks[count][exam] = readMark(input, exam + 1);
        }
        input.nextLine();
        System.out.println("Added " + names[count] + ".");
        return count + 1;
    }

    static int readMark(Scanner input, int number) {
        System.out.print("Mark " + number + ": ");
        int mark = input.nextInt();
        while (mark < 0 || mark > 100) {
            System.out.print("Enter 0 to 100: ");
            mark = input.nextInt();
        }
        return mark;
    }

    static void findStudent(Scanner input, String[] names, int[][] marks, int count) {
        System.out.print("Name to find: ");
        String target = input.nextLine().trim();
        for (int i = 0; i < count; i++) {
            if (names[i].equalsIgnoreCase(target)) {
                System.out.printf("%s: average %.2f%n", names[i], Stats.average(marks[i]));
                return;
            }
        }
        System.out.println("Not found.");
    }
}
grades/Stats.java
package grades;

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

    public static int max(int[] values) {
        int best = values[0];
        for (int value : values) {
            if (value > best) {
                best = value;
            }
        }
        return best;
    }

    public static int min(int[] values) {
        int worst = values[0];
        for (int value : values) {
            if (value < worst) {
                worst = value;
            }
        }
        return worst;
    }
}
grades/Report.java
package grades;

public class Report {
    public static void printTable(String[] names, int[][] marks, int count) {
        if (count == 0) {
            System.out.println("No students yet.");
            return;
        }
        System.out.printf("%-12s%5s%5s%5s%8s%6s%6s%n", "Name", "M1", "M2", "M3", "Avg", "Best", "Low");
        for (int i = 0; i < count; i++) {
            int[] row = marks[i];
            System.out.printf("%-12s%5d%5d%5d%8.2f%6d%6d%n", names[i], row[0], row[1], row[2],
                    Stats.average(row), Stats.max(row), Stats.min(row));
        }
    }

    public static void printSummary(int[][] marks, int count) {
        if (count == 0) {
            System.out.println("No students yet.");
            return;
        }
        double total = 0;
        for (int i = 0; i < count; i++) {
            total += Stats.average(marks[i]);
        }
        System.out.printf("Students: %d%n", count);
        System.out.printf("Class average: %.2f%n", total / count);
    }
}
Output
1 Add  2 Table  3 Summary  4 Find  0 Exit
Choice: No students yet.
1 Add  2 Table  3 Summary  4 Find  0 Exit
Choice: Name: Mark 1: Mark 2: Mark 3: Added Sara Ali.
1 Add  2 Table  3 Summary  4 Find  0 Exit
Choice: Name: Mark 1: Enter 0 to 100: Mark 2: Mark 3: Added Omar.
1 Add  2 Table  3 Summary  4 Find  0 Exit
Choice: Name           M1   M2   M3     Avg  Best   Low
Sara Ali       80   90  100   90.00   100    80
Omar           55   60   70   61.67    70    55
1 Add  2 Table  3 Summary  4 Find  0 Exit
Choice: Students: 2
Class average: 75.83
1 Add  2 Table  3 Summary  4 Find  0 Exit
Choice: Name to find: Sara Ali: average 90.00
1 Add  2 Table  3 Summary  4 Find  0 Exit
Choice: Name to find: Not found.
1 Add  2 Table  3 Summary  4 Find  0 Exit
Choice: Unknown choice.
1 Add  2 Table  3 Summary  4 Find  0 Exit
Choice: Goodbye.
  1. Easy: add a choice that removes the last student by decreasing count.
  2. Medium: add a method letter(double average) to Stats that returns A to F, and show it as a column in the table.
  3. Harder: sort the table by average with bubble sort. Remember: you must swap the name and the row together.
Part two: course review and its traps

Printing, variables and input: lectures 1 to 6

Lecture 1: the public class is named Hello. What must the file it is saved in be named?

Lecture 2: what does the compiler do with a line starting with //?

Lectures 1 and 2: print, println and \n. What does it print?

public class PrintReview {
    public static void main(String[] args) {
        System.out.print("A");
        System.out.println("B");
        System.out.print("C\nD");
    }
}
Show the output
Output
AB
C
D

Why? print stays on the line, println moves down after printing, and \n inside the text moves down right there.

Lecture 3: + with text and numbers. What does it print?

public class ConcatReview {
    public static void main(String[] args) {
        int a = 2;
        int b = 3;
        System.out.println("Sum: " + a + b);
        System.out.println(a + b + " is the sum");
    }
}
Show the output
Output
Sum: 23
5 is the sum

Why? + reads from the left. After the first string everything joins; before it, two numbers add. Parentheses are the fix: "Sum: " + (a + b).

Lecture 3: x++ uses the value, then increases it. What does it print?

public class IncrementReview {
    public static void main(String[] args) {
        int x = 5;
        int y = x++;
        System.out.println(x + " " + y);
    }
}
Show the output
Output
6 5

Why? y takes the old value 5, then x becomes 6.

Lecture 4: what happens when System.out.printf("%d%n", 3.5); runs?

Lecture 5: the input is 19 then Sara, each on its own line. What does it print?

import java.util.Scanner;

public class NextLineTrap {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int age = input.nextInt();
        String name = input.nextLine();
        System.out.println(age);
        System.out.println("[" + name + "]");
    }
}
Show the output
Output
19
[]

Why? nextInt reads the number and leaves the end of the line. nextLine then reads what is left of that line, which is nothing. The fix: an extra input.nextLine(); after nextInt.

The cure for the nextLine trap

The extra line passes the end of the first line. Delete it and run the program to see the empty name come back.

import java.util.Scanner;

public class NextLineFix {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int age = input.nextInt();
        input.nextLine();
        String name = input.nextLine();
        System.out.println(age);
        System.out.println("[" + name + "]");
    }
}
Output
19
[Sara]

Lecture 6: integer division. What do the four lines print?

public class DivisionReview {
    public static void main(String[] args) {
        System.out.println(7 / 2);
        System.out.println(7 / 2.0);
        System.out.println((double) 7 / 2);
        System.out.println((double) (7 / 2));
    }
}
Show the output
Output
3
3.5
3.5
3.0

Why? Two whole numbers give a whole result. On the last line the division happens first and the decimal is gone, so the cast comes too late. So put (double) on one side of the division.

Lecture 6: what is the value of 2 + 3 * 4 % 5?

Conditions and decisions: lectures 7 and 8

= or ==?

Lecture 7: fix the condition so it prints five.

public class AssignOrEqual {
    public static void main(String[] args) {
        int x = 5;
        if (x = 5) {
            System.out.println("five");
        }
    }
}
Compiler messageincompatible types: int cannot be converted to boolean
Show the fix
public class AssignOrEqual {
    public static void main(String[] args) {
        int x = 5;
        if (x == 5) {
            System.out.println("five");
        }
    }
}
Output
five

Why? x = 5 is an assignment whose result is the number 5, and a condition needs a boolean. That is why the message talks about types and not the real cause: the missing sign.

Lecture 7: the user types java. What does it print?

import java.util.Scanner;

public class StringEquals {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String word = input.next();
        System.out.println(word == "java");
        System.out.println(word.equals("java"));
    }
}
Show the output
Output
false
true

Why? == asks about the place in memory, and what is read from the user is a new string in a new place. Always compare strings with equals.

Lecture 8: the order of conditions. The mark is 95. What does it print?

public class GradeOrder {
    public static void main(String[] args) {
        int mark = 95;
        if (mark >= 60) {
            System.out.println("D");
        } else if (mark >= 80) {
            System.out.println("B");
        } else if (mark >= 90) {
            System.out.println("A");
        }
    }
}
Show the output
Output
D

Why? In an else if chain only the first branch whose condition holds runs, and the rest are not checked. Start from the highest mark.

Lecture 8: we forgot break. What does it print?

public class MissingBreak {
    public static void main(String[] args) {
        int day = 2;
        switch (day) {
            case 1:
                System.out.println("Sunday");
            case 2:
                System.out.println("Monday");
            case 3:
                System.out.println("Tuesday");
                break;
            default:
                System.out.println("Other");
        }
    }
}
Show the output
Output
Monday
Tuesday

Why? Execution starts at case 2 and falls through to the next case until it reaches break. It does not reach default because break stops it.

One small semicolon. What does it print?

public class IfSemicolon {
    public static void main(String[] args) {
        int x = 3;
        if (x > 10);
        {
            System.out.println("big");
        }
        System.out.println("done");
    }
}
Show the output
Output
big
done

Why? The semicolon ends the if with an empty statement, so the block after it stands alone and always runs. The compiler does not count this as an error.

What is result after String result = (7 % 2 == 0) ? "even" : "odd";?

Loops: lectures 9 and 10

The off-by-one mistake. How many times does the loop run?

public class OffByOne {
    public static void main(String[] args) {
        int count = 0;
        for (int i = 1; i <= 5; i++) {
            count++;
        }
        for (int i = 0; i <= 5; i++) {
            count++;
        }
        System.out.println(count);
    }
}
Show the output
Output
11

Why? The first runs from 1 to 5, five times; the second from 0 to 5, six times. Remember: i < n from 0 runs exactly n times.

Lecture 9: the condition is false from the start. What does it print?

public class DoWhileReview {
    public static void main(String[] args) {
        int a = 10;
        while (a < 5) {
            System.out.println("while");
        }
        do {
            System.out.println("do");
        } while (a < 5);
    }
}
Show the output
Output
do

Why? while checks first and never enters. do-while runs once and then checks, which is why it suits menus and input validation.

Lecture 10: continue and break together. What does it print?

public class BreakContinue {
    public static void main(String[] args) {
        for (int i = 1; i <= 6; i++) {
            if (i == 2) {
                continue;
            }
            if (i == 5) {
                break;
            }
            System.out.print(i + " ");
        }
        System.out.println();
    }
}
Show the output
Output
1 3 4

Why? continue skips the rest of this pass, and break ends the whole loop before 5 prints.

Lecture 10: nested loops. What does it print?

public class NestedReview {
    public static void main(String[] args) {
        for (int row = 1; row <= 3; row++) {
            for (int col = 1; col <= row; col++) {
                System.out.print("*");
            }
            System.out.println();
        }
    }
}
Show the output
Output
*
**
***

Why? The inner loop finishes on each pass of the outer one, and its length depends on row.

Where did i end?

We want to print i after the loop. Fix the program so it prints 3.

public class LoopScope {
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            System.out.println("Hi");
        }
        System.out.println(i);
    }
}
Compiler messagecannot find symbol
Show the fix
public class LoopScope {
    public static void main(String[] args) {
        int i;
        for (i = 0; i < 3; i++) {
            System.out.println("Hi");
        }
        System.out.println(i);
    }
}
Output
Hi
Hi
Hi
3

Why? A variable declared in the head of a for lives only inside the loop. Declare it before the loop if you need it after.

What is missing from this loop?

int n = 3;
while (n > 0) {
    System.out.println(n);
}

Methods: lectures 11 and 12

Pass by value. What does it print? Trace it too with "Debug line by line".

public class PassByValue {
    static void change(int x, int[] a) {
        x = 99;
        a[0] = 99;
    }

    public static void main(String[] args) {
        int n = 1;
        int[] list = {1, 2};
        change(n, list);
        System.out.println(n);
        System.out.println(list[0]);
    }
}
Show the output
Output
1
99

Why? A parameter always gets a copy of the value. The copy of n is a number, so the original is untouched. The copy of list is the address of the same array, so the change shows in the original.

A swap method. Does it swap the two values?

public class SwapMethod {
    static void swap(int a, int b) {
        int temp = a;
        a = b;
        b = temp;
    }

    public static void main(String[] args) {
        int a = 1;
        int b = 2;
        swap(a, b);
        System.out.println(a + " " + b);
    }
}
Show the output
Output
1 2

Why? The swap happened on two copies inside the method, and they vanished when it ended. Matching names do not connect the variables.

Every path needs a return

Fix the method so it returns the larger value. Notice that the message points at the end of the method, not at a particular place inside it.

public class MaxOfTwo {
    static int max(int a, int b) {
        if (a > b) {
            return a;
        }
    }

    public static void main(String[] args) {
        System.out.println(max(3, 8));
    }
}
Compiler messagemissing return statement
Show the fix
public class MaxOfTwo {
    static int max(int a, int b) {
        if (a > b) {
            return a;
        }
        return b;
    }

    public static void main(String[] args) {
        System.out.println(max(3, 8));
    }
}
Output
8

Why? If the condition is false, the method has nothing to return. The compiler demands a return on every possible path through the method.

Lecture 12: which method does show(5.0) call if both show(int) and show(double) exist?

Lecture 12: recursion. What does it print?

public class RecursionReview {
    static int sum(int n) {
        if (n == 0) {
            return 0;
        }
        return n + sum(n - 1);
    }

    public static void main(String[] args) {
        System.out.println(sum(4));
    }
}
Show the output
Output
10

Why? 4 + 3 + 2 + 1 + 0 = 10. The stopping condition n == 0 is what prevents the calls going on forever.

Arrays and strings: lectures 13 to 15

Array bounds

Run it knowing it will stop: 3 numbers appear, then ArrayIndexOutOfBoundsException: 3. Change the condition to i < marks.length.

public class ArrayBounds {
    public static void main(String[] args) {
        int[] marks = {70, 85, 92};
        for (int i = 0; i <= marks.length; i++) {
            System.out.println(marks[i]);
        }
    }
}
Output
70
85
92

Lecture 13: did we copy the array, or not? What does it print?

public class ArrayAlias {
    public static void main(String[] args) {
        int[] a = {1, 2, 3};
        int[] b = a;
        b[0] = 9;
        System.out.println(a[0]);
        int[] c = new int[2];
        System.out.println(c[1]);
    }
}
Show the output
Output
9
0

Why? b = a creates no array, only a second name for the same one. The items of a new int[] start at 0.

Lecture 14: Arrays and a 2D array. What does it print?

import java.util.Arrays;

public class ArraysReview {
    public static void main(String[] args) {
        int[] nums = {3, 1, 2};
        Arrays.sort(nums);
        System.out.println(Arrays.toString(nums));
        int[][] grid = {{1, 2, 3}, {4, 5, 6}};
        System.out.println(grid.length);
        System.out.println(grid[0].length);
        System.out.println(grid[1][2]);
    }
}
Show the output
Output
[1, 2, 3]
2
3
6

Why? grid.length is the number of rows, and grid[0].length the columns of the first row. Positions start at 0, so [1][2] is the last item. sort changes the array itself, unlike string methods.

What does System.out.println(marks); print if marks is an int[]?

Lecture 15: what really changed? What does it print?

public class StringReview {
    public static void main(String[] args) {
        String s = " Gaza ";
        s.trim();
        System.out.println(s.length());
        s = s.trim().toUpperCase();
        System.out.println(s + s.length());
        System.out.println(s.substring(1, 3));
    }
}
Show the output
Output
6
GAZA4
AZ

Why? s.trim(); alone ignores the result, so the length is still 6. After saving it, the length is 4. substring(1, 3) takes only positions 1 and 2.

Part three: a mock practical exam

The exam: 120 minutes and six questions that get harder

  • Start a timer. The suggested time is in the title of each question, and they add up to 120 minutes.
  • Do not open the hints or the solution until time is up: a real exam will not have them.
  • Do not print any input prompts, and print the output exactly as asked. The "Check my solution" button runs several tests, including edge cases.
  • If you get stuck past a question's time, move on. Partial marks exist.

Question 1 (10 minutes): parking fee

Read the number of hours as a whole number. The first two hours cost 2.50 each, every hour after them costs 1.50, and the fee is at most 15.00. Print Fee: with the fee to two decimals. If the hours are zero or negative, print Invalid.

import java.util.Scanner;

public class Parking {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        // Read the hours, work out the fee, print it

    }
}
stdin
1
Expected output
Fee: 2.50
Hint 1

Math.min(fee, 15.0) applies the limit.

Solution
import java.util.Scanner;

public class Parking {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int hours = input.nextInt();
        if (hours <= 0) {
            System.out.println("Invalid");
        } else {
            double fee;
            if (hours <= 2) {
                fee = hours * 2.5;
            } else {
                fee = 5 + (hours - 2) * 1.5;
            }
            System.out.printf("Fee: %.2f%n", Math.min(fee, 15.0));
        }
    }
}

Question 2 (15 minutes): digits

Read a non-negative whole number. Print on three lines: Digits: with how many digits it has, Sum: with their sum and Reversed: with the number reversed as a number, so 1200 reversed is 21. Do not use strings; use % and /.

import java.util.Scanner;

public class Digits {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        // Take digits off the end with % 10 and / 10

    }
}
stdin
1234
Expected output
Digits: 4
Sum: 10
Reversed: 4321
Hint 1

The case 0 needs care: while (n > 0) never runs, but 0 has one digit. Think about do-while.

Solution
import java.util.Scanner;

public class Digits {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        int digits = 0;
        int sum = 0;
        int reversed = 0;
        do {
            int last = n % 10;
            digits++;
            sum += last;
            reversed = reversed * 10 + last;
            n = n / 10;
        } while (n > 0);
        System.out.println("Digits: " + digits);
        System.out.println("Sum: " + sum);
        System.out.println("Reversed: " + reversed);
    }
}

Questions 3 and 4: arrays and methods, then strings

Question 3 (20 minutes): analysing an array

Read n (one or more), then n whole numbers, which may be negative. Write a method for each task and print: Max: 9 at 2 with the largest value and the position of its first appearance, Above average: with how many values are strictly greater than the true average, and Reversed: with the values reversed, separated by a space.

import java.util.Scanner;

public class ArrayStats {
    // Write your methods here: indexOfMax, countAboveAverage, printReversed

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        int[] values = new int[n];
        // Read the values, then call your methods

    }
}
stdin
5
4 8 15 16 23
Expected output
Max: 23 at 4
Above average: 3
Reversed: 23 16 15 8 4
Hint 1

Start from the first item, not from zero, because all the values may be negative.

Hint 2

The average is a double. Integer division gives the wrong answer in the last test.

Solution
import java.util.Scanner;

public class ArrayStats {
    static int indexOfMax(int[] values) {
        int best = 0;
        for (int i = 1; i < values.length; i++) {
            if (values[i] > values[best]) {
                best = i;
            }
        }
        return best;
    }

    static int countAboveAverage(int[] values) {
        int sum = 0;
        for (int value : values) {
            sum += value;
        }
        double average = (double) sum / values.length;
        int count = 0;
        for (int value : values) {
            if (value > average) {
                count++;
            }
        }
        return count;
    }

    static void printReversed(int[] values) {
        System.out.print("Reversed:");
        for (int i = values.length - 1; i >= 0; i--) {
            System.out.print(" " + values[i]);
        }
        System.out.println();
    }

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        int[] values = new int[n];
        for (int i = 0; i < n; i++) {
            values[i] = input.nextInt();
        }
        int at = indexOfMax(values);
        System.out.println("Max: " + values[at] + " at " + at);
        System.out.println("Above average: " + countAboveAverage(values));
        printReversed(values);
    }
}

Question 4 (20 minutes): title case

Read a line that may have extra spaces. Print its words on one line with one space between them, each word starting with a capital and the rest small. Then print Words: with their count, then Longest: with the longest word as formatted, the first one on a tie. If the line is empty, print only Words: 0.

import java.util.Scanner;

public class TitleCase {
    // Write a method that formats one word

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String line = input.nextLine();
        // Clean the line, split it, build the result

    }
}
stdin
hello world
Expected output
Hello World
Words: 2
Longest: Hello
Hint 1

Character.toUpperCase(w.charAt(0)) + w.substring(1).toLowerCase() formats one word, and it works even with a single letter.

Hint 2

StringBuilder suits building the line. Add no space before the first word.

Solution
import java.util.Scanner;

public class TitleCase {
    static String format(String word) {
        return Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase();
    }

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String line = input.nextLine().trim();
        if (line.length() == 0) {
            System.out.println("Words: 0");
            return;
        }
        String[] words = line.split("\\s+");
        StringBuilder out = new StringBuilder();
        String longest = "";
        for (int i = 0; i < words.length; i++) {
            String word = format(words[i]);
            if (i > 0) {
                out.append(" ");
            }
            out.append(word);
            if (word.length() > longest.length()) {
                longest = word;
            }
        }
        System.out.println(out);
        System.out.println("Words: " + words.length);
        System.out.println("Longest: " + longest);
    }
}

Questions 5 and 6: a 2D array, then a program combining everything

Question 5 (25 minutes): table totals

Read the number of rows r and columns c (one or more), then the table's values row by row. Print Row 0: with each row's total, then Column 0: with each column's total, then Best row: with the position of the row with the largest total, the first on a tie. Write a method rowSum and a method columnSum.

import java.util.Scanner;

public class TableTotals {
    // Write rowSum and columnSum here

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int r = input.nextInt();
        int c = input.nextInt();
        int[][] table = new int[r][c];
        // Read the table, then print the totals

    }
}
stdin
2 3
1 2 3
4 5 6
Expected output
Row 0: 6
Row 1: 15
Column 0: 5
Column 1: 7
Column 2: 9
Best row: 1
Hint 1

In columnSum the row changes and the column stays: table[i][col], with the loop running up to table.length.

Hint 2

The table is not always square: do not mix up r and c. All the totals may be negative, so start from row 0, not from the value 0.

Solution
import java.util.Scanner;

public class TableTotals {
    static int rowSum(int[][] table, int row) {
        int sum = 0;
        for (int k = 0; k < table[row].length; k++) {
            sum += table[row][k];
        }
        return sum;
    }

    static int columnSum(int[][] table, int col) {
        int sum = 0;
        for (int i = 0; i < table.length; i++) {
            sum += table[i][col];
        }
        return sum;
    }

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int r = input.nextInt();
        int c = input.nextInt();
        int[][] table = new int[r][c];
        for (int i = 0; i < r; i++) {
            for (int k = 0; k < c; k++) {
                table[i][k] = input.nextInt();
            }
        }
        int best = 0;
        for (int i = 0; i < r; i++) {
            System.out.println("Row " + i + ": " + rowSum(table, i));
            if (rowSum(table, i) > rowSum(table, best)) {
                best = i;
            }
        }
        for (int k = 0; k < c; k++) {
            System.out.println("Column " + k + ": " + columnSum(table, k));
        }
        System.out.println("Best row: " + best);
    }
}

Question 6 (30 minutes): a report from a marks file

The first line is n, then n lines of the form name,m1,m2,m3; there may be spaces around the commas. A line is good if it has four pieces and each mark is digits only with a value from 0 to 100. For each good line print Sara: 90.00 A, with the letter: A from 90, B from 80, C from 70, D from 60, otherwise F. For each bad line print Line 2: invalid, numbering lines from 1. At the end print Valid: 2 of 3, then Top: Sara 90.00 for the highest average, the first on a tie, or Top: none.

import java.util.Scanner;

public class MarksReport {
    // Suggested methods: isMark(String text), letter(double average)

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = Integer.parseInt(input.nextLine().trim());
        // Read n lines and process each one

    }
}
stdin
3
Sara,80,90,100
Omar,70,70,71
Huda,50,55,60
Expected output
Sara: 90.00 A
Omar: 70.33 C
Huda: 55.00 F
Valid: 3 of 3
Top: Sara 90.00
Hint 1

Write isMark to check the text is not empty and all its characters pass Character.isDigit, then convert it and check the range. A 0 can be the top mark, so do not start top from 0 with >.

Hint 2

A mark may be empty between two commas, so do not convert before checking, or NumberFormatException appears.

Solution
import java.util.Scanner;

public class MarksReport {
    static boolean isMark(String text) {
        if (text.length() == 0 || text.length() > 3) {
            return false;
        }
        for (int i = 0; i < text.length(); i++) {
            if (!Character.isDigit(text.charAt(i))) {
                return false;
            }
        }
        return Integer.parseInt(text) <= 100;
    }

    static char letter(double average) {
        if (average >= 90) {
            return 'A';
        } else if (average >= 80) {
            return 'B';
        } else if (average >= 70) {
            return 'C';
        } else if (average >= 60) {
            return 'D';
        }
        return 'F';
    }

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = Integer.parseInt(input.nextLine().trim());
        int valid = 0;
        String topName = "";
        double topAverage = -1;
        for (int line = 1; line <= n; line++) {
            String[] parts = input.nextLine().split(",");
            boolean good = parts.length == 4;
            int total = 0;
            for (int i = 1; good && i < 4; i++) {
                String mark = parts[i].trim();
                if (isMark(mark)) {
                    total += Integer.parseInt(mark);
                } else {
                    good = false;
                }
            }
            if (good) {
                String name = parts[0].trim();
                double average = total / 3.0;
                System.out.printf("%s: %.2f %c%n", name, average, letter(average));
                valid++;
                if (average > topAverage) {
                    topAverage = average;
                    topName = name;
                }
            } else {
                System.out.println("Line " + line + ": invalid");
            }
        }
        System.out.println("Valid: " + valid + " of " + n);
        if (valid == 0) {
            System.out.println("Top: none");
        } else {
            System.out.printf("Top: %s %.2f%n", topName, topAverage);
        }
    }
}

Last review questions, and see you in Programming 2

In our project, names live in names and marks in marks. What is the problem when sorting the students?

What is the role of the Stats class in our project?