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
- Split a program into several files inside one package
- Build a grades manager with a menu, a loop, arrays and methods, one stage at a time
- Move your project from the Java Lab to NetBeans through a ZIP file
- Avoid the classic traps met in each lecture
- Solve a mock practical exam within a time limit
In this lecture
- You will build a program that manages a whole class's marks, using everything you learned
- Each file has one job, and the
gradespackage holds them together - The Java Lab keeps your project and hands it over as a ZIP to open in NetBeans
- Stage 1:
Statscalculates the average, the highest and the lowest mark - Stage 2:
Reportprints a table with straight columns - Stage 3: a
do-whileloop and aswitchmake the menu - Stage 5: the pieces come together in a complete program
- Printing, variables and input: lectures 1 to 6
- Conditions and decisions: lectures 7 and 8
- Loops: lectures 9 and 10
- Methods: lectures 11 and 12
- Arrays and strings: lectures 13 to 15
- The exam: 120 minutes and six questions that get harder
- Questions 3 and 4: arrays and methods, then strings
- Questions 5 and 6: a 2D array, then a program combining everything
- Last review questions, and see you in Programming 2
You will build a program that manages a whole class's marks, using everything you learned
| Idea | Where it appears in the project |
|---|---|
Scanner and nextLine | Reading names and marks |
do-while and switch | The menu that repeats until exit |
while for input validation | Rejecting a mark outside 0 to 100 |
| Arrays and 2D arrays | String[] for names and int[][] for marks |
| Methods and parameters | Each task in a method, each group of methods in a class |
printf and equalsIgnoreCase | A formatted table and a search by name |
Each file has one job, and the grades package holds them together
| File | Its job | Its methods |
|---|---|---|
grades/GradeBook.java | main, the menu and reading from the user | main, addStudent, readMark, findStudent |
grades/Stats.java | Calculation only, no printing | average, max, min |
grades/Report.java | Formatted printing only | printTable, 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
staticmethods; we create no objects from them. - Inside the same package no
importis needed: we just writeStats.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.
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));
}
}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;
}
}Average: 81.67 Best: 90 Lowest: 70
Why does the Stats class print nothing and only return values?
Report prints it in a table, and other code may compare with it. Separating calculation from display is one of a programmer's best habits.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.
- Open "Java Lab" from the top of the siteFrom the "+ New project…" menu choose "Empty program", then press "Rename" and call it
Grades. - Add the files with the
+button above the editorType the name with its folder:grades/GradeBook.java, thengrades/Stats.javaandgrades/Report.java. The lab adds thepackageline itself. DeleteMain.javawith the×button. - Write the code, or copy it from the labs on this pageEverything saves in this browser automatically. Press Ctrl+Enter to run.
- Press "Download ZIP"Then extract it into a new folder, such as
GradesSource. You should find thegradesfolder inside it. - 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, notgrades. - 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));
}
}3 70 85 90
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.
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);
}
}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;
}
}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);
}
}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);
}
}2 Sara 80 90 100 Omar 55 60 70
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
count variableThe 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++;nextInt trapnextInt 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
}
}1 Sara Ali 1 Omar 2 0
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
}
}2 Sara Ali 80 Omar 75 sara ali
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.
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.");
}
}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;
}
}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.
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.");
}
}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;
}
}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);
}
}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.
- Easy: add a choice that removes the last student by decreasing
count. - Medium: add a method
letter(double average)toStatsthat returnsAtoF, and show it as a column in the table. - Harder: sort the table by average with bubble sort. Remember: you must swap the name and the row together.
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?
hello.java is not the same name.Lecture 2: what does the compiler do with a line starting with //?
/** */ is a javadoc comment, which the compiler also ignores but the javadoc tool reads.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
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
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
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?
%d is for whole numbers only and %f for decimals. The compiler does not check the format text, so IllegalFormatConversionException appears at run time.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
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 + "]");
}
}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
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?
* and % come before +, from left to right: 3 × 4 = 12, then 12 % 5 = 2, then 2 + 2 = 4.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");
}
}
}incompatible types: int cannot be converted to booleanShow the fix
public class AssignOrEqual {
public static void main(String[] args) {
int x = 5;
if (x == 5) {
System.out.println("five");
}
}
}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
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
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
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
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
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
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
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
* ** ***
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);
}
}cannot find symbolShow 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);
}
}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);
}
n never changes, so the condition stays true forever and the program prints 3 without stopping. Every loop needs a step that brings it closer to the end.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
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
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));
}
}missing return statementShow 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));
}
}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?
5.0 is a double.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
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]);
}
}
}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
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
[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[]?
Arrays.toString(marks) or a loop.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
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.
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
}
}1
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
}
}1234
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
}
}5 4 8 15 16 23
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
}
}hello world
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
}
}2 3 1 2 3 4 5 6
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
}
}3 Sara,80,90,100 Omar,70,70,71 Huda,50,55,60
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?
Student class holding the name and the marks in one object, and this problem disappears.What is the role of the Stats class in our project?
Stats.average(row) through the class name without creating an object, just like Math.sqrt. Classes that represent things with their fields are the subject of Programming 2.