Java Module 1 – Start here: JDK 25 LTS and your first programs
Classic main, javac and .class files
Write the classic public static void main(String[] args), compile it with javac -d, run classes from the class path, and see which main JDK 25 picks.
What you will learn
- Write the classic public static void main(String[] args) entry point and explain each keyword
- Compile with javac -d and run a class from the class path with command-line arguments
- Run a class in a package by its full name from the folders javac builds
- Explain how the JDK 25 launcher chooses among main methods, and when the classic form is needed
Before you start
What you’ll learn · Before you start
What you will learn
- Write the classic public static void main(String[] args) entry point and explain each keyword
- Compile with javac -d and run a class from the class path with command-line arguments
- Run a class in a package by its full name from the folders javac builds
- Explain how the JDK 25 launcher chooses among main methods, and when the classic form is needed
Before you start
On this page
Before JDK 25, every Java program started from the same method declaration, public static void main(String[] args),
usually in a public class named after its file. That classic form still runs on every JDK, it is how almost all
existing code and textbooks start a program, and it is what online judges compile. This lesson explains each word of
it, then the two-step way of running it:
javac -d out Hello.java compiles the source into a class file in the folder out, and java -cp out Hello runs
that class from the class path. It ends with the rules that the JDK 25 launcher follows when a class offers more than
one main method.
Every word of the classic main has a job
Here is a classic program. It does one thing more than the compact files of the last lesson: it prints the arguments given to it on the command line.
public class Hello {
public static void main(String[] args) {
System.out.println("Hello from a classic main");
System.out.println("Arguments received: " + args.length);
for (String arg : args) {
System.out.println(" " + arg);
}
}
} Read the two declarations word by word:
public class Hellodeclares a class namedHellothat code anywhere may use. Apublictop-level class must live in a file with the same name, hereHello.java.public, in front ofmain, lets the launcher, which is outside the class, call the method.staticmakes the method belong to the class itself, so it can be called without creating an object first.voidsays that the method returns no value.mainis the name the launcher looks for.String[] argsis the one parameter: an array of strings that holds the command-line arguments: The words typed after the class name, as in java -cp out Hello Asha Pune. The launcher passes them to main as the array String[] args, which is empty, never null, when there are none., and is empty when there are none.
System.out.println is the classic way to print a line: System.out is the standard output stream, and println
writes a value and ends the line. It does the same job as IO.println, and you will meet both. The for loop prints
each argument in turn; module 3 explains loops. Step through one launch of this class with two arguments, and see what
the launcher needs each word for:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- The command. You type java -cp out Hello Asha Pune. The class to run is Hello, and the two words after its name are arguments for the program.
- Find the class. The launcher finds out/Hello.class on the class path out. The next section shows the javac command that wrote it.
- public. It looks for public static void main(String[] args). public lets code outside the class call the method, and the launcher is outside the class.
- static. static makes main belong to the class itself, so the launcher can call it without creating an object of Hello first.
- String[] args. The words after the class name become the array args, here the two strings Asha and Pune. With no words after the name it would be an empty array, never null.
- main runs. main prints Hello from a classic main, then Arguments received 2, and then each argument on a line of its own.
- void. void means that main gives back no value. When it returns, the program ends; a program reports a failure through an exception or System.exit, not through a return value.
A public class needs a file of its own name
The compiler insists on the first rule of the list above. This file declares a public class Greeting but is called
Mismatch.java:
// A public class in a file with another name.
public class Greeting {
public static void main(String[] args) {
System.out.println("Hello");
}
} from session import run
run("javac -d out Mismatch.java", expect_failure=["javac -d out Mismatch.java"]) Output
# JDK in use: openjdk version "25.0.4.1" LTS
$ javac -d out Mismatch.java
Mismatch.java:2: error: class Greeting is public, should be declared in a file named Greeting.java
public class Greeting {
^
1 error
[exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 file_name_session.py
The Java Language Specification allows a compiler that stores code in files to enforce this, and javac does, which is
why every public class lives in a file of its own named after it. Classes that are not public may share a file.
Checkpoint
Check: The words of the classic main
javac writes a class file once, and java runs it from the class path
The source launcher of the last lesson compiles in memory every time. For anything you run more than once, compile
once with javac, keep the class file, and run it as often as you like:
Source-file mode and class-file mode
Text description of the diagram
The diagram shows two routes from the same source file, Hello.java, to a running program, one above the other.
- Source-file mode, one command: java Hello.java compiles the file in memory and runs it at once. No class file is kept.
- Class-file mode, two commands: javac -d out Hello.java writes the class file out/Hello.class, which stays on disk; then java -cp out Hello finds the class on the class path and runs it. The class file can be run again without compiling.
from session import run
# Compile once into out, then run the class three times with different arguments.
run("""
javac -d out Hello.java
find out -type f
java -cp out Hello
java -cp out Hello Asha Pune
java -cp out Hello "New Delhi"
""") Output
# JDK in use: openjdk version "25.0.4.1" LTS $ javac -d out Hello.java $ find out -type f out/Hello.class $ java -cp out Hello Hello from a classic main Arguments received: 0 $ java -cp out Hello Asha Pune Hello from a classic main Arguments received: 2 Asha Pune $ java -cp out Hello "New Delhi" Hello from a classic main Arguments received: 1 New Delhi
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 hello_session.py
javac -d out Hello.javacompiles the file and writes the class into the folderout, creating the folder if needed. Silence means success.java -cp out Helloruns it.-cp, short for--class-path, lists where the JVM should look for classes, the class path: The folders and JAR files in which the JVM looks for classes, given with -cp or --class-path. Without it, and without a CLASSPATH environment variable, it is the current folder.; without it (and without aCLASSPATHenvironment variable), the class path is the current folder. Notice that you name the class,Hello, not the file: no.javaand no.class.- Words after the class name become the elements of
args. Quotes keep"New Delhi"together as one argument, and with no argumentsargs.lengthis 0, not an error.
Key idea
javac -d out Hello.java compiles once into out/Hello.class, and java -cp out Hello runs the class, named
without .java or .class, as often as you like.
Checkpoint
Check: javac, the class path and arguments
A class in a package runs by its full name
Real programs put their classes in package: A named group of classes, such as com.example.shop, declared on the first line of a source file. javac -d stores its class files in folders that follow the name, and java runs such a class by its full name., named groups such as com.example.shop,
declared on the first line of the file. javac -d then builds a folder for each part of the package name, and java
needs the class’s full name:
package com.example.shop;
public class App {
public static void main(String[] args) {
System.out.println("Running " + App.class.getName());
}
} from session import run
# A class in a package: javac -d builds the matching folders, and java needs the class's full name.
run("""
javac -d out src/com/example/shop/App.java
find out -type f
java -cp out com.example.shop.App
java -cp out App
""", expect_failure=["java -cp out App"]) Output
# JDK in use: openjdk version "25.0.4.1" LTS $ javac -d out src/com/example/shop/App.java $ find out -type f out/com/example/shop/App.class $ java -cp out com.example.shop.App Running com.example.shop.App $ java -cp out App Error: Could not find or load main class App Caused by: java.lang.ClassNotFoundException: App [exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 packages_session.py
The class file landed in out/com/example/shop/App.class, and the class’s full name, com.example.shop.App, ran it.
The short name App fails with Could not find or load main class App, because the class path folder out holds no
App.class at its top. Step through the same session, from the package name to the folders and back:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- The source. App.java starts with package com.example.shop; and sits in matching folders under src, the usual layout of Java projects.
- javac -d out. javac -d out builds one folder for each part of the package name and writes the class file into the last one, out/com/example/shop/App.class, as find showed.
- The full name. java -cp out com.example.shop.App names the class by its full name, so the JVM looks in out/com/example/shop/ and finds App.class. The program prints Running com.example.shop.App.
- The short name. java -cp out App looks for out/App.class at the top of the class path. There is none, so it fails with Could not find or load main class App.
- Build tools. Maven and Gradle use the same idea, with the package folders under src/main/java instead of src.
The source file sits in matching folders under src too, the usual layout of Java projects; build tools such as Maven
and Gradle use the same idea, with the package folders under src/main/java.
Checkpoint
Check: Classes in packages
JDK 25 picks main by its parameters, and older JDKs want the classic one
Since JDK 25, the launcher accepts more kinds of main method than the classic one, which is what made compact
source files possible. The Java Language Specification (§12.1.4) and JEP 512 give the rules:
- A method qualifies when it is named
main, returnsvoid, is notprivate, and has either oneString[]parameter or none. It may bestaticor an instance method, and it no longer has to bepublic. - If the class has a qualifying
mainwith aString[]parameter, the launcher chooses that one; only when there is none does it choose amainwithout parameters. - A
staticmethod is called directly. For an instance method, the launcher first creates an object of the class, which needs a constructor without parameters that is notprivate, and then callsmainon it.
This class offers both kinds, in the less obvious combination:
// Two candidate main methods in one class: which one does the launcher choose?
class Shop {
static void main() {
System.out.println("static main() without parameters");
}
void main(String[] args) {
System.out.println("instance main(String[]) with " + args.length + " argument(s)");
}
} from session import run
# JDK 25 picks the main that takes String[]; JDK 21 accepts only public static void main(String[]).
run("""
javac -d out Shop.java
java -cp out Shop one two
javac --release 21 -d out21 Shop.java
""")
run("java -cp out21 Shop", jdk="21", expect_failure=["java -cp out21 Shop"]) Output
# JDK in use: openjdk version "25.0.4.1" LTS $ javac -d out Shop.java $ java -cp out Shop one two instance main(String[]) with 2 argument(s) $ javac --release 21 -d out21 Shop.java # JDK in use: openjdk version "21.0.12.1" LTS $ java -cp out21 Shop Error: Main method not found in class Shop, please define the main method as: public static void main(String[] args) or a JavaFX application class must extend javafx.application.Application [exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 which_main_session.py
JDK 25 ran the instance method main(String[]), even though the other main is static: the parameter list decides,
not the word static. The same class compiled for Java 21 and run on JDK 21 does not start at all, because before JDK
25 the launcher accepted only public static void main(String[] args) and says so in its error message. So when code
must also run on older JDKs, write the classic signature. Step through the three rules on Shop:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- Two candidates. Shop declares two main methods. Both qualify under rule 1, because each is named main, returns void, is not private and has one String[] parameter or none.
- String[] wins. Rule 2 chooses a main with a String[] parameter whenever there is one, static or not. The JDK 25 launcher therefore takes void main(String[] args).
- An object first. Rule 3 says how to call it. This main is an instance method, so the launcher first creates an object of Shop, which needs a constructor without parameters that is not private; Shop has the default one.
- main runs. The launcher calls main on that object with the two arguments one and two, and the program prints instance main(String[]) with 2 argument(s).
- The static main waits. static void main() never ran. The parameter list decided the choice, not the word static; a main without parameters is chosen only when no main takes String[].
- On JDK 21. The same class compiled for Java 21 does not start on JDK 21. Before JDK 25 the launcher accepted only public static void main(String[] args), and its error message says so.
Almost every Java program written before JDK 25 starts from the classic main, and that is most of the Java you will
read. The systems that judge programs automatically work with it too: they compile your file with javac and run the
class with java, exactly as above. The online judge Kattis, for example, documents that it compiles Java with javac
25.0.3 and runs your main class from the class path, and the ICPC World Finals programming environment lists OpenJDK
21.0.4, a JDK on which a compact source file does not compile.
For coding rounds
Use the classic form, a public class whose name matches the file with public static void main(String[] args),
unless the platform’s own instructions say otherwise.
Checkpoint
Check: Which main the launcher chooses
Practise: join the arguments
Complete Main.java so that it prints its command-line arguments on one line, joined by |, or the text
(no arguments) when it receives none. With the arguments one two it prints one | two; with "New Delhi" Pune it
prints New Delhi | Pune.
// Exercise: print the command-line arguments joined by " | ", or "(no arguments)" when there are none.
// Compile with: javac -d out Main.java Run with: java -cp out Main one two
public class Main {
public static void main(String[] args) {
System.out.println("TODO");
}
} The session compiles the class and runs three sample tests, each comparing the output with the expected line through
diff, as in the last lesson’s exercise. As given, main prints TODO, so all three fail:
from session import run
# The sample tests: run the class with three argument lists and compare each output with the expected line.
run("""
javac -d out Main.java
java -cp out Main > got.txt; diff expected-none.txt got.txt && echo PASS
java -cp out Main one two > got.txt; diff expected-two.txt got.txt && echo PASS
java -cp out Main "New Delhi" Pune > got.txt; diff expected-space.txt got.txt && echo PASS
""", expect_failure=[
"java -cp out Main > got.txt; diff expected-none.txt got.txt && echo PASS",
"java -cp out Main one two > got.txt; diff expected-two.txt got.txt && echo PASS",
'java -cp out Main "New Delhi" Pune > got.txt; diff expected-space.txt got.txt && echo PASS',
]) Output
# JDK in use: openjdk version "25.0.4.1" LTS $ javac -d out Main.java $ java -cp out Main > got.txt; diff expected-none.txt got.txt && echo PASS 1c1 < (no arguments) --- > TODO [exit status 1] $ java -cp out Main one two > got.txt; diff expected-two.txt got.txt && echo PASS 1c1 < one | two --- > TODO [exit status 1] $ java -cp out Main "New Delhi" Pune > got.txt; diff expected-space.txt got.txt && echo PASS 1c1 < New Delhi | Pune --- > TODO [exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 main_exercise_session.py
First hint: String.join(" | ", args) joins the elements of an array with a separator, and args.length == 0 is true
when there are no arguments. Remember to compile again with javac after every change, or the old class file runs.
Once all three tests pass, test yourself on the four ideas of this lesson. Each answer you give is explained on the spot, and the next question comes from the idea where you need it most:
Practice that adapts to you
Practise this lesson
11 questions about 4 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Fresher · What does each word of public static void main(String[] args) mean?
public lets code outside the class, including the launcher, call the method. static makes it belong to the class
rather than to an object, so it can run before any object exists. void means it returns nothing; a program reports
failure through an exception or System.exit, not a return value. main is the name the launcher searches for, and
String[] args receives the command-line arguments, an empty array when there are none. A good answer adds what JDK
25 changed: under JEP 512 the launcher also accepts main methods that are not public or static or have no
parameters, preferring one with a String[] parameter. Older JDKs, and many judges, still expect exactly this classic
signature.
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Fresher to mid
A program runs fine with "java Booking.java". After "javac -d out Booking.java" it either fails to compile or, once compiled, "java -cp out Booking" refuses to start. Give the likely causes of each failure and explain why the source launcher let them through.
-
Fresher to mid
Your Java solution passes every sample on your laptop, but an online judge or a placement test platform reports a compilation error or a runtime error for it. Which Java-specific causes do you check first, and in what order?
Key takeaways
- The classic entry point is
public static void main(String[] args)in a class, and a public top-level class must be in a file of the same name. javac -d out Hello.javawritesout/Hello.class, andjava -cp out Helloruns the class, named without an extension, withargsholding the command-line arguments, empty and never null when there are none.- A class in a package lands in matching folders and runs by its full name, such as
com.example.shop.App. - JDK 25 chooses a
mainwith aString[]parameter over one without, static or not, while JDKs before 25, and judges that run them, accept only the classic signature.
Terms in this lesson
- class path
- The folders and JAR files in which the JVM looks for classes, given with -cp or --class-path. Without it, and without a CLASSPATH environment variable, it is the current folder.
- command-line arguments
- The words typed after the class name, as in java -cp out Hello Asha Pune. The launcher passes them to main as the array String[] args, which is empty, never null, when there are none.
- package
- A named group of classes, such as com.example.shop, declared on the first line of a source file. javac -d stores its class files in folders that follow the name, and java runs such a class by its full name.
References
- The Java Language Specification, Java SE 25: 12.1.4, Invoke a main Method (Oracle)
- The Java Language Specification, Java SE 25: 7.6, Top Level Class and Interface Declarations (Oracle)
- JEP 512: Compact Source Files and Instance Main Methods (OpenJDK)
- The java Command (JDK 25 tool reference) (Oracle)
- The javac Command (JDK 25 tool reference) (Oracle)
- Java SE 25 API: java.lang.System (System.out) (Oracle)
- Java (Kattis language reference) (Kattis)
- ICPC World Finals Programming Environment (ICPC Foundation)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress