Agile System SoftwareLearn

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.

  • LevelBeginner
  • Durationself-paced ~22–26 h
  • Size8 sessions · 40 mini steps · ~320 min of lessons
  • EnvironmentA 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
  • PrerequisitesNone. A browser and about half an hour per sitting.
  • CertificateAgile System Software Certificate — Java from zero (JV050)

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).

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. 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_file seat_run

    #s1-1

  2. 1.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_run

    #s1-2

  3. 1.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_run

    #s1-3

  4. 1.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_file seat_build seat_run

    #s1-4

  5. 1.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_file seat_run

    #s1-5

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
  1. 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_file seat_run

    #s2-1

  2. 2.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_run

    #s2-2

  3. 2.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_file seat_run

    #s2-3

  4. 2.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_file seat_run

    #s2-4

  5. 2.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_file seat_run

    #s2-5

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
  1. 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_run

    #s3-1

  2. 3.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_run

    #s3-2

  3. 3.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_file seat_run

    #s3-3

  4. 3.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_run

    #s3-4

  5. 3.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_file seat_run seat_run

    #s3-5

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
  1. 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_run

    #s4-1

  2. 4.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_file seat_run

    #s4-2

  3. 4.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_file seat_run

    #s4-3

  4. 4.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_file seat_run

    #s4-4

  5. 4.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_file seat_run seat_run seat_run

    #s4-5

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
  1. 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_file seat_run

    #s5-1

  2. 5.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_file seat_run

    #s5-2

  3. 5.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_file seat_run

    #s5-3

  4. 5.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_run

    #s5-4

  5. 5.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_run seat_run

    #s5-5

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
  1. 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_file seat_build seat_run

    #s6-1

  2. 6.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_file seat_run

    #s6-2

  3. 6.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_test

    #s6-3

  4. 6.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_file seat_test

    #s6-4

  5. 6.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_file seat_test

    #s6-5

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
  1. 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_file seat_file seat_run

    #s7-1

  2. 7.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_run seat_file

    #s7-2

  3. 7.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_file seat_run

    #s7-3

  4. 7.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_file seat_run

    #s7-4

  5. 7.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_file seat_run seat_run seat_run

    #s7-5

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
  1. 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_file seat_build

    #s8-1

  2. 8.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_file seat_test seat_run

    #s8-2

  3. 8.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_file seat_test

    #s8-3

  4. 8.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_file seat_test seat_run

    #s8-4

  5. 8.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_build seat_test seat_run

    #s8-5

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.