Command Palette

Search for a command to run...

PHASE 11Intermediate Java 25+ ~30 min· topic 5 of 6

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 main method without static. The launcher makes an object first and then calls main on it.
Launcher
The java command. It loads your class, finds the main method and starts the program.
Source-file mode
Running java Hello.java directly: the launcher compiles the file in memory and runs it, with no separate javac step (Java 11).
Unnamed package
The package a class belongs to when its file has no package line. Fine for small programs and experiments.
IO
A Java 25 class in java.lang with simple static methods: IO.println(x), IO.print(x) and IO.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.

Hello.javawhole filejava
// 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!");
}
terminal
$ java Hello.java # JDK 25
── expected output ──
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.

What the compiler builds from a compact filediagram
Rendering diagram…

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.

terminal
$ javac Main.java
── expected output ──
Main.java:5: error: non-static variable greeting cannot be referenced from a static context
System.out.println(greeting);
^
1 error

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.

Todo.javawhole filejava
// 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");
}
terminal
$ java Todo.java # JDK 25
── expected output ──
1. buy milk
2. learn records
3. call Asha
3 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:

terminal
$ javac Main.java && java Main # JDK 21, class with only void main()
── expected output ──
Error: Main method not found in class Main, please define the main method as:
public static void main(String[] args)
or a JavaFX application class must extend javafx.application.Application

Try it yourself

  1. 1

    Remove the hand-written launcher step

    If you have JDK 25, save the first example as Main.java, delete the public static void main(String[] args) method, and run java Main.java. Predict the output first. On Java 17, the same change gives "Main method not found".

  2. 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, public and String[] args, and switch to IO.println. Compare with the Java 25 block.

  3. 3

    Grow it back

    Wrap your compact file in class Todo { ... }. Compile it on JDK 25 and read the error about List. Add the imports it needs. Notice that void main() can stay as it is.

Code & diagrams

What an instance main means, written out for Java 17 New tab

On Java 25 you could delete the static main entirely and the launcher would do the same two steps.

Sign in to run this example in your browser.

Expected output

Welcome to Chai Point
sold 2, total 2
sold 3, total 5
The classic version of a small program New tab
Sign in to run this example in your browser.

Expected output

1. buy milk
2. learn records
3. call Asha
3 tasks
The same program as a Java 25 compact source file Java 25+java

Save 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");
}
Reading input with IO.readln (Java 25, run locally) Java 25+java

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.

terminal
$ javac Hello.java # JDK 21
── what you'll see ──
Hello.java:1: error: unnamed classes are a preview feature and are disabled by default.
void main() {
^
(use --enable-preview to enable unnamed classes)
1 error

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).

terminal
$ javac Main.java
── what you'll see ──
Main.java:5: error: non-static variable greeting cannot be referenced from a static context
System.out.println(greeting);
^
1 error

Break #3

Use IO on an older JDK

Write IO.println("hi"); and compile with JDK 21.

terminal
$ javac Main.java # JDK 21
── what you'll see ──
Main.java:3: error: cannot find symbol
IO.println("hi");
^
symbol: variable IO
location: class Main
1 error

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 ordinary final class 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.java a 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 write import module java.base; yourself (Java 25, JEP 511). Module imports can create ambiguity, e.g. List from both java.util and java.awt when importing java.desktop too; a single-type import resolves it.

  • ▸

    The IO class changed during the previews: it first appeared in Java 23 as java.io.IO, with its methods statically imported into compact files so you could write a bare println(...). Java 25 moved it to java.lang and dropped the static import, so you write IO.println(...). Tutorials written for JDK 23 or 24 previews may not compile on 25 as-is.

Remember this

  1. 1

    The classic first program needs public class Hello, public static void main(String[] args) and System.out.println. Each word is a concept (classes, access modifiers, static, arrays, fields of System) that a beginner meets long before they understand it. Java 25 (JEP 512) lets you write void main() { IO.println("Hello, world!"); } in Hello.java and run it with java Hello.java.

  2. 2

    A compact source file is a .java file whose top level contains fields and methods that aren't inside any class. The compiler implicitly declares a class around them: a final class in the unnamed package, extending Object, with a default constructor, named after the file. Your code can't refer to that class by name, and it must contain a main method, because the only thing you can do with it is run it.

  3. 3

    An **instance main method** is a main that isn't static and may have no parameters and no public. To run it, the launcher creates an object of the class with its no-argument constructor and calls main() on that object. So fields and other methods can be used directly, without the classic beginner error non-static variable cannot be referenced from a static context. Instance main methods also work in ordinary, explicitly declared classes.

  4. 4

    How the launcher picks main: if the class has a main(String[]) method (static or not, and not private) it uses that; otherwise it uses a main() with no parameters. If the chosen method is an instance method, the class needs a non-private no-argument constructor. The classic public static void main(String[] args) still works everywhere and still takes priority, so no existing program changes behaviour.

  5. 5

    Two helpers come with it. The new class **java.lang.IO** has static print, println and readln methods for simple console output and input, and like everything in java.lang it needs no import. And a compact source file automatically imports every public top-level type from the packages that the java.base module exports, so List, Map, ArrayList and friends work without import lines (as if it began with import module java.base;, the module import declaration also finalised in Java 25 by JEP 511).

  6. 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

01

What is a compact source file, and what does the compiler do with it?

02

What is an instance main method and how does the launcher run it?

03

In what order does the Java 25 launcher choose a main method?

04

Why was this feature added, and does it change existing programs?

Practice

01

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.

02

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.

03

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 main avoids 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.base are 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 main and 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.