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.

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 called Name.java.
  • The difference between println, print and printf, and the placeholders %s, %d, %.2f and %n.
  • That + sticks texts together from left to right and that brackets change the order.
  • How to compile and run: javac Name.java and java 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.

printf widthsA row of the enrolment card: %-6s takes 6 columns left-aligned with FP2, %-30s takes 30 with the subject name and %5d takes 5 right-aligned with the 6.printf("%-6s %-30s %5d%n", "FP2", "Programming Fundamentals II", 6)FP2%-6sleft, 6Programming Fundamentals II%-30sleft, 306%5dright, 5043The spaces between placeholders are printed too: 6 + 1 + 30 + 1 + 5 = 43 columns.%.2f prints 8.75 in English and 8,75 on a computer set to Spanish (decimal comma).
The number in each placeholder is its width: with the minus sign the text sticks to the left; without it, to the right.

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: %5d doesn’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 println with the spaces already written. It works, but if you change a word you have to count again.
  • With printf: %-9s fills 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 %-9s to %9s in 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.

Errors arrive in roundsThree javac rounds with the receipt: first two writing errors (semicolon and quotes), then a name error (printn does not exist) and finally it compiles.Round 1: BadReceipt.javaline 3: ';' expectedline 4: unclosed stringwriting errorsRound 2: 3 and 4 fixedline 5: cannot findsymbol printnname errorsRound 3: printlnit compilesand runsno errorsjavac first checks the text is well written and only then looks up the names
javac saying "2 errors" does not mean there are only two: name errors show up once you fix the writing ones.

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 Receipt and read the error. Which line does it point to?
  • Change %.2f to %d in the total’s printf. 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 %n at the end of printf. The next output sticks to the same line.
  • Fixing javac errors from the last one. Start with the first and compile again.
  • Thinking javac reports every error at once. Name errors come after writing errors.
  • Printing money with +. You get 399.3 instead of 399.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:

  1. Each printf adds a whole line to the output panel: none is left halfway because they all end in %n.
  2. “Programming” (11 letters) and “Computers” (9) fit in %-12s; change one to “AI” and watch it fill up with spaces.
  3. 6 + 6 inside the printf is a sum (12), because it’s a separate argument, not a piece of text.
  4. The last line works out 12 × 15.50 = 186. On a computer set to Spanish it would print 186,00.

Summary and next step

  • printf with widths (%-30s, %5d, %10.2f) lines up tables without counting spaces.
  • The minus sign sticks to the left; without it, to the right.
  • \t jumps to columns that are multiples of 8: it doesn’t line up long words.
  • javac checks 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 printf row is by adding up its widths?
  • Can you explain why Wednesday makes the \t table 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 →