The Java main() method is the entry point of a Java program. When we run a class with the java command, the JVM calls main() first and passes the command-line arguments to it as a String array. The classic signature is public static void main(String[] args), and Java 25 also accepts shorter forms such as void main().
Every Java program that we start from a terminal, a script, a container or an IDE needs a main() method. Command-line tools, batch jobs and Spring Boot applications all begin there, and they read their startup options from the args array.
The following example prints an invoice header for the customer name passed as the first argument, with a default when no argument is given.
public class Invoice {
public static void main(String[] args) {
String customer = args.length > 0 ? args[0] : "guest";
System.out.println("Invoice for " + customer); // java Invoice.java Lokesh -> Invoice for Lokesh
}
}
The program checks args.length before it reads args[0], so it also runs without arguments. We go through the classic signature, the way the launcher finds main(), the instance main methods of Java 25, launcher errors, and the ways to pass, read and parse command-line arguments.
1. The Classic Java main() Method Signature
Up to Java 20, the launcher accepted only one form, and it is still the form that every JDK understands. Each word of public static void main(String[] args) answers a question the launcher has before it can start our code.
| Part | Why it is there |
|---|---|
| public | The launcher calls the method from outside the class, so the classic rules required public access. |
| static | The launcher calls the method without creating an object, so it needs no constructor arguments. The static keyword makes the method belong to the class. |
| void | The program does not return a value to the launcher. To report success or failure to the operating system, we call System.exit(code). |
| main | The fixed name the launcher looks for, borrowed from C and C++. |
| String[] args | The command-line arguments, one array element per argument. The array is empty, never null, when we pass no arguments. |
A few spellings of the same signature are legal, because they compile to the same method.
- The varargs form public static void main(String… args) is identical to String[] args for the launcher.
- The C-style array syntax String args[] compiles, but Java code reads better with String[] args.
- The parameter name is free, so String[] arguments works as well.
- Adding final or synchronized to the method changes nothing for the launcher.
2. How the JVM Launches main()
The java command is a small native program. It starts the JVM, loads the class we name, checks that the class has a usable main() method, builds the String[] from the remaining words on the command line and invokes the method. Java 25 widened the rules for a usable main() method with JEP 512.
![Flow of the java launcher, loading the class, building the args array, choosing main(String[]) before main(), and calling it on the class or on a new instance](https://howtodoinjava.com/wp-content/uploads/2026/10/java-main-method-launch-flow.png)
The launcher checks for main() before it initializes the class. A class with only a static block and no main() therefore fails with Main method not found, and the static block never runs. Before Java 7, that trick printed text from the static block before the error.
When main() returns, the JVM keeps running until all non-daemon threads finish. If main() throws an exception, the JVM prints the stack trace to standard error and exits with code 1.
Exception in thread "main" java.io.IOException: report.csv not found
at Boom.main(Boom.java:1)
3. Instance main() Methods in Java 25
Java 25 made instance main methods a standard feature. Since that release, a main() method may be non-static, may have package, protected or public access, and may drop the String[] parameter. Java 21 to 24 offered the same idea only as a preview behind –enable-preview.
The launcher picks the method in a fixed order, which matters when a class declares more than one candidate.
- A main(String[] args) method wins over a main() method without parameters.
- A private main() is ignored, and so is a main() with a return type other than void.
- A static main() is called on the class. For an instance main(), the launcher first creates an object with the non-private no-argument constructor.
- An inherited main() from a superclass also counts.
The following example is a regular class with an instance main method, so it can read an instance field without making it static.
class Report {
private final String title = "Daily report";
void main(String[] args) {
IO.println(title + ", arguments: " + args.length);
}
}
java Report.java daily
Daily report, arguments: 1
In a compact source file, the class declaration goes away as well, and the whole program can be a single void main() method, as the Java Hello World program shows. If the class has only a private constructor, the launcher cannot create the object and stops with an error.
Error: no non-private zero argument constructor found in class Report
remove private from existing constructor or define as:
public Report()
| Declaration | Java 20 and older | Java 25 |
|---|---|---|
| public static void main(String[] args) | Runs | Runs |
| static void main(String[] args) | Main method not found | Runs |
| public void main(String[] args) | Main method is not static | Runs on a new instance |
| void main() | Main method not found | Runs on a new instance |
| private static void main(String[] args) | Main method not found | Main method not found |
| public static int main(String[] args) | Main method not found | Main method not found |
Library code and anything that must run on Java 21 or older keeps the classic public static void main(String[] args) signature. The shorter forms fit small programs, scripts and teaching code that target Java 25 or later.
4. When the Launcher Cannot Find main()
A missing or unusable main() is a runtime error, not a compile error, because javac has no idea which class we plan to launch. A class with a private main(), a main() that returns int, or no main() at all compiles fine and fails only at launch.
Error: Main method not found in class Report, please define the main method as:
public static void main(String[] args)
or a JavaFX application class must extend javafx.application.Application
The message still suggests the classic signature, even on Java 25. A different message, Could not find or load main class, means the launcher did not find the class itself, which happens with a wrong class name, a wrong package or a missing classpath entry, as explained in the Java classpath article.
5. Passing Command-Line Arguments
Command-line arguments are the words that follow the class name, the source file name or the JAR file name in the java command. The launcher puts them into the args array in the same order, as strings.
A nightly job in a reporting app is a typical case. A scheduler runs java -jar reports.jar daily 2026-10-09, and the program reads the report type from args[0] and the date from args[1], so the same JAR serves daily, weekly and monthly runs.
The position of each word decides who reads it. Options before the class name, such as -Denv=prod or -Xmx1g, go to the JVM. Everything after the class name goes to our program, even when it looks like a JVM option. In the next run, we pass a system property and four program arguments.
public class ShowArgs {
public static void main(String[] args) {
System.out.println("env = " + System.getProperty("env"));
for (int i = 0; i < args.length; i++) {
System.out.println(i + ": [" + args[i] + "]");
}
}
}
java -Denv=prod ShowArgs.java daily "New York" "" -Xmx1g
env = prod
0: [daily]
1: [New York]
2: []
3: [-Xmx1g]
We can see that the quotes kept New York as one argument and that the empty quotes produced an empty string. The -Xmx1g after the class name did not change the heap size, it became a plain argument. Values meant for the JVM go before the class name and are read with system properties.
The shell processes the command line before Java sees it, so a few rules depend on the shell.
- Spaces separate arguments, and double quotes group words into one argument in every common shell.
- On Linux and macOS, the shell expands *.java into the matching file names. We write ‘*.java’ in single quotes to pass the asterisk itself.
- On Windows cmd.exe, only double quotes group words, and single quotes are ordinary characters.
- The total length of a command line has an operating system limit, e.g. 32,767 characters for a Windows process, so long lists of values belong in a file.
5.1. Reading and Parsing the args Array
Every argument arrives as a String, so numbers, dates and flags need parsing. Reading an index that does not exist, or parsing text that is not a number, throws an exception and ends the program with a stack trace.
String[] noArgs = {};
String first = noArgs[0]; // ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
int days = Integer.parseInt("seven"); // NumberFormatException: For input string: "seven"
The safe version checks the array length, strips stray spaces and falls back to a default value when the input is missing or invalid. The String to int conversion article covers the parsing methods in detail.
static int parseDays(String[] args, int defaultDays) {
if (args.length < 2) {
return defaultDays;
}
try {
return Integer.parseInt(args[1].strip());
} catch (NumberFormatException e) {
System.err.println("Not a number: " + args[1] + ", using " + defaultDays);
return defaultDays;
}
}
int given = parseDays(new String[] {"daily", " 7"}, 1); // 7
int missing = parseDays(new String[] {"daily"}, 1); // 1
int invalid = parseDays(new String[] {"daily", "seven"}, 1); // 1
For named options such as –env=prod, a small loop that splits each argument at the first = sign is enough for a few options.
static Map<String, String> parseOptions(String[] args) {
Map<String, String> options = new TreeMap<>();
for (String arg : args) {
int eq = arg.indexOf('=');
if (arg.startsWith("--") && eq > 2) {
options.put(arg.substring(2, eq), arg.substring(eq + 1));
}
}
return options;
}
Map<String, String> opts = parseOptions(new String[] {"--env=prod", "--days=7", "extra"}); // {days=7, env=prod}
String env = opts.getOrDefault("env", "dev"); // "prod"
String region = opts.getOrDefault("region", "eu"); // "eu"
Once a tool needs help text, required options, typed values and subcommands, a parsing library such as picocli saves a lot of hand-written code. In a Spring Boot application, the CommandLineRunner interface receives the same arguments after the application context starts.
5.2. Passing Arguments in an IDE or a Build Tool
An IDE starts the program for us, so the arguments go into the run configuration instead of a terminal. The field is named “Program arguments” in the IDEs, which keeps it apart from the field for JVM options.
| Tool | Where the arguments go |
|---|---|
| IntelliJ IDEA | Run, Edit Configurations, select the application, field Program arguments |
| Eclipse | Run, Run Configurations, Java Application, tab Arguments, field Program arguments |
| VS Code | The args array of the configuration in .vscode/launch.json |
| Maven (exec plugin) | mvn exec:java -Dexec.mainClass=com.example.Report -Dexec.args=”daily 7″ |
| Gradle (application plugin) | gradle run –args=”daily 7″ |
| Executable JAR | java -jar reports.jar daily 7, with the class taken from Main-Class in the manifest |
The last row matters for deployment. An executable JAR names its main class in META-INF/MANIFEST.MF, so java -jar needs no class name and passes every word after the JAR name to main().
6. Returning an Exit Code with System.exit()
A void main() cannot return a status, yet scripts, schedulers and CI pipelines decide what to do next from the exit code. By convention, 0 means success and any other value means failure, and System.exit(code) ends the JVM with that code.
public class Cleanup {
public static void main(String[] args) {
if (args.length == 0) {
System.err.println("Usage: java Cleanup <folder>");
System.exit(2);
}
System.out.println("Cleaning " + args[0]);
}
}
java Cleanup.java; echo "exit code: $?"
java Cleanup.java /tmp/reports; echo "exit code: $?"
Usage: java Cleanup <folder>
exit code: 2
Cleaning /tmp/reports
exit code: 0
We call System.exit() only in the outermost code of a command-line tool. Inside a library or a web application, it would stop the whole JVM, including every other request the server is handling.
7. Java main() Method FAQs
Java 25 changed the rules for main(), so several classic interview answers about its modifiers and the static keyword have a Java 25 exception.
7.1. Is it possible to overload main() in Java?
Yes. A class may declare main(int) or main(String, String) next to the real entry point. The launcher calls only main(String[]), or main() on Java 25 when no main(String[]) exists, and our code calls the other overloads like any method.
7.2. Can the main() method be private or final?
A final modifier is allowed and changes nothing. A main() method cannot be private, because the launcher ignores private main methods and reports Main method not found.
7.3. Why is main() static?
Because the launcher must call it before any object exists, and a static method needs no instance. Java 25 relaxes the rule, and for an instance main() the launcher creates the object with the no-argument constructor first.
7.4. Can a Java program run without a main() method?
Not from the java command. Code that runs inside a container, such as a servlet in Tomcat or a test in JUnit, has no main() of its own, because the container’s own main() starts it.
7.5. Can main() throw checked exceptions?
Yes, public static void main(String[] args) throws Exception is legal. An uncaught exception prints its stack trace and ends the program with exit code 1, so a command-line tool catches the errors it expects and prints a short message instead.
7.6. What is args when we pass no arguments?
An empty array with length 0, not null. Code can therefore check args.length without a null check.
8. Conclusion
The main() method is where every launched Java program starts. The classic public static void main(String[] args) runs on all versions, while Java 25 also accepts non-public, non-static and parameterless forms, preferring main(String[]) over main().
Command-line arguments arrive in args as strings, in order, after the class or JAR name. We check the length before reading them, parse numbers with a fallback, and quote values that contain spaces. JVM options go before the class name, and System.exit() reports success or failure to the calling script. The Java tutorial lists the next basics to cover.
9. References
- JLS 25, section 12.1.4, Invoke the main Method
- JEP 512, Compact Source Files and Instance Main Methods
- The java launcher reference
- System.exit(int) Javadoc
- picocli documentation
Happy Learning !!
Public : JVM must access it JVM is outside your class, so it needs a door (public) to call main().
Static : JVM calls it without creating an object No object is created before the program starts. static lets JVM call main() directly on the class.
void: No return value needed After starting, JVM doesn’t expect anything back from main() It just runs and that’s it.
Thanks for sharing.
A Java program is a sequence of Java instructions that are executed in a certain order. Since the Java instructions are executed in a certain order, a Java program has a start and an end.
I believe, the rational behind enforcing main as public is more to do with the language specification – rather than whether something could be achieved otherwise or not.
Reference
The method main must be declared public, static, and void. It must specify a formal parameter (§8.4.1) whose declared type is array of String. Therefore, either of the following declarations is acceptable:
Of course the JNI way to launch java application will never have any issue in calling a private main – but this is more like jail-brake (like another jail-brake, where reflection API let you access private method) and definitely not in spirit of java specification.
Agree. But I wonder that what could be in minds of writers of this specification when they wrote this.
Actually, if I remember till JDK 1.3 – it was not mandatory from developer’s perspective. i.e. even a private main was being accepted by JRE. Though, it was not inline with JLS 1.3.
I tried searching JLS 1.3 for a reference, but could not get a link. But I found that it was reported as a bug by developers across world:
Please refer: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4155575
So, a fix was made in subsequent version to enforce rule stated by JLS.
Now, the point is why JLS writer enforced this rule at first place – I really have no idea. The only thing I can think of is that – to keep it “obvious” and non-confusing for developers; which boils down to your paragraph starting with : Second way to answer this is another question, why not public….
Obviously, the next best convention could have been to keep it into default access scope; but definitely not in protected/private scope (which will simply create more “difficult” question – Why main should be private/protected…)
To add more; the JVM specification too has rules for JVM vendor; preventing them to write any incompatible JVM (which may allow a non-public main).
Reference: https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.2
The Java Virtual Machine starts up by creating an initial class, which is specified in an implementation-dependent manner, using the bootstrap class loader (§5.3.1). The Java Virtual Machine then links the initial class, initializes it, and invokes the public class method void main(String[]). The invocation of this method drives all further execution.
In this way Sun (now Oracle) closed all the door for any ambiguity.
Thanks and some assumptions
Thank u very much for sharing your knowledge…I m not familiar with applets but i defiantly sure that applet should also have any initial point like Servlet’s init() method that’s why they don’t need main() method.
You are right. Applet’s does have init(), start(), stop(), destroy() and paint() methods as it’s life cycle methods. JRE enabled on client browser automatically call these methods.
Do we always need main method to run java program
I believe not. We do have applets, where we do not write the main method. I still need to check how they are executed. If you already knew, please share with me.
My opinion is that any java source code which is not having main() method but correctly implements certain contracts like Applet, Swing, Servlet, EJB runs in a managed environment and has lifecycle callbacks which are automatically invoked by the runtime environments like servlet container(Servlet), browser having jre plugin installed(Applet), JNLP protocol(which also needs a suitable pre-installed JRE) for remote swing code or JRE for local swing code, J2EE server/app server for EJB.
Thanks for sharing your views. You are right.
/*
* Entry point.
*/
int
JLI_Launch(int argc, char ** argv, /* main argc, argc */
int jargc, const char** jargv, /* java args */
int appclassc, const char** appclassv, /* app classpath */
const char* fullversion, /* full version defined */
const char* dotversion, /* dot version defined */
const char* pname, /* program name */
const char* lname, /* launcher name */
jboolean javaargs, /* JAVA_ARGS */
jboolean cpwildcard, /* classpath wildcard*/
jboolean javaw, /* windows-only javaw */
jint ergo /* ergonomics class policy */
)
I went through this file java.c I show there is no main method i.e int main(int argc, char **argv, …). in java.c file they are saying JLI_Launch is entry point so i am little bit confused is it first entry point ? or some other method ? and that method is calling this method(JLI_Launch ) please clear it I am very curious to know when I write “java mainClass” command on cmd who is the first guy that receive these inputs ? and how it process ?
https://hg.openjdk.org/jdk7/jdk7/jdk/raw-file/jdk7-b24/src/share/bin/java.c
1st: Line 163: int JNICALL JavaMain(void * args); /* entry point */ Defined at line no. 308
Main invocation: mainClassName = GetMainClassName(env, jarfile); //Line no. 400
Really it’s very nice post thanks and please keep posting such type of information.
This was a nice post.loved it….but i still have a doubt about it if we do not use static keyword with main .we simply use public void main() in this case compilation succeeds but it causes run time exception thats a null pointer exception .Why this happens.????
Compilation succeeds because it is still a valid java class definition. Exception occurs because there is no entry point to program i.e. method what java.exe is expecting.
Its really great tutorial. Loved it.
java main method should b made public otherwise JVM cannot access it
Abhi JVM invokes the main method using native code and does not need access permissions to invoke this method. Even if main method is private, you call call it from native code without any problem.
These are really good points. The reference main methodand this article really completes everything there to know about main method