Java syntax practice — the enrolment card, the FP2 timetable and a receipt with three errors (to stop fearing javac)
The first Java assignment I handed in had two compilation errors. I fixed them, compiled again feeling pleased with myself, and javac gave me back… a new error that hadn’t been there before. I thought I’d broken something while fixing the others. I hadn’t: javac checks in rounds, and the third error had been there from the start, waiting for the other two to let it through. You’ll see it with real output in program 3.
In the Java syntax theory we saw the structure of a program: class, main, braces, semicolons, println, print, printf and concatenation. Now we write three programs from scratch, medium level and with nothing outside this topic (no variables, no if, no loops, no Scanner: those come in the next topics). The thread is the card of an FP2 student: her enrolment lined up in columns with printf, her timetable in a text table and the receipt for what she pays. All the data are made up, and the outputs are real, with Java 21.
Table of Contents
Before you start
It helps to be clear about:
- The minimal structure:
public class Name { public static void main(String[] args) { … } }, and that the file is calledName.java. - The difference between
println,printandprintf, and the placeholders%s,%d,%.2fand%n. - That
+sticks texts together from left to right and that brackets change the order. - How to compile and run:
javac Name.javaandjava Name.
If any of these sounds shaky, review the theory before carrying on.
Create a syntax_practice folder and one file per program, named exactly after its class: EnrolmentCard.java, Timetable.java and Receipt.java. If you use VS Code, the Run button compiles and runs; if you prefer the terminal, javac and java.
Program 1: the enrolment card with printf
We want to print the second-semester enrolment card: the student’s details, a table of subjects with their code and their credits (ECTS) in columns, and the amount to pay. What makes it tricky: the subject names have very different lengths and the numbers have to be right-aligned, like in a real table.
The idea
No counting spaces by hand: each column is a printf placeholder with a width. %-6s is a text in 6 columns stuck to the left; %-30s, one in 30; %5d, a whole number in 5 columns stuck to the right. If every row uses the same format, the columns line up by themselves. The amounts are worked out inside the printf itself (24 × 15.50): we don’t have variables yet, so the numbers are written as they are.
Before programming, add up the widths of a row (6 + 1 + 30 + 1 + 5) and check that the header and the lines of dashes measure the same.
The code
/** Enrolment card with printf: aligned columns and the Spanish decimal comma (made-up data). */
public class EnrolmentCard {
public static void main(String[] args) {
System.out.println("============================================");
System.out.println(" ENROLMENT CARD · SECOND SEMESTER");
System.out.println("============================================");
System.out.printf("%-12s %s%n", "Student:", "Laura Santana Ruiz");
System.out.printf("%-12s %s%n", "Degree:", "GCID");
System.out.printf("%-12s %dst%n", "Year:", 1);
System.out.println("--------------------------------------------");
System.out.printf("%-6s %-30s %5s%n", "Code", "Subject", "ECTS"); // header with the same widths
System.out.printf("%-6s %-30s %5d%n", "FP2", "Programming Fundamentals II", 6);
System.out.printf("%-6s %-30s %5d%n", "IC2", "Computer Engineering II", 6);
System.out.printf("%-6s %-30s %5d%n", "EST", "Statistics", 6);
System.out.printf("%-6s %-30s %5d%n", "ALG", "Algebra", 6);
System.out.println("--------------------------------------------");
System.out.printf("%-37s %5d%n", "Total credits", 6 + 6 + 6 + 6);
System.out.printf("%-30s %10.2f €%n", "Price (24 x 15.50 €)", 24 * 15.50);
System.out.printf("%-30s %10.2f €%n", "Admin fee", 27.30);
System.out.printf("%-30s %10.2f €%n", "To pay", 24 * 15.50 + 27.30);
System.out.println("============================================");
}
}
Output
============================================
ENROLMENT CARD · SECOND SEMESTER
============================================
Student: Laura Santana Ruiz
Degree: GCID
Year: 1st
--------------------------------------------
Code Subject ECTS
FP2 Programming Fundamentals II 6
IC2 Computer Engineering II 6
EST Statistics 6
ALG Algebra 6
--------------------------------------------
Total credits 24
Price (24 x 15.50 €) 372.00 €
Admin fee 27.30 €
To pay 399.30 €
============================================
Let’s check it. Credits: 6 + 6 + 6 + 6 = 24. Price: 24 × 15.50 = 372.00 €. Total: 372.00 + 27.30 = 399.30 €. On a computer set to Spanish the decimals would come out with a comma (399,30), because printf uses the system’s language. And the ECTS column lines up with the header because the five rows have the same format.
Questions you might ask
Why %-37s on the total line? Because we want the 24 to land right under the ECTS column. The subject rows take 6 + 1 + 30 = 37 characters before the credits placeholder, so the text “Total credits” has to take those same 37.
What happens if a name is longer than its placeholder? printf doesn’t cut it: it prints it whole and pushes everything else along. “Programming Fundamentals II” has 27 characters; with %-20s that row would be shifted seven columns.
Why does 1st come out right with %dst? Because everything that isn’t a placeholder is copied as it is: %d puts the 1 and st goes after it.
Can I force a particular decimal separator? Yes: System.out.printf(java.util.Locale.US, "%.2f%n", 399.30) always prints 399.30, whatever the computer’s language. Use it if a statement demands the point.
Why is the € outside the placeholder? Because the placeholder only formats the number. %10.2f € reserves 10 columns for the amount and then writes a space and the symbol.
Each row using a different width (%-30s in one, %-28s in another). The columns go crooked and it looks like a mysterious bug. Copy the whole format from one row to the next.
Try it yourself
- Add a fifth subject worth 4.5 credits. Which placeholder do you have to change for the number to fit? (hint:
%5ddoesn’t work for decimals). - Change the format so the credits column is stuck to the left and watch it fall apart.
Program 2: the FP2 timetable in a text table
Now the weekly FP2 and IC2 timetable drawn with characters: +, - and |. We do it in three ways: with spaces counted by hand, with printf and with tabs (\t). What makes it tricky: “Wednesday” has 9 letters and “Monday” 6, so something has to fill the difference.
The idea
- By hand: each line is a
printlnwith the spaces already written. It works, but if you change a word you have to count again. - With
printf:%-9sfills up to 9 columns, so “Monday” and “Wednesday” take the same space without counting anything. - With
\t: the tab jumps to the next column that is a multiple of 8. It looks convenient, but you’ll see what happens with “Wednesday”.
Before programming, count how many letters the longest word in each column has. That’s the minimum width of the placeholder.
The code
/** FP2 and IC2 timetable in a text table: by hand with spaces and with printf (made-up timetable). */
public class Timetable {
public static void main(String[] args) {
// 1. By hand: every space counted
System.out.println("+-----------+-------+-------+");
System.out.println("| Day | 9:00 | 11:00 |");
System.out.println("+-----------+-------+-------+");
System.out.println("| Monday | FP2 | IC2 |");
System.out.println("| Wednesday | IC2 | FP2 |");
System.out.println("| Friday | FP2 | - |");
System.out.println("+-----------+-------+-------+");
System.out.println();
// 2. With printf: %-9s sets the width and there is nothing to count
System.out.printf("| %-9s | %-5s | %-5s |%n", "Day", "9:00", "11:00");
System.out.printf("| %-9s | %-5s | %-5s |%n", "Monday", "FP2", "IC2");
System.out.printf("| %-9s | %-5s | %-5s |%n", "Wednesday", "IC2", "FP2");
System.out.printf("| %-9s | %-5s | %-5s |%n", "Friday", "FP2", "-");
System.out.println();
// 3. With \t: the tab jumps to column 8, 16, 24…
System.out.println("Day\t9:00\t11:00");
System.out.println("Monday\tFP2\tIC2");
System.out.println("Wednesday\tIC2\tFP2");
System.out.println("Friday\tFP2\t-");
}
}
Output
+-----------+-------+-------+ | Day | 9:00 | 11:00 | +-----------+-------+-------+ | Monday | FP2 | IC2 | | Wednesday | IC2 | FP2 | | Friday | FP2 | - | +-----------+-------+-------+ | Day | 9:00 | 11:00 | | Monday | FP2 | IC2 | | Wednesday | IC2 | FP2 | | Friday | FP2 | - | Day 9:00 11:00 Monday FP2 IC2 Wednesday IC2 FP2 Friday FP2 -
The first two tables are identical: %-9s has put in the same spaces we counted by hand. The third goes crooked right at Wednesday. Why? “Monday” has 6 letters and its \t jumps to column 8; “Wednesday” has 9, it’s already past column 8, so its \t jumps to 16. Tabs only line things up if every word in a column has fewer than 8 characters.
Questions you might ask
Why does "9:00" go in quotes and not as a number? Because it’s a text with a colon inside; and that way the same %-5s placeholder works as for “FP2”.
Do you have to write the | in the format? Yes: everything that isn’t a placeholder is printed as it is. "| %-9s | %-5s | %-5s |%n" puts bars and spaces around each value.
Why doesn’t the second table have the +---+ borders? So you compare just the rows; add them with println and you’ll see they line up just the same.
So I never use \t? It’s for separating columns that another program will read later (for example, to paste into a spreadsheet), but not for lining things up on screen.
Aligning with \t and testing only with short words. It works until “Wednesday” shows up. For tables, printf with widths.
Try it yourself
- Add the 13:00 column to the first two tables.
- Change
%-9sto%9sin the day column. Which way do the names stick now?
Program 3: a receipt with three errors, fixed round by round
The last program prints the enrolment receipt, but we write it with three errors of the kind you see most in the first labs, and we fix them by reading what javac says. What makes it tricky: javac doesn’t show them all at once.
The idea
Compile, read the first error, fix it, compile again. Always in that order: a writing error can hide the ones below it, and sometimes fixing the first makes others disappear because they were consequences of it.
Before compiling, read the code below and look for the three errors yourself. Then compare with what javac finds.
The code
Version with errors:
public class BadReceipt {
public static void main(String[] args) {
System.out.println("Enrolment receipt")
System.out.println("Item: 24 credits);
System.out.printn("Total: " + 399.30 + " €");
}
}
Round 1, javac BadReceipt.java:
BadReceipt.java:3: error: ';' expected
System.out.println("Enrolment receipt")
^
BadReceipt.java:4: error: unclosed string literal
System.out.println("Item: 24 credits);
^
2 errors
Two writing errors: the ; on line 3 is missing and the quotes on line 4 never close (unclosed string literal: the text starts and doesn’t end). We fix them and compile again (saved as BadReceipt2.java, so you can see both rounds):
public class BadReceipt2 {
public static void main(String[] args) {
System.out.println("Enrolment receipt");
System.out.println("Item: 24 credits");
System.out.printn("Total: " + 399.30 + " €");
}
}
Round 2:
BadReceipt2.java:5: error: cannot find symbol
System.out.printn("Total: " + 399.30 + " €");
^
symbol: method printn(String)
location: variable out of type PrintStream
1 error
An error that wasn’t there before! cannot find symbol … method printn(String): there’s no printn method. javac first checks that the text is well written (quotes, semicolons, braces) and only when everything is well written does it look up the names (methods, classes). With the writing errors it never reached this stage.
Fixed version, with the total properly formatted:
public class Receipt {
public static void main(String[] args) {
System.out.println("Enrolment receipt");
System.out.println("Item: 24 credits");
System.out.printf("Total: %.2f €%n", 24 * 15.50 + 27.30); // printf: locale decimal separator
System.out.println("Total with +: " + (24 * 15.50 + 27.30) + " €"); // +: always with a point
}
}
Output
Enrolment receipt Item: 24 credits Total: 399.30 € Total with +: 399.3 €
Look at the last two lines: the same sum, 24 × 15.50 + 27.30, comes out 399.30 with printf and 399.3 with +. printf gives you the decimals you ask for (and the separator of the computer’s language: on a Spanish system, 399,30); + converts the number as it is, always with a point and no padding zeros. For money, printf.
Questions you might ask
Why didn’t the printn error show up in round 1? Because javac works in stages: first it reads the text and, if there are writing errors, it stops there. Names are checked in a later stage.
Why does the arrow of unclosed string literal point at the opening quote? Because that’s where the text that never closes begins. The real error is at the end of the line, where the other quote is missing.
What do symbol and location mean in the message? symbol is the name it can’t find (printn with a String) and location is where it looked: in out, which is a PrintStream. Translated: “System.out has no printn method”.
And if I fix one error and more appear? That’s normal, as you’ve just seen. What isn’t normal is the number of errors shooting up after a small change: then you’ve opened a quote or a brace that you don’t close.
Trying to fix all the errors at once starting from the last. Always start with the first: the following ones are often consequences of it.
Try it yourself
- Remove a closing brace from the fixed
Receiptand read the error. Which line does it point to? - Change
%.2fto%din the total’sprintf. Does it compile? What happens when you run it?
Common mistakes in this practice
- Using different widths in rows of the same table. The columns go crooked.
- Aligning with
\t. It only works if every word in the column has fewer than 8 letters. - Forgetting
%nat the end ofprintf. The next output sticks to the same line. - Fixing
javacerrors from the last one. Start with the first and compile again. - Thinking
javacreports every error at once. Name errors come after writing errors. - Printing money with
+. You get399.3instead of399.30.
The error that will appear most in these programs
The semicolon. In the three programs there are lots of almost identical lines and it’s easy for one to slip. Remember: ';' expected points at the line with the error or the one below, and lines ending in { or } don’t take one.
Watch it step by step in Python Tutor
Go to Python Tutor, choose Java and paste this short version of the card:
public class Tutor {
public static void main(String[] args) {
System.out.printf("%-6s %-12s %5s%n", "Code", "Subject", "ECTS");
System.out.printf("%-6s %-12s %5d%n", "FP2", "Programming", 6);
System.out.printf("%-6s %-12s %5d%n", "IC2", "Computers", 6);
System.out.printf("%-19s %5d%n", "Total", 6 + 6);
System.out.printf("To pay: %.2f €%n", 12 * 15.50);
}
}
Output:
Code Subject ECTS FP2 Programming 6 IC2 Computers 6 Total 12 To pay: 186.00 €
What to watch step by step:
- Each
printfadds a whole line to the output panel: none is left halfway because they all end in%n. - “Programming” (11 letters) and “Computers” (9) fit in
%-12s; change one to “AI” and watch it fill up with spaces. 6 + 6inside theprintfis a sum (12), because it’s a separate argument, not a piece of text.- The last line works out 12 × 15.50 = 186. On a computer set to Spanish it would print 186,00.
Summary and next step
printfwith widths (%-30s,%5d,%10.2f) lines up tables without counting spaces.- The minus sign sticks to the left; without it, to the right.
\tjumps to columns that are multiples of 8: it doesn’t line up long words.javacchecks in rounds: first the writing, then the names.- Always fix the first error and compile again.
- For amounts,
printf(two decimals);+gives the number as it is.
Checklist: have you practised it?
- Have you run the three programs and checked the outputs?
- Can you work out how wide a
printfrow is by adding up its widths? - Can you explain why Wednesday makes the
\ttable crooked? - Have you read
javac‘s messages until you understood what each little arrow points at? - Have you tried the “Try it yourself” tasks without looking at the theory?
If you answered yes to almost everything, you’re ready for the Java syntax exercises, where I no longer give you the code. See you there!
Next step: exercisesJava syntax exercises — the converter, the grade report and the cash machine receipt, lined up to the last character (with solutions and a cheat sheet)Did this article help you? Here is how you can support SergioLearns →




IC2 practical mock exam — flowcharts, fork and threads in C (and the screenshots you can’t leave out)