Classes, methods and blocks

The four things every Java program is made of, why one file can produce three class files, and the block that decides what exists.

5 min read Java Fundamentals

Every Java program is the same four things nested inside each other: a class holds methods, a method holds statements, and statements are grouped into blocks. Once you can name those in a file you have never seen, reading unfamiliar Java stops being decoding and starts being reading.

The smallest complete program, named part by part

Greet.javajava
public class Greet {                                   // a class
    public static void main(String[] args) {           // a method
        String who = "world";                          // a statement
        System.out.println("hello " + who);            // another
    }
}
  • class Greet — a named thing that holds code. In Java nothing lives outside one; there are no free-floating functions.
  • main — a method: a named block of statements you can call, with a list of parameters and a return type.
  • String who = "world"; — a statement. It ends with a semicolon, which is how the compiler knows where it stops. Newlines mean nothing to Java.
  • { ... } — a block. Every pair groups statements and, as the next section shows, decides what is visible.

The file is Greet.java because a public class must be in a file of its own name. That is a rule about public specifically; a non-public class can live anywhere.

One file does not mean one class

That last rule is often mis-learned as one file, one class. Compile a file with a nested class and an anonymous one in it:

Shape.javajava
public class Shape {
    static class Inner { }
    public static void main(String[] a) {
        Runnable r = new Runnable() { public void run() { } };
    }
}
plaintext
$ javac Shape.java
$ ls *.class
Shape$1.class   Shape$Inner.class   Shape.class

Three class files from one source file. The compiler's unit is the class, not the file, and the naming is worth recognising: Outer$Inner for a named nested class, Outer$1 for an anonymous one, numbered in the order they appear.

You will meet those exact names again in a stack trace. A frame reading OrderService$1.run is an anonymous class inside OrderService, and now it is a place you can find rather than a puzzle.

A block decides what exists

Braces are not only punctuation. Anything declared inside a block stops existing when the block ends:

java
{
    int insideTheBlock = 1;
    System.out.println("inside:  " + insideTheBlock);   // fine
}
System.out.println(insideTheBlock);                     // not fine
plaintext
Scope.java:4: error: cannot find symbol
        System.out.println(insideTheBlock);
                           ^
  symbol:   variable insideTheBlock

That is why a variable declared in a for loop's header is gone after the loop, and why declaring one inside an if and using it after does not compile. The rule is the same everywhere: a name lives from its declaration to the closing brace of the block it is in, and nowhere else.

It is also a tool. Declaring a variable in the smallest block that needs it is how you stop it being reused by accident twenty lines later — which the local variables lesson takes further.

Expressions and statements are different things

Worth separating early, because error messages assume you know:

  • An expression produces a value. 2 + 2, name.length(), age > 18.
  • A statement does something. An assignment, a method call used for its effect, an if, a return.

Some expressions can be statements on their own — a method call is the usual case. Most cannot: 2 + 2; is not a legal statement, and the compiler says not a statement, which reads as nonsense until you know it means this produces a value and does nothing with it.

The distinction is why if (x = 5) fails in Java and quietly works in C: x = 5 is an expression producing 5, an int where a boolean is required. Java's type system catches it; that is a deliberate design decision and not an accident.

Keywords and identifiers

Keywords are the roughly fifty words the language reserves — class, public, static, if, return, new, final. You cannot use one as a name; that is the entire rule, and the compiler enforces it immediately.

Three details worth carrying:

  • true, false and null are not keywords, they are literals. The effect is the same: you cannot name a variable null.
  • Some words are keywords only in context. var, record, sealed, yield and permits were added without breaking code that used them as names, so a variable called record still compiles. They are called contextual keywords.
  • goto and const are reserved and unused. They exist in the keyword list so that a program using them as names fails clearly rather than working and then breaking if the words are ever given meaning.

Identifiers — your names — may contain letters, digits, _ and $, and may not start with a digit. The mechanical rule is uninteresting; the convention is not, and the naming lesson covers what a reviewer actually reads. One piece of it belongs here: $ is legal and reserved by convention for generated code, which is why the compiler used it for Shape$Inner. Do not put it in a name you write.

Comments, and the two kinds worth writing

java
// to the end of the line
/* across
   several lines */
/** a documentation comment, read by javadoc and by your IDE */

The compiler discards all three. They exist for the next person, who is usually you.

The useful distinction is not the syntax, it is what the comment says:

java
// add one to i
i++;                       // noise: it repeats the code and rots when the code changes
 
// Retry three times: the payment provider returns 503 during their
// nightly maintenance window and recovers within ~2 seconds.

The second cannot be recovered from the code at any price. Comment the why, and the surprising: a workaround for somebody else's bug, a constant that came from a measurement, a decision that looks wrong and is not.

The third form, /** ... */, is different in kind because it is published. It becomes the tooltip in every caller's IDE, so it is written for someone who will never open your file: what the method does, what its parameters mean, what it returns, and what it throws.

Progress is saved on this device and to your account when signed in.