Topic 11.5
Compact Source Files and Instance main Methods
In one line
From Java 25 a complete program can be just void main() { IO.println("Hello"); }: no class declaration, no public static, no String[] args. The compiler wraps the file in a hidden class, the launcher creates an object and calls main on it, and common java.base packages are imported automatically.
Think of it like this
A child's first cycle has training wheels. They aren't a different, toy cycle: they're the same frame, pedals and brakes, with a little help bolted on that you remove later. A compact source file is Java with training wheels: the same language, minus the ceremony a beginner doesn't need on day one, and you take the wheels off by wrapping the code in a class.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Compact source file
- A Java 25 source file with methods and fields at the top level, not inside any class. The compiler adds the class for you.
- Implicitly declared class
- The hidden class the compiler creates around a compact source file. It has no name you can use in code.
- Instance main method
- A
mainmethod withoutstatic. The launcher makes an object first and then callsmainon it. - Launcher
- The
javacommand. It loads your class, finds themainmethod and starts the program. - Source-file mode
- Running
java Hello.javadirectly: the launcher compiles the file in memory and runs it, with no separatejavacstep (Java 11). - Unnamed package
- The package a class belongs to when its file has no
packageline. Fine for small programs and experiments. - IO
- A Java 25 class in
java.langwith simple static methods:IO.println(x),IO.print(x)andIO.readln(prompt). - Module import
import module java.base;imports all public types of all packages a module exports in one line (Java 25).
Step by step
01Hello, world: then and now
The classic program has five concepts before the one line that does the work. The Java 25 version keeps only the method and the line that prints.
Both are real Java. The compact one is not a scripting dialect: it compiles to a normal class file and runs on the normal JVM.
// Classic (every Java version)
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
// Java 25: the whole file
void main() {
IO.println("Hello, world!");
}02What the compiler builds from a compact file
When javac sees methods or fields outside any class, it wraps the whole file in an implicitly declared final class named after the file (Hello.java gives a class Hello). Everything you wrote becomes a member of that class.
Because the class is anonymous to your code, nothing else can call its methods or create it. It is a program, not a library: it must contain a main method, and it can't declare a package.
03Instance main: fields and methods without static
With a classic static main, a beginner who adds a field and uses it from main gets non-static variable cannot be referenced from a static context. The usual fixes are to make everything static (bad habit) or to write new Main().run() (a concept they haven't learned).
An instance main removes the trap: the launcher does the new Main() part. Inside main, this is a real object, so fields and instance methods just work. The first runnable example below writes that launcher step out by hand so you can see it on Java 17.
04A small program with state, Java 25 style
A compact file can hold fields, several methods, and even nested classes or records. List and ArrayList need no import, because compact files import everything java.base exports.
When the program grows, "take the training wheels off": put the members inside class Todo { ... }, add the imports you use, and keep going. An instance main is still allowed in an explicit class.
// Java 25 compact source file: no class, no imports
List<String> todo = new ArrayList<>();
void add(String task) {
todo.add(task);
}
void show() {
for (int i = 0; i < todo.size(); i++) {
IO.println((i + 1) + ". " + todo.get(i));
}
}
void main() {
add("buy milk");
add("learn records");
add("call Asha");
show();
IO.println(todo.size() + " tasks");
}05Which main runs?
The launcher looks for a non-private main(String[] args) first, static or instance. Only if there isn't one does it look for a main() with no parameters. If the method it picks is an instance method, it calls the class's non-private no-argument constructor first.
So a class with both public static void main(String[] args) and void main() runs the classic one, exactly as before Java 25. Existing programs are unaffected.
06On older JDKs
JDK 21 to 24 had this as a preview, under older names, and refuse a compact file unless you pass --enable-preview. JDK 17 and earlier don't know the syntax at all. Also, before Java 25 a launcher that finds only an instance main reports that no main method exists:
Try it yourself
- 1
Remove the hand-written launcher step
If you have JDK 25, save the first example as
Main.java, delete thepublic static void main(String[] args)method, and runjava Main.java. Predict the output first. On Java 17, the same change gives "Main method not found". - 2
Convert a program
Take the classic to-do example and convert it to a compact source file yourself: delete the class line and closing brace, the imports, every
static,publicandString[] args, and switch toIO.println. Compare with the Java 25 block. - 3
Grow it back
Wrap your compact file in
class Todo { ... }. Compile it on JDK 25 and read the error aboutList. Add the imports it needs. Notice thatvoid main()can stay as it is.
Code & diagrams
On Java 25 you could delete the static main entirely and the launcher would do the same two steps.
Expected output
Welcome to Chai Point
sold 2, total 2
sold 3, total 5Expected output
1. buy milk
2. learn records
3. call Asha
3 tasksSave as Todo.java and run java Todo.java on JDK 25. Same output as the classic version.
List<String> todo = new ArrayList<>();
void add(String task) {
todo.add(task);
}
void show() {
for (int i = 0; i < todo.size(); i++) {
IO.println((i + 1) + ". " + todo.get(i));
}
}
void main() {
add("buy milk");
add("learn records");
add("call Asha");
show();
IO.println(todo.size() + " tasks");
}Keyboard input can't run in the browser. Run it in a terminal on JDK 25.
void main() {
String name = IO.readln("What is your name? ");
int cups = Integer.parseInt(IO.readln("How many cups of chai? "));
IO.println("Hello, " + name + "! That's " + (cups * 20) + " rupees.");
}Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Compile a compact file on JDK 21
Save void main() { System.out.println("Hello, world!"); } as Hello.java and compile with JDK 21.
Break #2
Use a field from a static main
In a classic class, declare String greeting = "Namaste"; and print it from public static void main(String[] args).
Break #3
Use IO on an older JDK
Write IO.println("hi"); and compile with JDK 21.
Myth vs fact
Myth
Compact source files are a separate scripting language or a different Java.
Fact
They're ordinary Java. The compiler adds a class around the members and produces a normal class file; you can grow the program into a regular class at any time.
Myth
public static void main(String[] args) is deprecated now.
Fact
It's still valid everywhere and takes priority when both forms exist. Java 25 only adds simpler options.
Myth
An instance main method is called without creating an object.
Fact
The launcher creates one first with the no-argument constructor, then calls main() on it. That's why fields and instance methods are usable.
Myth
Compact files can be used as libraries by other classes.
Fact
No. The implicitly declared class can't be referenced by name from other code. It exists only to be launched.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
The launch protocol lives in the launcher (
sun.launcher.LauncherHelper), not the JVM specification: the JVM itself still just invokes methods. That's why the change needed no bytecode or class-file changes; the implicitly declared class is an ordinaryfinalclass whose name comes from the file name. - ▸
Source-file mode (Java 11, JEP 330) compiles in memory and runs; Java 22 (JEP 458) extended it to programs of several source files, compiling other files from the same directory tree on demand. With compact source files, that makes
java Main.javaa complete workflow for small tools without a build system. - ▸
The automatic
import module java.base;applies only to compact source files. In an ordinary class you can writeimport module java.base;yourself (Java 25, JEP 511). Module imports can create ambiguity, e.g.Listfrom bothjava.utilandjava.awtwhen importingjava.desktoptoo; a single-type import resolves it. - ▸
The
IOclass changed during the previews: it first appeared in Java 23 asjava.io.IO, with its methods statically imported into compact files so you could write a bareprintln(...). Java 25 moved it tojava.langand dropped the static import, so you writeIO.println(...). Tutorials written for JDK 23 or 24 previews may not compile on 25 as-is.
Remember this
- 1
The classic first program needs
public class Hello,public static void main(String[] args)andSystem.out.println. Each word is a concept (classes, access modifiers, static, arrays, fields ofSystem) that a beginner meets long before they understand it. Java 25 (JEP 512) lets you writevoid main() { IO.println("Hello, world!"); }inHello.javaand run it withjava Hello.java. - 2
A compact source file is a
.javafile whose top level contains fields and methods that aren't inside any class. The compiler implicitly declares a class around them: afinalclass in the unnamed package, extendingObject, with a default constructor, named after the file. Your code can't refer to that class by name, and it must contain amainmethod, because the only thing you can do with it is run it. - 3
An **instance
mainmethod** is amainthat isn'tstaticand may have no parameters and nopublic. To run it, the launcher creates an object of the class with its no-argument constructor and callsmain()on that object. So fields and other methods can be used directly, without the classic beginner errornon-static variable cannot be referenced from a static context. Instance main methods also work in ordinary, explicitly declared classes. - 4
How the launcher picks
main: if the class has amain(String[])method (static or not, and notprivate) it uses that; otherwise it uses amain()with no parameters. If the chosen method is an instance method, the class needs a non-private no-argument constructor. The classicpublic static void main(String[] args)still works everywhere and still takes priority, so no existing program changes behaviour. - 5
Two helpers come with it. The new class **
java.lang.IO** has staticprint,printlnandreadlnmethods for simple console output and input, and like everything injava.langit needs no import. And a compact source file automatically imports every public top-level type from the packages that thejava.basemodule exports, soList,Map,ArrayListand friends work withoutimportlines (as if it began withimport module java.base;, the module import declaration also finalised in Java 25 by JEP 511). - 6
The feature was a preview for four releases under different names: Java 21 ("unnamed classes and instance main methods", JEP 445), 22 ("implicitly declared classes", JEP 463), 23 (JEP 477) and 24 ("simple source files", JEP 495), before becoming standard in Java 25. This course's runner uses Java 17, so the Java 25 programs here are shown as code to run locally, next to runnable Java 17 versions that do the same thing the classic way.
Explain it without notes
What is a compact source file, and what does the compiler do with it?
What is an instance main method and how does the launcher run it?
In what order does the Java 25 launcher choose a main method?
Why was this feature added, and does it change existing programs?
Practice
Write the Java 25 compact-file version of a program that prints the first 10 Fibonacci numbers on one line using a helper method fib(int n). Then write and run the Java 17 version.
A class has both public static void main(String[] args) printing "static" and void main() printing "instance". What does java Main print on Java 17, and on Java 25? Write it and run it on Java 17.
Rewrite this Java 17 class as an explicit class with an instance main method that runs on Java 25 (keep the class declaration): a class Counter with a field int n, a method inc(), and a main that calls inc() three times and prints n.
Trade-offs
- ↔
Compact source files are ideal for learning, scripts and quick experiments, but they live in the unnamed package and can't be referenced by other classes. Anything that grows beyond one file or needs tests should become ordinary classes in packages.
- ↔
Instance
mainavoids the static trap, but a production application's entry point usually stays the classic static form: frameworks, build tools and older JDKs all expect it, and it makes the start-up path obvious. - ↔
Automatic imports of all of
java.baseare convenient, but they hide where types come from and can clash with your own type names. Explicit imports document dependencies better in larger code.
Done when you can
Done when you can write and run a compact source file on JDK 25.
Done when you can explain what class the compiler creates and what you can't do with it.
Done when you can explain how the launcher runs an instance
mainand the order it chooses methods.Done when you know which JDK versions accept this and what older ones report.
Done when you can grow a compact file into a normal class.