Java 25: The Future of Coding, Now
A new version of Java is released every six months. Java 25 came out on September 16, 2025.
1. Instance Main Methods
Now in Java 25, You no longer need public static void main(String[] args) for simple programs. You can write main without static, public, or even the String[] args if you don’t need them.
class Test{
void main() {
System.out.println("Hello, World!");
System.out.println("This is testing file");
}
}
Java application launch follows a strict sequence to find a valid main
method. The launcher's selection process is as follows:
-
Priority to Standard Main: The JVM first looks for a traditional
public static void main(String[] args)
method, which is the most common entry point. -
Fallback to Parameterless Main: If the standard method isn't found, it checks for a
public static void main()
method (with no parameters). -
Instance Main Method Handling: If only a non-static (instance)
main
method exists, the JVM must instantiate the class. This requires the class to have a public, no-argument constructor. The launcher creates an instance using this constructor and then calls the instance'smain
method. -
Error Condition: If no valid
main
method is found, or if an instancemain
exists without a required constructor, the launcher throws an error and terminates.
2. Compact source files
Now in Java 25, You can write a standalone Java source file without declaring a class explicitly (no class Hello { … } needed). The compiler will assume there is an implicit class behind the scenes.
Now, you can create a file with just your methods and variables. The Java compiler will automatically place them inside a hidden, or "unnamed," class for you. This means you can focus on learning the basics of programming without first needing to understand advanced concepts.
How to Write a Compact File
Simply create a .java
file and write your code directly in it. You must have a main
method, as it's the program's starting point.
Example: Hello, World! Made Simple
void main() {
System.out.println("Hello, World!");
}
You can also use methods and fields you define right in the same file:
String message = "Hello, World!";
void main() {
printMessage();
}
void printMessage() {
System.out.println(message);
}
Key Things to Know About Unnamed Classes:
-
The class is created automatically and is final (cannot be extended).
-
It has a default, no-argument constructor you can't see.
-
You can use the
this
keyword inside the file to refer to the current instance. -
The class name is generated by the compiler and is not for you to use in your code.
- Must have a launchable
main
method; if it does not, a compile-time error is reported.
3. Simplifying Java Console Input and Output for Beginners
Writing to and reading from the console is a fundamental first step for new programmers. Traditionally, Java made this more complex than necessary. New features now make basic console interaction much simpler.
Reading user input was even harder. The standard code required understanding advanced concepts like BufferedReader, InputStreamReader, and handling IOException with try/catch blocks. This boilerplate code was a significant barrier to writing simple interactive programs.
The New Solution: The IO Helper Class
To solve this, Java introduces a new java.lang.IO class. It provides simple, static methods for all basic console operations, eliminating the need for complex setup.
import static java.lang.IO.*;
void main() {
println("Hello, World!");
}
For input
import static java.lang.IO.*;
void main(){
var name=readln("what is your name?");
println("Hello, " + name);
}
Key Methods of the IO
Class:
-
IO.print("text")
- Prints text without a new line. -
IO.println("text")
- Prints text and moves to the next line. -
IO.println()
- Just prints a blank line. -
IO.readln()
- Pauses and reads a line of input from the user. -
IO.readln("Prompt: ")
- Displays a prompt and then reads input.
Since the IO
class is in the fundamental java.lang
package, no import
statement is needed. This works in any Java program, from simple unnamed classes to full traditional applications.
4. Automatic imports of common APIs
In compact source files, classes from commonly used packages in java.base (like java.util, java.io) are automatically available. You don’t always have to write import statements for them.
void main(){
IO.println("Hello, World!");
}
5.Flexible Constructor Bodies
Java 25 allows constructor bodies to have statements before calling super(...) or this(...). Normally, those calls must be the very first line.
These early statements (called the prologue) cannot use the object under construction (no this or accessing instance methods/fields that are already initialized). But you can initialize uninitialized fields or do safe computations.
class Parent{
Parent(){
IO.println("Parent class constructor");
}
}
class Child extends Parent{
Child(){
IO.println("This is child class constructor");
super();
}
public static void main(String args[]){
new Child();
}
}
output:
This is child class constructor
Parent class constructor
Key Rules:
- Statements before the super(...) or this(...) call are called the prologue; those after are the epilogue.
- In the early part (prologue), you cannot use this or any instance methods/fields except to initialize uninitialized fields in that class.
- return statements are allowed in the epilogue (but not with an expression), not in the prologue.
- For record classes, enums, and nested classes, similar rules apply, with some existing constraints still in place.
6. Module Import Declarations
This feature adds a new way to import all packages that a module exports by using a single declaration:
import module M;
This helps reduce the need to write many import statements for individual packages, especially from modular libraries.
Before this feature:
import java.util.*;
import java.sql.*;
import javax.sql.*;
import java.nio.file.*;
public class MyApp {
// you have to import each needed package separately
Path path = java.nio.file.Paths.get("file.txt");
Map<String, String> map = new HashMap<>();
Connection conn = DriverManager.getConnection(...);
// and so on
}
After (with module import)
import module java.base;
import module java.sql;
public class MyApp {
Path path = Paths.get("file.txt"); // from java.base
Map<String, String> map = new HashMap<>(); // from java.base
Connection conn = DriverManager.getConnection(...); // from java.sql
}
7. Scoped Values
A scoped value is a new way in Java to share immutable data (data that doesn’t change) across methods in a thread and child threads, without passing that data explicitly all the time.
It is meant to be simpler and more efficient than using ThreadLocal.
Scoped values have a limited lifetime: once the scope ends, the binding (assignment) ends too.
Declaration:
You declare a scoped value (usually static final) of some type.
static final ScopedValue<FrameworkContext> CONTEXT = ScopedValue.newInstance();
Binding a value:
Use ScopedValue.where(...).run(...) (or .call(...)) to bind a value for that scope. While inside that run block, methods (direct or indirect) can read the scoped value via .get().
where(CONTEXT, context).run(() -> {
// inside here, CONTEXT.get() works in this thread & its callees
Application.handle(request, response);
});
Lifetime:
The binding is only valid during the execution of that run block (or call). Once run finishes, the binding is removed.
LoggerExample
import java.lang.ScopedValue;
import static java.lang.ScopedValue.where;
public class LoggerExample {
// Define a ScopedValue for request ID
static final ScopedValue<String> REQUEST_ID = ScopedValue.newInstance();
public static void main(String[] args) {
handleRequest("REQ-101");
handleRequest("REQ-202");
}
static void handleRequest(String id) {
// Bind the request ID for this request
where(REQUEST_ID, id).run(() -> {
process();
log("Request completed!");
});
}
static void process() {
log("Processing data...");
// Imagine more deep calls here
saveToDB();
}
static void saveToDB() {
log("Saving to database...");
}
static void log(String message) {
// Access the bound request ID
System.out.println("[" + REQUEST_ID.get() + "] " + message);
}
}
output:
bash-5.1# java LoggerExample.java
[REQ-101] Processing data...
[REQ-101] Saving to database...
[REQ-101] Request completed!
[REQ-202] Processing data...
[REQ-202] Saving to database...
[REQ-202] Request completed!