Java 25 Compact Source Files and Instance main Methods

Introduction The traditional first Java program needs a surprising amount of ceremony: a public class, a method declared public static void main(String[] args), and System.out.println to print a line. Beginners must type concepts - classes, access modifiers, static members, arrays - long before they can understand them, and even experienced developers carry that ceremony into every small script. After four previews from Java 21 to 24, Java 25Click to read tutorial: Overview of New Features in Java 25 (JEP 512) finalized compact source files and instance main methods. A source file can now contain just methods and fields, with a main method that need not be static, public or take any parameters, and a new IO class makes console input and output simple. This tutorial explains them with examples - the simplest programs, how a compact source file works, how the launcher chooses a main method, and how a small program grows into a normal class. The simplest Java program
Hello World in Java 25
//Hello.java
void main() {
  IO.println("Hello, Java 25!");
}
java Hello.java
 OUTPUT of the above command
Hello, Java 25!
That is a complete program: no class declaration, no public static, no String[] args. Compare it with the traditional Hello WorldClick to read: traditional Hello World, where every line needs explaining. The program runs directly from its source file with the source launcherClick to read tutorial: Java 22: Run Multi-File Programs Without Compiling, or can be compiled with javac as usual.
A compact source file with fields and methods A compact source file can contain any number of methods and fields, which can call and use each other, just like the members of a class -
Fields and methods in a compact source file
//Grades.java
final Map<String, Integer> marks = new TreeMap<>();     // a field: java.util is imported automatically

void main() {
  marks.put("Tom", 78);
  marks.put("Harry", 91);
  marks.put("Sam", 64);
  marks.forEach((name, mark) -> IO.println(name + ": " + mark + " -> " + grade(mark)));
  IO.println("Average: %.1f".formatted(average()));
}

String grade(int mark) {
  return mark >= 90 ? "A" : mark >= 75 ? "B" : "C";
}

double average() {
  return marks.values().stream().mapToInt(Integer::intValue).average().orElse(0);
}
 OUTPUT of the above code
Harry: 91 -> A
Sam: 64 -> C
Tom: 78 -> B
Average: 77.7
How it works
  • A source file whose top level contains methods or fields outside any class is a compact source file. The compiler wraps its contents in an implicitly declared class, named after the file - here Grades. The class is final, has a no-argument constructor, and cannot be referred to by name from other code.
  • A compact source file automatically imports the whole java.base module, using a module import declarationClick to read tutorial: Java 25 Module Import Declarations with Examples. That is why Map and TreeMap from java.util can be used without an import.
  • main() is an instance method: the launcher creates an instance of the implicit class and calls main() on it, so main() can use the instance field marks and call instance methods like grade() - no static needed anywhere.
The IO class: console input and output java.lang.IO, new in Java 25, has static methods for the console: println(), print(), and readln(prompt), which prints a prompt and reads a line - replacing the classic Scanner or BufferedReader setup for simple programs. Since IO is in java.lang, it can be used in any class, not only in compact source files -
Reading input with IO.readln
//Greet.java
void main() {
  String name = IO.readln("What is your name? ");
  int year = Integer.parseInt(IO.readln("Which year were you born? "));
  IO.println("Hello " + name + ", you will be " + (2025 - year) + " in 2025.");
}
 OUTPUT of the above code
What is your name? Tom
Which year were you born? 1995
Hello Tom, you will be 30 in 2025.
Instance main methods in ordinary classes Instance main methods are not limited to compact source files: any class can now declare void main(), with or without String[] args, and with any access other than private. When it launches a class, the JVM chooses the main method in this order -
  1. a main(String[] args) method - static or instance - is preferred,
  2. otherwise a main() method with no parameters is used.
If the chosen method is an instance method, the class must have a non-private constructor with no parameters, which the launcher calls first. The traditional public static void main(String[] args) still works exactly as before. Growing the program A compact source file is meant as an on-ramp, not a separate dialect of Java. When a program grows, wrapping its contents in class Grades { ... } and adding the imports it uses turns it into an ordinary class, with no other changes. More files can be added and run with the multi-file source launcherClick to read tutorial: Java 22: Run Multi-File Programs Without Compiling, and eventually the program can move to a build tool. Compact source files cannot declare a package, and other classes cannot refer to the implicit class by name. Summary In this tutorial we saw how Java 25's compact source files and instance main methods remove the ceremony from small programs. We wrote Hello World as a single void main() method, built a compact source file with fields and methods in an implicitly declared class which automatically imports java.base, and read console input with the new IO class. Lastly, we saw how the launcher chooses a main method, and how a compact source file grows into an ordinary class. Next, read about module import declarationsClick to read tutorial: Java 25 Module Import Declarations with Examples.