A .java file is plain text. The specification calls it a
compilation unit and describes it as a sequence of Unicode characters; how those
characters are stored as bytes, and how files are named and arranged in folders, is left
to the host system. This page describes both: what the language requires (JLS chapters 3
and 7) and what the javac compiler of OpenJDK expects.
Characters and encoding
The language is defined over Unicode characters, held as UTF-16 code units (JLS 3.1).
Characters outside the Basic Multilingual Plane, such as emoji, are two code units, a
surrogate pair. Which characters count as letters and digits in identifiers follows the
Unicode version of the JDK’s Character class, Unicode 17.0 since
JDK 26.
The bytes of the file are decoded by the compiler before the language sees them:
| Compiler | Encoding used when none is given | How to set it |
|---|---|---|
javac from JDK 18 | UTF-8, on every system (JEP 400) | javac -encoding windows-1252 … |
javac before JDK 18 | The platform’s default charset: often windows-1252 on Western Windows, UTF-8 on most Linux and macOS systems | javac -encoding UTF-8 … |
A file written on an older Windows system in a legacy code page can therefore fail to
compile, or compile with different string contents, on JDK 18 and later; the fix is to
pass its real encoding with -encoding, or to convert the file to UTF-8.
Build tools such as Maven and Gradle have their own settings for the source
encoding.
Any character can also be written in pure ASCII with a Unicode escape, such as
\u00e9 for é, so a file that uses only escapes reads the same in every
encoding.
How the compiler reads the text
The raw characters are translated in three steps (JLS 3.2), each working on the output of the one before:
- Unicode escapes (
\ufollowed by four hexadecimal digits) are replaced by the characters they stand for. This happens first, everywhere in the file, including inside comments and string literals. - The stream is split into lines at line terminators.
- The lines are split into input elements: white space, comments and tokens (identifiers, keywords, literals, separators and operators). These are described on the Lexical structure page.
At each step the longest possible translation is taken. So a--b is read
as a, --, b, which is not a valid expression,
even though a - -b would be. The one exception is the closing angle
brackets of type arguments: in List<List<String>> the
>> is split into two > tokens.
Unicode escapes
UnicodeEscape:
\ UnicodeMarker HexDigit HexDigit HexDigit HexDigit
UnicodeMarker:
u {u}
- Several
us are allowed:\uuuu0041isA. Tools that convert a file to ASCII add auto escapes that were already there, so the conversion can be undone. - A backslash only starts an escape if it is preceded by an even number of backslashes
(counting zero). In
"\\u2122"the second backslash is escaped by the first, so the string is the six characters\u2122. - A
\followed by one or moreus but not by four hexadecimal digits is a compile-time error, even in a comment. - An escape produces exactly one UTF-16 code unit. A supplementary character needs two
escapes, one for each half of the surrogate pair:
"\uD83D\uDE00".
Because escapes are replaced before anything else, they can change the structure of the program:
char newline = '\u000a'; // error: the escape becomes a real line break
char newline = '\n'; // correct: an escape sequence, handled later
// The comment below ends early, because \u000a is a line terminator:
// \u000a System.out.println("this line runs");
Line terminators
A line ends with a carriage return (CR, \u000d), a line feed (LF,
\u000a) or CR followed by LF, which counts as one terminator (JLS 3.4). All
three may be mixed in one file. Line numbers in error messages and in the
LineNumberTable of the compiled class count these terminators.
Inside a text block, every line terminator becomes a single LF in the string, whatever the file uses, so the value of a text block does not depend on how the file was saved.
As a concession to old systems, an ASCII SUB character (\u001a,
Ctrl-Z) is ignored if it is the very last character of the file (JLS 3.5).
The three kinds of compilation unit
CompilationUnit:
OrdinaryCompilationUnit
CompactCompilationUnit
ModularCompilationUnit
OrdinaryCompilationUnit:
[PackageDeclaration] {ImportDeclaration} {TopLevelClassOrInterfaceDeclaration}
CompactCompilationUnit:
{ImportDeclaration} {ClassMemberDeclarationNoMethod} MethodDeclaration {ClassMemberDeclaration}
ModularCompilationUnit:
{ImportDeclaration} ModuleDeclaration
Every compilation unit implicitly imports the public types of
java.lang, as if it began with import java.lang.*;
(JLS 7.3). An empty file is a valid ordinary compilation unit.
Ordinary compilation units
The usual kind. In order:
- An optional
packagedeclaration, possibly with annotations (which are allowed only inpackage-info.java). Without one, the classes are in the unnamed package. - Any number of
importdeclarations, of the five kinds described on Packages and modules. - Any number of top-level declarations: classes, interfaces, enum classes, record classes and annotation interfaces. Stray semicolons between them are allowed.
package com.example.shapes;
import java.util.ArrayList;
import java.util.List;
import static java.lang.Math.PI;
public sealed interface Shape permits Circle, Square {
double area();
}
record Circle(double r) implements Shape {
public double area() { return PI * r * r; }
}
record Square(double side) implements Shape {
public double area() { return side * side; }
}
com/example/shapes/Shape.java: one public type and two package-private onesCompact compilation units (Java 25)
A compact compilation unit is a file of class members with no class around them (JLS 7.3 and 8.1.8; JEP 512). It is meant for small programs and for learning. It may hold fields, methods, nested classes and interfaces, in any order after the imports, and must hold at least one method.
String greeting = "Hello";
void main() {
String name = IO.readln("Your name: ");
IO.println(greeting + ", " + name);
List<Integer> squares = Stream.of(1, 2, 3).map(n -> n * n).toList();
IO.println(squares);
}
List and Stream need no import.The compiler wraps the members in an implicitly declared class, which:
- is a top-level,
finalclass in the unnamed package, with package access, extendingObjectand implementing nothing; - is named by the host system, in practice after the file (
Hello.javagivesHello), but that name cannot be used in source code, so no other class can refer to it; - has only the default constructor: the file cannot declare constructors, instance initialisers or static initialisers;
- must have a candidate
mainmethod (see Execution), which may be an instance method with no parameters; - imports the whole
java.basemodule, as if the file began withimport module java.base;, sojava.util,java.io,java.timeand the rest are available without imports.
IO is the class java.lang.IO (Java 25), with the static
methods print(Object), println(Object),
println(), readln(String) and readln(). Its
methods are not imported implicitly; calls are written IO.println.
From Java 21 to 24 this was a preview feature, first called unnamed classes, then implicitly declared classes and then simple source files.
Modular compilation units: module-info.java
A module is described by one compilation unit holding a module
declaration, by convention in a file named module-info.java at the root
of the module’s source tree (JLS 7.7). The name is not a valid class name, so the file
cannot be mistaken for a class. It compiles to module-info.class.
import com.example.spi.Plugin;
/** The shapes library. */
module com.example.shapes {
requires transitive java.desktop;
requires static com.example.annotations;
exports com.example.shapes;
exports com.example.shapes.internal to com.example.tests;
opens com.example.shapes.model;
uses Plugin;
provides Plugin with com.example.shapes.ShapePlugin;
}
module-info.java using every kind of directiveEvery directive is explained on Packages and modules.
package-info.java
Annotations on a package, and the package’s documentation comment, go in a file named
package-info.java in the package’s folder (JLS 7.4.1 recommends this
scheme). Only one annotated package declaration is allowed for each
package. The file holds the documentation comment, the annotations, the
package declaration and any imports the annotations need, and no
classes; package-info is not a legal class name. It compiles to
package-info.class.
/**
* Shapes and their areas.
*/
@NullMarked
package com.example.shapes;
import org.jspecify.annotations.NullMarked;
com/example/shapes/package-info.javaFile names and folders
The specification leaves storage to the host. When packages are stored in a file
system, it allows the compiler to require that a type is in a file named after it, with
an extension such as .java, if the type is public or is used
from other files (JLS 7.6). javac enforces this for public types:
| Rule | Example |
|---|---|
| A public top-level type must be in a file of the same name, matching case | public class Toad in Toad.java |
| So a file has at most one public top-level type | Others in the file are package-private |
The folders normally follow the package name; javac needs this to find sources on the source path | wet.sprocket.Toad in wet/sprocket/Toad.java |
A module’s declaration is in module-info.java | src/com.example.shapes/module-info.java |
Package annotations are in package-info.java | wet/sprocket/package-info.java |
Package names are conventionally lower case and start with a reversed internet domain
(com.example), so the folder names are lower case too.
What the compiler writes
javac writes one .class
file for every class and interface in the source, including nested ones. The names
below are those javac chooses; the language only fixes the binary names of
member types (JLS 13.1).
Declaration in Outer.java | Class file |
|---|---|
Top-level class Outer | Outer.class |
Member class Outer.Inner | Outer$Inner.class |
| The first anonymous class | Outer$1.class |
Local class Helper in a method | Outer$1Helper.class |
| A module declaration | module-info.class |
| A package declaration with annotations | package-info.class |
Lambda bodies are compiled into private methods of the enclosing class, not into class files of their own.
Running a source file directly
The java launcher can compile and run source without a separate
javac step:
| Since | Command | What it does |
|---|---|---|
| Java 11 (JEP 330) | java Hello.java | Compiles the file in memory and runs the first top-level class’s main |
| Java 11 (JEP 330) | #!/usr/bin/java --source 25 as the first line of a file without the .java extension | Makes the file an executable script on Unix-like systems; the launcher skips the first line |
| Java 22 (JEP 458) | java Main.java | Also compiles the other source files that Main.java refers to, found in the same folder tree |
The #! line is not Java; a file that starts with one cannot be compiled by
javac.