Polyglot
Languages Java syntax
Java § syntax

Syntax

The syntax of Java is defined by the Java Language Specification (JLS), maintained by the JCP and Oracle. The grammar resembles C++ at the surface and shares lineage with C and C++; the principal departures are the absence of pointer arithmetic, the requirement that every callable belong to a class, and the substantial set of access modifiers and contextual keywords. Java’s syntax has been intentionally conservative: extensions across each major revision have favoured readability and backward compatibility over expressive density. The current revision (Java 23 at the time of writing, with Java 25 designated as the next long-term-support release) admits substantial modern features — records, sealed classes, pattern matching, text blocks, switch expressions — while remaining recognisable as the Java that was first released in 1995.

This page covers the surface a working programmer encounters routinely. The dedicated pages cover the major sub-grammars (classes, interfaces, generics, the control-flow forms).

A complete program

The classical form, with a class containing the entry-point main method:

public class Greeter {
    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("Hello, world.");
        } else {
            for (String name : args) {
                System.out.printf("Hello, %s.%n", name);
            }
        }
    }
}

Compilation and execution:

javac Greeter.java       # produces Greeter.class
java Greeter alice bob   # executes the main method

Java 21 introduced unnamed classes and instance main methods as a preview, admitting a substantially shorter form for introductory programs:

void main() {
    System.out.println("Hello, world.");
}

The compiler synthesises an enclosing class and the conventional main(String[]) method. The form is intended for scripts and learning exercises; production code retains the explicit class declaration.

Source character set

Java source is interpreted as Unicode (typically UTF-8 with optional BOM). The basic source set includes the ASCII letters, digits, and conventional punctuation; identifiers may use any Unicode character whose category the JLS admits, though in practice most code restricts identifiers to ASCII. Unicode escapes (\uXXXX) are processed before lexical analysis, which produces the occasional surprise — a in the middle of a line becomes a newline that the rest of the lexer must accommodate.

Identifiers and keywords

An identifier consists of a Unicode letter or underscore followed by any number of letter, digit, or underscore characters. The keywords are reserved and may not be used as identifiers; the contextual keywords (such as var, record, yield, sealed, permits) are reserved only in specific syntactic positions.

The reserved keywords (Java 23):

abstract    continue    for           new         switch
assert      default     goto*         package     synchronized
boolean     do          if            private     this
break       double      implements    protected   throw
byte        else        import        public      throws
case        enum        instanceof    return      transient
catch       extends     int           short       try
char        final       interface     static      void
class       finally     long          strictfp    volatile
const*      float       native        super       while

The starred keywords (const, goto) are reserved but unused; the language reserves them to permit future use without breaking existing programs.

Java has no @-prefixed escape for using keywords as identifiers (as C# admits with @class); a Java program cannot use a reserved word as an identifier under any circumstances.

Comments

Three comment forms:

// a line comment

/* a block comment, possibly spanning multiple lines */

/**
 * A Javadoc comment. Tools extract these to produce documentation.
 *
 * @param input the input string
 * @return the processed result
 */

Javadoc comments — /** … */ immediately above a declaration — are the conventional form for API documentation. They are extracted by the javadoc tool and consumed by IDE tooling. Javadoc admits a small set of structured tags (@param, @return, @throws, @see, @since, @deprecated) and embedded HTML.

Declarations

Java declarations attach a name to a type, with optional modifiers. The principal forms:

// Field declaration
private final int counter = 0;

// Method declaration
public static int max(int a, int b) {
    return a > b ? a : b;
}

// Class declaration
public class Counter {
    private int value;

    public Counter(int initial) {
        this.value = initial;
    }
}

// Interface declaration
public interface Comparable<T> {
    int compareTo(T other);
}

// Enum declaration
public enum Direction {
    NORTH, EAST, SOUTH, WEST
}

// Record declaration (Java 14)
public record Point(double x, double y) { }

// Annotation declaration
public @interface Marker { }

Every declaration occurs at some enclosing scope: a class, an interface, an enum, a method, or a block. There are no top-level variables or top-level functions in Java (apart from the Java 21 preview unnamed classes, which the compiler implicitly wraps).

Statements

The statement grammar resembles C and C++ with several Java-specific additions:

expression;                            // expression statement
{ /* statements */ }                   // block

if (cond) statement                    // selection
if (cond) statement else statement
switch (expr) { /* cases */ }
switch (expr) { /* arrows */ };        // switch expression as a statement (Java 14+)

while (cond) statement                 // iteration
do statement while (cond);
for (init; cond; step) statement
for (Type x : iterable) statement      // enhanced for (since Java 5)

break;                                 // unconditional transfer
break label;
continue;
continue label;
return;
return expr;

throw expr;                            // exception
try { … } catch (…) { … } finally { … }
try (Resource r = …) { … }             // try-with-resources (since Java 7)

assert expr;                           // assertion
assert expr : message;

yield expr;                            // switch-expression value (since Java 14)

Each statement is terminated by a semicolon or, for compound statements, by a closing brace.

Type qualifiers and modifiers

Several modifiers refine declarations:

ModifierEffect
publicVisible from any code
protectedVisible to subclasses and same-package code
privateVisible only within the declaring class
(none — package-private)Visible within the same package
staticMember belongs to the type, not to instances
final(Field) cannot be reassigned; (method) cannot be overridden; (class) cannot be subclassed
abstract(Class) cannot be instantiated; (method) has no body
volatile(Field) reads and writes are not optimised across thread boundaries
transient(Field) excluded from default serialisation
synchronized(Method) acquires the instance/class monitor for the duration of the call
native(Method) implemented in non-Java code (typically through JNI)
strictfpFloating-point semantics are platform-independent (mostly redundant since Java 17)
default(Interface method) provides an implementation in the interface
sealed / non-sealed(Class, interface) restricts which types may extend / implement (Java 17)

Several modifiers are contextual — they have keyword status only in specific positions. record, sealed, permits, non-sealed, var, yield are contextual.

Storage and lifetime

Java does not have C/C++ storage-class specifiers. The lifetime of a value is governed by reachability:

  • Local variables are valid for the duration of the method (more precisely, for as long as they are reachable); the GC reclaims them when no references remain.
  • Instance fields live as long as the enclosing object.
  • Static fields live as long as the type — typically the entire program.
  • Allocated objects (instances of classes, arrays) live as long as references to them exist; they are reclaimed by the GC.

The combination of stack-allocated locals, GC-managed heap allocations, and the absence of explicit delete is the substance of the Java memory model; the full treatment is in Memory and the JVM.

Annotations

Annotations are declarative metadata attached to types, members, parameters, and packages. They are the Java mechanism for serialisation hints, validation rules, ORM mappings, dependency injection, and similar declarative configuration:

@Deprecated(since = "21", forRemoval = true)
public class OldApi { /* ... */ }

public class Document {
    @Override
    public String toString() { return title; }

    @SuppressWarnings("unchecked")
    public List<Object> get() { /* ... */ }

    @Nullable
    private String body;
}

Annotations are applied with @Name immediately before the declaration. Each annotation is a type derived from java.lang.annotation.Annotation; the @interface declaration introduces a new annotation type.

The Java standard library defines several attributes that the compiler recognises:

  • @Override — verifies the method overrides a superclass or interface method.
  • @Deprecated — marks the entity as deprecated; usage produces a diagnostic.
  • @SuppressWarnings — silences specified warnings.
  • @FunctionalInterface — verifies the interface has exactly one abstract method.
  • @SafeVarargs — suppresses unchecked-warning for varargs of generic types.

Frameworks define their own (@Autowired, @Entity, @Test, etc.); the runtime exposes them through reflection.

A note on undefined behaviour

Java has substantially less undefined behaviour than C or C++. Most operations are defined: integer overflow wraps (well-defined), array access throws ArrayIndexOutOfBoundsException, null dereference throws NullPointerException, type cast failure throws ClassCastException. The principal exceptions are:

  • JNI (Java Native Interface) admits calling native code, which has its own undefined-behaviour categories.
  • sun.misc.Unsafe and the related internal APIs admit raw memory access; these are formally not part of the language.
  • Concurrent unsynchronised access to non-volatile fields is governed by the Java memory model and may produce surprising visibility effects, though never the C-style undefined behaviour.

The discipline of writing correct Java is therefore largely the discipline of using the standard library correctly, handling exceptions appropriately, and respecting the concurrency rules. The compiler and runtime catch many errors that would compile silently in C; the cases that remain are typically logic errors and concurrency issues rather than memory-safety violations.