Workshop · Java · Beginner
JV050 Java from zero
Java taught the way working teams use it: typed variables, exact money with BigDecimal, methods you can test, files you can trust, and a Maven project that builds offline in your own VS Code seat with the AI tutor beside you. Forty short steps across eight sessions take you from one printed line to Tally, a tested command-line expense tracker with an exit-code contract, and every step ends with a check the machine runs on your own code.
Open Coding Tutor at codingtutor.agilesystemsoftware.com, pick JV050 from the course button in the chat and press Tutor me. Every lesson, demo and hands-on step is also published below.
Who it is for
Adults and teens who have never programmed, or who tried once and stopped at the first wall of red text. You can type, follow a recipe and read an error message slowly. No maths beyond percentages. If you already code in another language, take the placement step 8.5 or start at JV100.
Where it leads. JV100 Java I · DN100 C# I: the working programmer’s toolkit · PY100 Python I · RS100 Rust I
What you will be able to do
- Run a Java file from the terminal, build a Maven project offline, and read a compiler error back to the line it names
- Hold text, whole numbers, decimals and money in the right type, and explain why money is BigDecimal, never double
- Format numbers and columns with printf so a human can read them
- Choose with if/else and switch expressions, and repeat with for and while without an off-by-one
- Keep things in ArrayList and HashMap, walk them, sort them and count them
- Write a static method with inputs and a return value, model data with a record, and test both with JUnit 5
- Read and write files with java.nio.file and try-with-resources, parse CSV lines, and survive a missing file
- Build a command-line program with arguments, a clear structure and an exit-code contract
Syllabus
Every session ends in a lab the machine checks on your own code. Open a session's mini steps to read each lesson, demo and hands-on task; the badge shows how far the engine can verify it: checked · T3 a check runs over your files, proven · T4 the grader also tries inputs you did not see, tutored · T0 done on your word. Each step has its own link (for example #s1-1).
- 1. Your first programs
- 2. Data: text, numbers and money
- 3. Computing and formatting
- 4. Choices and repetition
- 5. Lists and maps
- 6. Methods and tests
- 7. Files and errors
- 8. Capstone: Tally
Session 1 · Your first programs
Five steps that make the seat yours. What a Java program is: a class with a main method the Java launcher runs from the top. The rhythm of the course — run, read, change, run again — using the single-file launcher java Hello.java, so there is no project to set up for the first hour. Text and numbers as different kinds of value. Reading a compiler error back to the file and line it names. Then the first Maven project, copied from the seat's starter and built offline with mvn -o, so you see the shape of the real thing. Live in the seat: a file run from the terminal, a deliberate typo and its message, three ways to break println, and a build that goes green.
Lab Write Greet.java: it prints a three-line banner, a sentence that uses a number computed from 2 + 3, and a line built from two pieces of text joined with +. It must run clean on the first try after you have read your own code once. Session 1 checkpoint.
5 mini steps · ~41 min
1.1Run, read, change8 minchecked · T3
Predict
Hello.java has a main method with System.out.println("Hello"); What does java Hello.java show?
Lesson
A Java program is a class with a main method; the launcher compiles the file and runs main from the top. The ceremony around main is the same every time, so you learn it once. The rhythm: run, read the output, change one thing, run again.
Demo
In the seat the tutor opens java/jv050/demo/Hello.java, runs java Hello.java in the terminal, points at the one line of output, changes the word inside the quotes, runs again, and shows both runs stacked in the terminal.
On screen
Explorer → java/jv050/demo/Hello.java → Terminal → java Hello.java → edit the string → run again.
Hands-on
Create java/jv050/s1/Hello.java with a class Hello, a main method and one println of your own words (not Hello), run it, then add a second println and run it again so the terminal shows both runs.
Explain
In one sentence: what did the java command do with your file?
Check
Hello.java prints two lines of your own — checked (T3):
seat_fileseat_run1.2Text and numbers are different things7 minchecked · T3
Predict
What do println(2 + 3) and println("2" + "3") print?
Lesson
Java keeps text (String) and numbers (int, double) as different types and the same symbol does different work on each: + adds numbers but joins strings. Every variable declares its type, so the compiler can tell you when you mix them up.
Demo
The tutor opens jshell in the terminal and types 2 + 3, "2" + "3", "ab".repeat(3), 7 / 2 and 7.0 / 2, reading each result and pausing on 7 / 2 being 3, then quits jshell with /exit.
Hands-on
In java/jv050/s1/Types.java print four lines: the sum 40 + 2, the text "40" + "2", "ab".repeat(3), and 7 / 2. Run it and compare with your predictions.
Explain
Why did 7 / 2 print 3 and not 3.5?
Check
Types.java prints 42, 402, ababab and 3 — checked (T3):
seat_run1.3Reading a compiler error8 minchecked · T3
Predict
Line 4 has System.out.println("Hi") without a semicolon. What does java Hello.java say?
Lesson
A compiler error is a map, not a verdict: file name, line number, a caret under the spot, and a reason. Go to the line first, read it slowly, then think about the reason. Fix one error at a time; later ones are often echoes of the first.
Demo
The tutor breaks java/jv050/demo/Broken.java three ways in turn — a missing semicolon, Println with a capital P, a string assigned to an int — runs each, and reads the message from the top: file, line, caret, reason.
Hands-on
Copy java/jv050/samples/s1/Oops.java to java/jv050/s1/Fixed.java. It has three errors. Fix them one at a time by reading each message, until it prints "Total: 12" and "Done".
Explain
Which part of the error message do you read first, and why?
Check
Fixed.java compiles and prints Total: 12 and Done — checked (T3):
seat_run1.4The Maven project9 minchecked · T3
Predict
The seat has no internet. What does mvn -o do?
Lesson
Real Java lives in a project: a pom.xml that names the project and its tools, src/main/java for code, src/test/java for tests, target/ for what the build makes. The seat's starter pom has every plugin pinned and offline; copy it, do not rewrite it.
Demo
The tutor copies the seat starter to java/jv050/tally, renames the class to tally.App, runs mvn -o -q package and then mvn -o -q exec:java, and walks the folder tree: pom.xml, src/main/java, src/test/java, target/.
On screen
Explorer → copy /opt/sfseat/starter/java/hello-java → java/jv050/tally → edit pom artifactId and exec.mainClass → Terminal → mvn -o -q package → mvn -o -q exec:java.
Hands-on
Copy the starter into java/jv050/tally. Rename the main class to tally.App in src/main/java/tally/App.java, set exec.mainClass in the pom to tally.App, make it print "Tally 0.1", and build with mvn -o -q package, then run it with exec:java.
Explain
What does the pom.xml do for you?
Check
The tally project builds offline and prints Tally 0.1 — checked (T3):
seat_fileseat_buildseat_run1.5Lab: the greeting banner9 minchecked · T3
Predict
println("Ja" + "va " + 25) prints what?
Lesson
Everything from this session in one small file: a class with main, printlns, strings joined with +, a number computed in a sentence, a header comment, and the discipline of reading your own file once before you run it.
Demo
The tutor builds java/jv050/demo/Banner.java live: three banner lines, a sentence with 2 + 3 in brackets, a line from two joined strings, runs it, then shows the lab check's output so the learner knows what the grader reads.
Hands-on
Write java/jv050/s1/Greet.java: a // comment on line 1; a banner of exactly three lines (= signs, a title, = signs); a sentence that includes the number 5 computed as (2 + 3); and one line made by joining two strings with +. Run it clean on the first try.
Explain
Name one thing you now check in a file before you run it.
Check
Greet.java: three-line banner, a computed 5, a joined line — checked (T3):
seat_fileseat_run
Session 2 · Data: text, numbers and money
Five steps on how Java holds values. Declared types: int, long, double, boolean, String, and why the compiler wants to know. Whole numbers versus decimals: integer division, overflow at two billion, and the one fact that prevents a hundred bugs: 0.1 + 0.2 is not 0.3 in binary floating point. Money as BigDecimal built from a string, added and compared properly. What survives when a value is poured into a differently shaped type: the (int) cast that truncates, setScale that rounds, the long that wraps. Live in the seat: a double that prints 0.30000000000000004, an int that overflows to a negative number, BigDecimal adding 0.10 a thousand times to exactly 100.00, and 12345.67 cast to an int.
Lab Write Receipt.java: three items with BigDecimal prices and integer quantities, a line per item, a subtotal, a 10% tax computed and rounded to cents with setScale, and a total. The numbers must be exact. Session 2 checkpoint.
5 mini steps · ~40 min
2.1Variables have types7 minchecked · T3
Predict
int count = "3"; What happens?
Lesson
Every variable declares its type: int for whole numbers, double for decimals, boolean for true/false, String for text. The compiler uses the types to catch mistakes before anything runs, which is the trade you make for the extra words.
Demo
The tutor declares one of each type in java/jv050/demo/Vars.java, prints them, then tries int count = "3" and reads the compiler's incompatible types message, then fixes it with Integer.parseInt("3").
Hands-on
In java/jv050/s2/Vars.java declare an int qty = 3, a double weightKg = 1.5, a boolean inStock = true and a String item = "Notebook", and print each on its own line as "<name>: <value>".
Explain
Why does Java make you write the type?
Check
Vars.java declares four types and prints four labelled lines — checked (T3):
seat_fileseat_run2.2Whole numbers and decimals8 minchecked · T3
Predict
double a = 0.1 + 0.2; println(a == 0.3); What prints?
Lesson
int is exact but limited to about two billion; long goes much further. double holds decimals approximately in binary, so 0.1 + 0.2 prints 0.30000000000000004. Integer division drops the fraction; % gives the remainder.
Demo
The tutor prints 0.1 + 0.2, then 2_000_000_000 + 2_000_000_000 as int (negative!) and as long, then 7 / 2, 7 % 2 and 7.0 / 2 in java/jv050/demo/Numbers.java, reading each surprise aloud.
Hands-on
In java/jv050/s2/Numbers.java print 0.1 + 0.2; the int sum 2_000_000_000 + 2_000_000_000; the same sum as long (2_000_000_000L); 17 / 5; and 17 % 5. Five lines.
Explain
Why is 0.1 + 0.2 not 0.3 in a double?
Check
Numbers.java shows the float error, the overflow, the long and integer division — checked (T3):
seat_run2.3Money is BigDecimal8 minchecked · T3
Predict
new BigDecimal("0.10") added to itself ten times prints what?
Lesson
Money is never a double. BigDecimal holds exactly the digits you give it: build it from a String (new BigDecimal("19.99"), never from a double), add with .add, multiply with .multiply, compare with compareTo. It is more typing and it is correct.
Demo
The tutor adds 0.10 a thousand times as double and as BigDecimal in java/jv050/demo/Money.java and prints both, then shows new BigDecimal(0.1) versus new BigDecimal("0.1") to make the string-constructor rule stick.
Hands-on
In java/jv050/s2/Money.java build price = new BigDecimal("19.99") and qty = new BigDecimal("3"); print price.multiply(qty), then a loop adding new BigDecimal("0.10") a hundred times and print the sum.
Explain
Why build a BigDecimal from a String and not from a double?
Check
Money.java prints 59.97 and 10.00 exactly — checked (T3):
seat_fileseat_run2.4What survives a conversion8 minchecked · T3
Predict
double gross = 12345.67; println((int) gross); What prints?
Lesson
Pouring a value into a differently shaped type decides what survives. (int) 12345.67 drops the fraction silently; a long that does not fit an int wraps; BigDecimal.setScale(2, RoundingMode.HALF_UP) rounds on purpose and says how.
Demo
The tutor casts 12345.67 to int, (int) 3_000_000_000L, then new BigDecimal("12345.678").setScale(2, HALF_UP) and the same with setScale(0) in java/jv050/demo/Shapes.java, pointing at what was lost each time.
Hands-on
In java/jv050/s2/Shapes.java print (int) 12345.67; (int) 3_000_000_000L; new BigDecimal("12345.675").setScale(2, RoundingMode.HALF_UP); and the same value with setScale(0, RoundingMode.HALF_UP). Four lines.
Explain
Why is the lost fraction in a cast silent?
Check
Shapes.java prints 12345, -1294967296, 12345.68 and 12346 — checked (T3):
seat_fileseat_run2.5Lab: the receipt9 minchecked · T3
Predict
Subtotal 23.97, tax 10% rounded HALF_UP to cents. What is the tax?
Lesson
A receipt is the first program where exactness matters: BigDecimal prices from strings, integer quantities, a subtotal, a tax rate as new BigDecimal("0.10"), setScale(2, HALF_UP) once at the end of the tax, and a total that adds up to the cent.
Demo
The tutor writes java/jv050/demo/ReceiptDemo.java with two items, prints each line as "<qty> x <item> <lineTotal>", computes tax with setScale, and shows the three money lines, then changes a price by a cent to show the total move.
Hands-on
Write java/jv050/s2/Receipt.java with three items: Notebook 2.50 × 3, Pen 1.20 × 4, Folder 3.99 × 1. Print a line per item "<qty> x <item> <lineTotal>", then "Subtotal: 16.29", "Tax: <10% HALF_UP>", "Total: <subtotal + tax>".
Explain
Where exactly did you round, and why only there?
Check
Receipt.java prints exact money lines: subtotal 16.29, tax 1.63, total 17.92 — checked (T3):
seat_fileseat_run
Session 3 · Computing and formatting
Five steps on getting numbers right and readable. Operators and precedence, and why a / 100 * b differs from a * b / 100 with ints. Math.round, Math.max, Math.abs and RoundingMode for the cases that matter. printf and String.format: %d, %.2f, %-10s and %8s to line up a column so a human can read it. Reading what the user typed with Scanner and from the command line with args. Live in the seat: a tip computed three ways with two of them wrong, a column of prices that jumps around until %8.2f fixes it, and a program that asks a question and answers back.
Lab Write Tip.java: given a bill and a tip percentage on the command line, print the tip and the total, both to two decimals, lined up in a two-column table. Session 3 checkpoint.
5 mini steps · ~39 min
3.1Operators and precedence7 minchecked · T3
Predict
int bill = 80; println(bill / 100 * 15); What prints?
Lesson
Multiplication and division run before addition and subtraction, left to right among equals; brackets change the order. With ints, order also decides whether a fraction is lost in the middle, so multiply before you divide.
Demo
The tutor prints bill / 100 * 15, bill * 15 / 100 and (bill * 15) / 100.0 in java/jv050/demo/Ops.java, then 2 + 3 * 4 and (2 + 3) * 4, naming the rule that applied to each.
Hands-on
In java/jv050/s3/Ops.java with int bill = 80 print four lines: bill / 100 * 15; bill * 15 / 100; 2 + 3 * 4; (2 + 3) * 4.
Explain
Why does the order of * and / matter for ints?
Check
Ops.java prints 0, 12, 14 and 20 — checked (T3):
seat_run3.2Math and rounding on purpose7 minchecked · T3
Predict
Math.round(2.5) and Math.round(-2.5) give?
Lesson
Math has round, floor, ceil, max, min and abs for doubles and ints. Math.round rounds half toward positive infinity, which surprises people on negatives; for money use BigDecimal with a named RoundingMode and say which.
Demo
The tutor prints Math.round(2.5), Math.round(-2.5), Math.floor(2.7), Math.ceil(2.1), Math.max(3, 9) and Math.abs(-4) in java/jv050/demo/Rounding.java, then the BigDecimal HALF_UP and HALF_EVEN of 2.5 side by side.
Hands-on
In java/jv050/s3/Rounding.java print Math.round(2.5), Math.round(-2.5), Math.floor(2.7), Math.ceil(2.1), and new BigDecimal("2.5").setScale(0, RoundingMode.HALF_EVEN). Five lines.
Explain
When would you pick HALF_EVEN over HALF_UP?
Check
Rounding.java prints 3, -2, 2.0, 3.0 and 2 — checked (T3):
seat_run3.3printf and String.format8 minchecked · T3
Predict
System.out.printf("%-6s|%6.2f%n", "Pen", 1.2); prints?
Lesson
printf fills a template: %d for whole numbers, %.2f for two decimals, %s for text, a width like %8 to pad, a minus to left-align, %n for a new line. String.format builds the same text without printing it.
Demo
The tutor prints three products in java/jv050/demo/Table.java first with println and +, then with printf("%-10s %8.2f%n", …), and the column snaps into place; then a header line with the same widths.
Hands-on
In java/jv050/s3/Table.java print a header "Item Price" and three rows with printf("%-10s %8.2f%n") for Notebook 2.5, Pen 1.2 and Folder 13.99.
Explain
What do the 10 and the 8 in the format do?
Check
Table.java prints an aligned price column — checked (T3):
seat_fileseat_run3.4Reading input8 minproven · T4
Predict
java Ask.java 12 is run. What is args.length and what is args[0]?
Lesson
Two ways to get input: Scanner on System.in asks the user while the program runs; args takes values from the command line, which is what a script or a grader uses. Both give Strings; convert with Integer.parseInt or new BigDecimal.
Demo
The tutor writes java/jv050/demo/Ask.java with a Scanner that asks for a quantity and answers back, then a second version that reads args[0] instead and runs java Ask.java 7, pointing out that only the second is testable.
Hands-on
In java/jv050/s3/Double.java read a whole number from args[0] and print "Twice <n> is <2n>". Then, if no argument is given (args.length == 0), print "Usage: java Double.java <number>" instead.
Explain
Why do the course checks use args and not Scanner?
Check
Double.java doubles the argument and prints usage without one — proven (T4):
seat_run3.5Lab: the tip calculator9 minchecked · T3
Predict
Bill 48.00, tip 15%. Tip and total?
Lesson
A real little tool: two arguments, exact money with BigDecimal, one rounding with setScale(2, HALF_UP), and a two-column printf table. Everything from sessions 2 and 3 in thirty lines.
Demo
The tutor builds java/jv050/demo/TipDemo.java: parse args[0] as BigDecimal and args[1] as a percentage, compute the tip, round once, print Bill/Tip/Total with %-6s %8.2f, and runs it with 48.00 15 and 19.99 18.
Hands-on
Write java/jv050/s3/Tip.java taking a bill and a tip percentage from args. Print three printf rows with "%-6s %8.2f%n": Bill, Tip (rounded HALF_UP to cents) and Total. Run with 48.00 15 and with 19.99 18.
Explain
Why BigDecimal for the bill and not double?
Check
Tip.java prints Bill, Tip and Total as an aligned table — checked (T3):
seat_fileseat_runseat_run
Session 4 · Choices and repetition
Five steps on control flow. if/else and the shape of a decision; combining conditions with &&, || and !, and why strings are compared with equals and never ==. switch expressions with arrows that return a value and refuse to fall through. for loops for a known count, while loops until something changes, and the off-by-one that bites everyone once. Live in the seat: a grade decided by a chain of else if, a password check that fails because of ==, a switch that turns a weekday number into a name, a loop that runs one time too many and the fix.
Lab Write Quiz.java: five questions with fixed answers, the player's answers given on the command line, one line per question saying right or wrong with the expected answer, and a final score out of five. Session 4 checkpoint.
5 mini steps · ~40 min
4.1if, else if, else7 minproven · T4
Predict
if (score >= 90) "A" else if (score >= 80) "B" else "C". Score 90 gives?
Lesson
if takes a boolean; the first true branch runs and the chain stops. Order the conditions from most specific to least, and let else catch what is left. Always use the curly brackets, even for one line; it saves a famous bug.
Demo
The tutor writes a grade chain in java/jv050/demo/Grade.java, runs it with 95, 85 and 40, then swaps two branches to show why order matters, and removes brackets to show the one-line trap.
Hands-on
In java/jv050/s4/Grade.java read a score from args[0] and print exactly one letter: A at 90 or more, B at 80 or more, C at 70 or more, otherwise F.
Explain
Why must the conditions be ordered from highest to lowest here?
Check
Grade.java prints the right letter for any score — proven (T4):
seat_run4.2Combining conditions and comparing strings8 minproven · T4
Predict
String a = args[0]; if (a == "yes") … The user typed yes. Does the branch run?
Lesson
&& means both, || means either, ! flips. Java stops evaluating as soon as the answer is known. Strings are objects: compare them with equals or equalsIgnoreCase, never with ==, which checks identity.
Demo
The tutor writes an age-and-member check with && and || in java/jv050/demo/Logic.java, then a password check with == that fails for typed input, and fixes it with equals, running each.
Hands-on
In java/jv050/s4/Entry.java read an age (args[0]) and a ticket type (args[1]). Print "Enter" if age is 18 or more and the ticket equals "full", or if the ticket equals "guest" regardless of age; otherwise print "No entry".
Explain
Why did == fail on strings that looked equal?
Check
Entry.java decides Enter or No entry with && and || — proven (T4):
seat_fileseat_run4.3switch expressions8 minproven · T4
Predict
String name = switch (day) { case 1 -> "Mon"; case 2 -> "Tue"; default -> "?"; }; day is 2. name is?
Lesson
A switch expression picks one case among many and gives back a value: switch (x) { case 1 -> "Mon"; … default -> "?"; }. Arrows do not fall through, several labels can share a case, and default covers the rest.
Demo
The tutor turns a weekday number into a name with a switch expression in java/jv050/demo/Days.java, groups 6 and 7 as "Weekend", runs it with 3 and 7, and shows the compiler complaining when default is missing.
Hands-on
In java/jv050/s4/Days.java read a number from args[0] and print the weekday name (Mon to Fri for 1 to 5), "Weekend" for 6 and 7, and "Unknown" otherwise, using a switch expression with arrows.
Explain
What does the arrow form give you that the old colon form did not?
Check
Days.java maps 1–7 and the rest with a switch expression — proven (T4):
seat_fileseat_run4.4for and while, and the off-by-one8 minproven · T4
Predict
for (int i = 1; i <= 10; i++) prints i. How many lines, and what is the last?
Lesson
for counts: start, condition, step. while repeats until a condition changes, and you must make it change. Off-by-one errors come from < versus <= and from starting at 0 or 1: decide, then check the first and last value by hand.
Demo
The tutor sums 1 to 100 with for in java/jv050/demo/Loops.java (5050), then writes a while loop that doubles a balance until it passes 1000 and prints the years, then breaks it with a missing step to show an endless loop and stops it.
Hands-on
In java/jv050/s4/Grow.java read a starting amount (args[0], whole number) and a rate percent (args[1]). Count the years, with a while loop, until the amount is at least double the start, growing by the rate each year with integer arithmetic amount = amount * (100 + rate) / 100. Print "Years: <n>".
Explain
What stops your while loop, and what would make it run forever?
Check
Grow.java counts the doubling years with a while loop — proven (T4):
seat_fileseat_run4.5Lab: the quiz game9 minchecked · T3
Predict
Five answers are compared with equalsIgnoreCase. The player types "PARIS" for "Paris". Right or wrong?
Lesson
Arrays hold the questions and the expected answers; a for loop walks them in step with args; an if with equalsIgnoreCase scores each one; a counter accumulates; a final printf reports the score. Thirty lines of everything so far.
Demo
The tutor writes java/jv050/demo/QuizDemo.java with two questions, runs it with both answers right and then one wrong, and shows how the counter and the per-question lines come out.
Hands-on
Write java/jv050/s4/Quiz.java with five fixed questions whose answers are Paris, 7, Java, blue and 1969. The player's five answers come from args. For each print "Q<n>: right" or "Q<n>: wrong (expected <answer>)", then "Score: <n>/5". If fewer than five answers are given print "Usage: five answers" and nothing else.
Explain
How does the loop know which expected answer goes with which argument?
Check
Quiz.java scores five answers and prints the score — checked (T3):
seat_fileseat_runseat_runseat_run
Session 5 · Lists and maps
Five steps on collections. ArrayList for an ordered list that grows: add, get, size, remove and the for-each loop. Walking a list to sum and to find the largest. HashMap for looking things up by key and counting how often each word appears, with getOrDefault. Sorting with Collections.sort and a Comparator, and List.of for a fixed list. Live in the seat: a shopping list that grows and shrinks, a total computed from a list of BigDecimal, a word count over a sentence, a list of prices sorted both ways.
Lab Write ShoppingList.java: items from the command line, printed sorted alphabetically with duplicates counted ("apple x2"), then the number of distinct items. Session 5 checkpoint.
5 mini steps · ~39 min
5.1ArrayList: a list that grows7 minchecked · T3
Predict
List<String> xs = new ArrayList<>(); xs.add("a"); xs.add("b"); xs.remove(0); println(xs.get(0)); prints?
Lesson
ArrayList is an ordered list that grows as you add. Declare it as List<String> and create it with new ArrayList<>(); add, get(index), size(), remove. Indexes start at 0, and the for-each loop walks it without an index.
Demo
The tutor builds a list of three items in java/jv050/demo/Lists.java, prints size and get(1), removes one, prints again, then walks it with for (String s : items) and with an indexed for to show both.
Hands-on
In java/jv050/s5/Items.java create a List<String>, add Notebook, Pen and Folder, print the size, remove Pen, then print each remaining item on its own line with a for-each loop.
Explain
When would you use the indexed loop instead of for-each?
Check
Items.java prints 3, Notebook, Folder — checked (T3):
seat_fileseat_run5.2Walking a list: sums and largest7 minchecked · T3
Predict
BigDecimal total = BigDecimal.ZERO; for each price: total.add(price); What is total at the end?
Lesson
An accumulator starts at zero and grows on each turn: total = total.add(price). BigDecimal does not change in place, so always assign the result. For the largest, start with the first element and replace it when you see a bigger one.
Demo
The tutor sums a List<BigDecimal> with the assignment forgotten (ZERO!) and then fixed, in java/jv050/demo/Sum.java, then finds the largest with compareTo and prints both.
Hands-on
In java/jv050/s5/Totals.java make a List<BigDecimal> of 2.50, 13.99, 1.20 and 7.00 from strings. Print "Total: <sum>" and "Largest: <max>" using a loop and compareTo.
Explain
Why must you write total = total.add(price)?
Check
Totals.java prints Total: 24.69 and Largest: 13.99 — checked (T3):
seat_fileseat_run5.3HashMap: look up and count8 minproven · T4
Predict
counts.put("a", counts.getOrDefault("a", 0) + 1) run three times. counts.get("a") is?
Lesson
A Map<String, Integer> holds a value per key: put, get, containsKey, getOrDefault. Keys are unique, so a map is the natural way to count things: look up the current count with a default of 0, add one, put it back.
Demo
The tutor counts the words of a sentence in java/jv050/demo/Count.java with split(" ") and getOrDefault, prints the map, then walks counts.entrySet() to print one line per word.
Hands-on
In java/jv050/s5/Count.java take a sentence from args (join args with spaces), count each word with a HashMap<String, Integer>, and print "<word>: <count>" for each word in a TreeMap order (sorted keys).
Explain
Why is a map better than a list for counting?
Check
Count.java counts words and prints them sorted — proven (T4):
seat_fileseat_run5.4Sorting8 minchecked · T3
Predict
Collections.sort(List.of(3, 1, 2)) does what?
Lesson
Collections.sort orders a mutable list in place by natural order; list.sort(Comparator.reverseOrder()) or Comparator.comparing(...) choose another order. List.of is fixed: copy it to new ArrayList<>(...) before sorting.
Demo
The tutor sorts a list of prices ascending and descending in java/jv050/demo/Sort.java, then sorts names by length with Comparator.comparing(String::length), and shows the List.of trap and its fix.
Hands-on
In java/jv050/s5/Sort.java put 13.99, 2.50, 7.00 and 1.20 (BigDecimal) in an ArrayList. Print them sorted ascending on one line joined by spaces, then descending on a second line.
Explain
What is a Comparator for?
Check
Sort.java prints the prices ascending then descending — checked (T3):
seat_run5.5Lab: the shopping list9 minchecked · T3
Predict
Items apple, pear, apple sorted with counts. How many lines before the distinct count?
Lesson
A TreeMap gives you sorted keys for free, getOrDefault counts duplicates, and the output rule is the spec: a plain name for one, "name xN" for more, then the distinct count. Lists, maps and sorting in one small tool.
Demo
The tutor writes java/jv050/demo/ListDemo.java reading args into a TreeMap<String, Integer>, prints each entry with the xN rule, then "Distinct: N", running it with apple pear apple.
Hands-on
Write java/jv050/s5/ShoppingList.java: items from args (lower-case them), one line per distinct item in alphabetical order — "<item>" for one, "<item> x<n>" for more — then "Distinct: <n>". With no args print "Nothing to buy".
Explain
Why did you pick a TreeMap over a HashMap here?
Check
ShoppingList.java prints sorted items with counts and the distinct total — checked (T3):
seat_runseat_run
Session 6 · Methods and tests
Five steps that move you into the Tally project for good. A static method with parameters and a return value, and why a method that only depends on its inputs is the easiest thing in the world to test. A record as a one-line data type with named fields. Your first JUnit 5 test in src/test/java, run with mvn -o -q test and read from the surefire report. Test first: write the failing test, make it pass, tidy. Live in the seat: a percentOf method extracted from the tip calculator, an Expense record printed and compared, a red test turning green, and a hidden test the grader adds that your method must also pass.
Lab In the Tally project write Budget.java with total(List<Expense>) and byCategory(List<Expense>) and the tests that prove them, including an empty list. The grader adds a hidden test. Session 6 checkpoint.
5 mini steps · ~39 min
6.1A method with inputs and a result7 minchecked · T3
Predict
static BigDecimal percentOf(BigDecimal amount, int pct) is called twice with the same inputs. Same result?
Lesson
A method names a piece of work: static BigDecimal percentOf(BigDecimal amount, int pct) takes inputs and returns a result. If it touches nothing outside its parameters it is pure, and pure methods are the easiest to test and reuse.
Demo
The tutor pulls the tip maths out of 3.5 into percentOf in java/jv050/demo-tally/src/main/java/tally/Calc.java, calls it from main twice, and shows that main got shorter and the maths now has a name.
Hands-on
In java/jv050/tally create src/main/java/tally/Calc.java with static BigDecimal percentOf(BigDecimal amount, int pct) that returns the percentage rounded HALF_UP to two places, and call it from App.main to print percentOf("48.00", 15).
Explain
What makes percentOf easy to test?
Check
Calc.percentOf exists and App prints 7.20 — checked (T3):
seat_fileseat_buildseat_run6.2Records: data with named fields7 minchecked · T3
Predict
record Expense(String category, BigDecimal amount) {} — what does new Expense("food", x).category() return?
Lesson
A record is a one-line data type: record Expense(String category, BigDecimal amount) {}. Java writes the constructor, the accessors, equals, hashCode and toString. Records are immutable, which is exactly right for a line of a ledger.
Demo
The tutor declares Expense in the demo project, creates two with the same values, prints one (the toString) and prints e1.equals(e2), then tries to assign to a field to show it cannot be changed.
Hands-on
In java/jv050/tally add src/main/java/tally/Expense.java as a record with String category and BigDecimal amount. In App.main create Expense("food", 12.50) and print it, then print whether it equals a second Expense with the same values.
Explain
Why is an immutable record a good fit for an expense line?
Check
Expense is a record and App prints it and true — checked (T3):
seat_fileseat_run6.3Your first JUnit test8 minchecked · T3
Predict
assertEquals(new BigDecimal("7.20"), Calc.percentOf(new BigDecimal("48.00"), 15)) — passes?
Lesson
A test is a method in src/test/java annotated @Test that arranges inputs, acts, and asserts the result. mvn -o -q test runs them all and writes a report to target/surefire-reports. One behaviour per test, named for what it proves.
Demo
The tutor writes CalcTest in the demo project with two tests for percentOf, runs mvn -o -q test, opens the surefire XML to show the pass count, then breaks a test on purpose and reads the failure message.
Hands-on
In java/jv050/tally add src/test/java/tally/CalcTest.java with tests percentOf_15_of_48 (expects 7.20) and percentOf_rounds_half_up (18% of 19.99 expects 3.60). Run mvn -o -q test and make both pass.
Explain
What are the three parts of a test, in order?
Check
CalcTest has two passing tests — checked (T3):
seat_test6.4Test first8 minchecked · T3
Predict
You write a test for a method that does not exist yet and run mvn test. What happens?
Lesson
Test first: write the test for the behaviour you want, watch it fail, write the smallest code that passes, then tidy. The test is the specification, and the red step proves the test can fail.
Demo
The tutor adds a test for Calc.share(BigDecimal total, int people) in the demo (100.00 among 3 is 33.33), sees the compile error, writes the method, sees green, then adds a case for 4 people and refactors.
Hands-on
In CalcTest add share_splits_evenly (100.00 among 4 → 25.00) and share_rounds_down (100.00 among 3 → 33.33), see them fail, then write static BigDecimal share(BigDecimal total, int people) in Calc using setScale(2, RoundingMode.DOWN).
Explain
Why run the test before writing the method?
Check
share tests pass alongside the percentOf tests — checked (T3):
seat_fileseat_test6.5Lab: the budget methods9 minproven · T4
Predict
byCategory of an empty list should return?
Lesson
Two pure methods over a list of records: total adds every amount; byCategory returns a Map<String, BigDecimal> of sums per category, sorted by key. Tests for each, including the empty list. The grader also runs a test you have not seen.
Demo
The tutor writes Budget.total in the demo project test-first, then byCategory with a TreeMap and merge, running the tests after each step and showing the empty-list test last.
Hands-on
In java/jv050/tally write src/main/java/tally/Budget.java with static BigDecimal total(List<Expense>) and static Map<String, BigDecimal> byCategory(List<Expense>) returning a TreeMap, and BudgetTest with total_of_three, total_of_empty_is_zero, byCategory_sums_per_key and byCategory_of_empty_is_empty. All green with mvn -o -q test.
Explain
Why do we test the empty list separately?
Check
Budget.total and byCategory pass your tests and the hidden test — proven (T4):
seat_fileseat_test
Session 7 · Files and errors
Five steps on the outside world. Reading a whole file with Files.readAllLines and a Path, writing one with Files.write, and where the file lands relative to the project. Exceptions: what a stack trace says, catching NoSuchFileException where you can act and letting the rest surface. try-with-resources with a BufferedReader so a file is closed even when a line is bad. CSV by split(","), the header row, and a bad line that must be reported, not swallowed. Live in the seat: a file read and counted, a file written and opened in the editor, a missing file crashing with a trace and then handled, a CSV with one broken line.
Lab In the Tally project write Ledger.load(Path) that reads expenses.csv into a List<Expense>, skipping the header and reporting bad lines to standard error, and a main that prints the total. A missing file prints one clear message and exits with code 2. Session 7 checkpoint.
5 mini steps · ~39 min
7.1Reading a file7 minchecked · T3
Predict
Files.readAllLines(Path.of("data/notes.txt")) returns?
Lesson
Path.of names a file relative to where the program runs; Files.readAllLines reads it into a List<String>. The method declares IOException, so main must declare throws IOException for now; session 7.3 handles it properly.
Demo
The tutor creates data/notes.txt in the demo project, reads it with Files.readAllLines, prints the line count and each line numbered, then runs from a different folder to show why the relative path matters.
Hands-on
Put a file java/jv050/tally/data/notes.txt with four lines. In a new class tally.ReadDemo read it with Files.readAllLines and print "Lines: 4" followed by each line prefixed with its number ("1: …").
Explain
Why does the relative path break when you run from another folder?
Check
ReadDemo prints the line count and numbered lines — checked (T3):
seat_fileseat_fileseat_run7.2Writing a file7 minchecked · T3
Predict
Files.write(path, lines) is run twice with different lines. What is in the file?
Lesson
Files.write(path, List<String>) writes lines and replaces the file; add StandardOpenOption.APPEND to add to the end. Files.createDirectories makes the folder first. Write, then open the file in the editor to see exactly what you produced.
Demo
The tutor writes a three-line summary to data/out/summary.txt in the demo, opens it in the editor, runs again with APPEND to show the lines doubling, then removes APPEND.
Hands-on
In tally.WriteDemo build a List<String> of three lines ("Tally summary", "Items: 3", "Total: 24.69"), create data/out if needed and write them to data/out/summary.txt. Print "Wrote data/out/summary.txt".
Explain
When would you want APPEND?
Check
WriteDemo writes a three-line summary file — checked (T3):
seat_runseat_file7.3Exceptions: catch where you can act8 minproven · T4
Predict
Files.readAllLines on a file that does not exist throws?
Lesson
An exception stops the method and travels up until something catches it; uncaught, it prints a stack trace and the program exits with code 1. Catch where you can do something useful (tell the user, exit cleanly) and let the rest surface.
Demo
The tutor runs the demo against a missing file and reads the trace top-down, then wraps the read in try/catch (NoSuchFileException e), prints one clear message to System.err and calls System.exit(2), and runs again.
Hands-on
In tally.SafeRead read the file named by args[0]. If it is missing, print "No such file: <path>" to System.err and exit with code 2; otherwise print the line count. Try it with data/notes.txt and with data/missing.txt.
Explain
Why exit with 2 rather than letting the trace print?
Check
SafeRead counts lines or exits 2 with a clear message — proven (T4):
seat_fileseat_run7.4try-with-resources and CSV lines8 minchecked · T3
Predict
"food,12.50".split(",") gives how many parts, and "food,".split(",")?
Lesson
try (BufferedReader r = Files.newBufferedReader(path)) { … } closes the reader whatever happens inside. Read line by line, skip the header, split on commas, check the part count, and turn each good line into a record.
Demo
The tutor reads data/expenses.csv in the demo with try-with-resources, skips the header, splits each line and builds Expense records, then adds a broken line to the file to show the part-count check reporting it.
Hands-on
Create java/jv050/tally/data/expenses.csv with a header category,amount and four rows (food 12.50, travel 30.00, food 7.25, books 15.99). In tally.CsvDemo read it with try-with-resources, build Expense records, and print each on its own line.
Explain
What does try-with-resources guarantee?
Check
CsvDemo reads four expenses with try-with-resources — checked (T3):
seat_fileseat_run7.5Lab: the saved ledger9 minchecked · T3
Predict
A CSV line reads "food,abc". What should Ledger.load do?
Lesson
Ledger.load(Path) returns List<Expense>: header skipped, each line split and validated, bad lines reported to stderr with their number and skipped, the reader closed by try-with-resources, and a missing file turned into exit code 2 by main.
Demo
The tutor builds Ledger.load in the demo, runs the demo main over expenses.csv for the total, then over a copy with a bad line to show the stderr report and the unchanged exit code 0, then over a missing file for exit 2.
Hands-on
In java/jv050/tally write tally.Ledger with static List<Expense> load(Path) as described, and make App.main take the CSV path from args[0], print "Total: <Budget.total>" and exit 0; missing file → "No such file: <path>" on stderr and exit 2. Add data/bad.csv with one invalid amount to test the skip.
Explain
Why report bad lines on stderr and not stdout?
Check
App totals a CSV, skips bad lines and exits 2 on a missing file — checked (T3):
seat_fileseat_runseat_runseat_run
Session 8 · Capstone: Tally
Five steps to finish Tally, a command-line expense tracker. Reading a specification and turning it into a list of behaviours before writing code. The skeleton: arguments, usage, and the exit-code contract 0 (all good), 2 (file problem), 3 (bad data). Parsing with validation: a negative amount or an unknown category is a reject with a reason. The report: a total, a sorted category table with printf, and the number of rejected lines. Live in the seat: the spec read aloud and turned into tests, a skeleton that does nothing but exit correctly, the parser growing a rule at a time with its tests, and the finished report over three different files.
Lab Tally complete: java -jar or exec:java with a CSV path and an optional --categories list, printing the report and exiting 0, 2 or 3 per the contract; your tests green; the grader's hidden CSV files and hidden test also pass. Session 8 checkpoint and the course placement step.
5 mini steps · ~43 min
8.1Reading the specification7 minchecked · T3
Predict
The spec says exit 3 when any line is rejected and exit 0 otherwise. A file has one bad line and nine good ones. Exit code?
Lesson
Before code, list behaviours. Tally's spec: input a CSV with category,amount; optional --categories a,b,c that restricts valid categories; report = Total, a category table, Rejected: N; exit 0 all good, 2 file problem, 3 any reject. Each sentence becomes a test name.
Demo
The tutor opens java/jv050/samples/s8/TALLY-SPEC.md, reads it, and writes a list of ten test names in the demo project's TallyTest as empty @Test methods with @Disabled, showing the spec becoming a checklist.
Hands-on
Read java/jv050/samples/s8/TALLY-SPEC.md. In java/jv050/tally create src/test/java/tally/TallyTest.java with at least eight @Disabled @Test methods whose names describe the behaviours (for example rejects_negative_amount, exits_3_on_any_reject).
Explain
Why write the test names before any code?
Check
TallyTest lists at least eight behaviours as disabled tests — checked (T3):
seat_fileseat_build8.2The skeleton and the exit-code contract8 minchecked · T3
Predict
main should compute an exit code and call System.exit once at the very end. Why not exit inside the helpers?
Lesson
A walking skeleton: Tally.run(String[] args) returns an int exit code and does no I/O beyond printing; main calls System.exit(run(args)). Usage with no args exits 2. Nothing else yet, and all of it tested.
Demo
The tutor writes Tally.run in the demo returning 2 with a usage line when args are empty, a test that asserts run(new String[0]) == 2, and main as a one-liner, then runs both the test and the program.
Hands-on
In java/jv050/tally write tally.Tally with static int run(String[] args): no args → print "Usage: tally <file.csv> [--categories a,b]" to stderr and return 2; a missing file → "No such file: <path>" and return 2. main is System.exit(run(args)). Enable and pass the tests usage_exits_2 and missing_file_exits_2.
Explain
What does the skeleton prove before any business logic exists?
Check
Tally.run returns 2 for usage and a missing file; main exits with it — checked (T3):
seat_fileseat_testseat_run8.3Parsing with validation9 minchecked · T3
Predict
Line "food,-5.00" with no --categories given. Accept or reject?
Lesson
Validation is a method that returns either an Expense or a reason: wrong part count, amount not a number, amount not positive, category not in the allowed list. Rejects carry the line number and reason; the loader counts them.
Demo
The tutor writes Parser.parse(int lineNo, String line, Set<String> allowed) in the demo returning a small result record, with tests for each rule, then wires it into the loader and shows a reject line on stderr.
Hands-on
In java/jv050/tally write tally.Parser with a record Parsed(Expense expense, String reason, int lineNo) and static Parsed parse(int lineNo, String line, Set<String> allowed) applying the four rules, with tests parses_good_line, rejects_wrong_part_count, rejects_non_positive_amount and rejects_unknown_category.
Explain
Why return a reason instead of throwing for a bad line?
Check
Parser applies the four rules and its tests pass — checked (T3):
seat_fileseat_test8.4Totals and the category report9 minchecked · T3
Predict
The category table must list categories alphabetically with amounts right-aligned. Which two tools from earlier sessions?
Lesson
Report.print(List<Expense> ok, int rejected): "Total: <sum>", one printf row per category sorted ("%-12s %10.2f%n"), then "Rejected: <n>". Build the text with a StringBuilder or return a String so the report itself is testable.
Demo
The tutor writes Report.render returning a String in the demo, a test comparing it with an expected block for three expenses, then prints it from run over data/expenses.csv.
Hands-on
In java/jv050/tally write tally.Report with static String render(List<Expense> ok, int rejected) producing Total, the sorted category rows with "%-12s %10.2f%n", and Rejected. Test render_three_expenses against the exact expected text, then print it from Tally.run.
Explain
Why return the report as a String rather than printing inside render?
Check
Report.render produces the exact report text — checked (T3):
seat_fileseat_testseat_run8.5Lab: Tally complete10 minproven · T4
Predict
The grader runs Tally on a CSV you have never seen with two bad lines. What must your output and exit code be?
Lesson
Finish the wiring: Tally.run parses args including --categories, loads with Parser, renders the Report, prints rejects to stderr with line numbers, and returns 0 or 3; file problems return 2. Run it over the five sample files and compare with the published outputs.
Demo
The tutor runs the demo Tally over java/jv050/samples/s8/{clean,rejects,empty,restricted,big}.csv, compares each with the expected files beside them, and shows the hidden-test idea: a sixth file nobody has seen.
Hands-on
Complete java/jv050/tally so that tally.Tally <file> [--categories a,b] prints the report and exits 0/2/3 per the spec, all your tests are enabled and green, and the five sample files produce the published expected outputs. Then run mvn -o -q package.
Explain
Which rule did a sample file catch that your tests had missed?
Check
Tally passes the published files, the hidden files and the hidden test — proven (T4):
seat_buildseat_testseat_run
Outcome
By the end of the course the tutor's checks will have verified: a compiled, running program for every hands-on step; eight session labs that each grow the last — the banner, the receipt, the tip calculator, the quiz, the shopping list, the budget methods with tests, the saved ledger; and the Tally capstone, whose totals, category report and exit codes 0/2/3 match the published expected values on CSV files you did not see.
Certificate
Agile System Software Certificate — Java from zero (JV050) attests that you can write, build, test and debug a small command-line Java program that reads and writes files, with methods, records, collections and an exit-code contract, verified by a machine from your own seat.
To earn it:
- Pass the lab check of every session (steps 1.5, 2.5, 3.5, 4.5, 5.5, 6.5 and 7.5) on your own code.
- Pass the Tally capstone check (8.5) including the hidden CSV files and the hidden JUnit test the grader supplies.
- Score 80% or better on the 20-question reading quiz.
Gates (step · tier required):
Plus a 20-question reading quiz at 80%, retake after 48 h. Certificates are issued once the seat check runner is live; until then every step records what the engine saw.
Environment
A VS Code seat in your browser with JDK 25, Maven and JUnit 5 ready and offline; single files run with java File.java, the Tally project builds with mvn -o. Your work lives in java/jv050; the tutor's demo files live apart from yours and the tutor never edits your hands-on file while you are working on it.
Open Coding Tutor at codingtutor.agilesystemsoftware.com, pick JV050 from the course button in the chat and press Tutor me. Every lesson, demo and hands-on step is also published below.