Run Java
The java command starts a Java Virtual Machine and runs a program in it. It can launch a compiled class, a packaged JAR file, or a source file directly. This lesson covers each form, how the JVM finds the classes it needs, how to pass arguments and settings into a program, and how a program reports success or failure when it ends.
Running a Compiled Class
javac Hello.java
java Hello
Note the difference between the two commands:
| Command | Takes | Example |
|---|---|---|
javac | A file name, with .java | javac Hello.java |
java | A class name, without .class | java Hello |
Writing java Hello.class fails:
Error: Could not find or load main class Hello.class
Caused by: java.lang.ClassNotFoundException: Hello.class
The JVM interprets Hello.class as a class named class in a package named Hello. Give the class name only.
The main method requirement
The class must have a valid entry point. The standard form is:
public static void main(String[] args)
If it is missing or has the wrong signature:
Error: Main method not found in class Hello, please define the main method as:
public static void main(String[] args)
...
Since Java 25, the launcher also accepts simpler entry points, such as an instance void main() with no parameters, used mainly in compact source files for beginners and scripts. The standard form above works on every Java version.
How the JVM Finds Classes: the Classpath
The JVM loads classes from the locations on the classpath, a list of directories and JAR files. By default the classpath is the current directory (.). Set it explicitly with -cp (or the long form --class-path):
java -cp out Hello
For a class in a package, give the fully qualified name, and point the classpath at the directory that contains the package's top-level folder:
out/
└── com/example/app/Main.class
java -cp out com.example.app.Main # correct
java -cp out/com/example/app Main # wrong: Main is not in the unnamed package
The JVM converts com.example.app.Main into the path com/example/app/Main.class and searches for it under each classpath entry. The second form finds the file but rejects it, because the class declares package com.example.app and is being loaded as if it had none.
Multiple entries are separated by : on macOS/Linux and ; on Windows:
java -cp "out:lib/*" com.example.app.Main # macOS / Linux
java -cp "out;lib\*" com.example.app.Main # Windows
lib/* includes every JAR in the lib directory. Quote it so the shell does not expand the wildcard.
"Could not find or load main class"
This is the most common launch error. Check, in order:
- Is the name correct, including capitalization and the full package?
- Is the classpath pointing at the directory above the package folders?
- Was the code compiled successfully, and is the
.classfile where you expect it? - If
-cpis set, does it include.orout(wherever your classes are)? Setting-cpreplaces the default current directory.
Similarly, NoClassDefFoundError at runtime, rather than at launch, means a class your program uses (often from a library JAR) is not on the classpath.
Passing Command-Line Arguments
Everything after the class name is passed to the program in the args array:
public class Sum {
public static void main(String[] args) {
int total = 0;
for (String a : args) {
total += Integer.parseInt(a);
}
System.out.println("Arguments: " + args.length + ", total: " + total);
}
}
java Sum 10 20 30
Arguments: 3, total: 60
Arguments are always strings, split on spaces by the shell. Quote an argument that contains spaces:
java Greet "Ada Lovelace" # args[0] is "Ada Lovelace"
java Greet Ada Lovelace # args[0] is "Ada", args[1] is "Lovelace"
Options placed before the class name go to the JVM; arguments placed after it go to the program:
java -Xmx512m -Dmode=test Sum 1 2 3
# └── JVM options ──┘ └class┘ └─ program arguments ─┘
Running a JAR File
Applications are usually distributed as JAR files. If the JAR's manifest names a main class, run it with -jar:
java -jar inventory.jar
java -jar inventory.jar --port 8080 # with program arguments
The manifest (META-INF/MANIFEST.MF inside the JAR) contains a line like Main-Class: com.example.app.Main. If it is missing:
no main manifest attribute, in inventory.jar
In that case, name the class explicitly:
java -cp inventory.jar com.example.app.Main
With -jar, the -cp option and the CLASSPATH environment variable are ignored. Dependencies must be listed in the manifest's Class-Path attribute or bundled into the JAR itself (a "fat" or "uber" JAR, which build tools can produce). JAR files have their own lesson.
Running Source Files Directly
Since Java 11, java can run a source file without a separate compile step:
java Hello.java
The launcher compiles the file in memory, then runs the first top-level class it declares. No .class file is written. This is ideal for small programs, quick experiments, and scripts.
java Sum.java 4 5 6
# Arguments: 3, total: 15
Since Java 22, the source launcher can also run a program spread across several source files: when the main file refers to another class, the launcher finds and compiles that class's source file from the same directory tree. Libraries can be added with --class-path.
Shebang scripts (macOS / Linux)
A source file can be made directly executable. The file must not end in .java:
#!/usr/bin/env -S java --source 21
public class Backup {
public static void main(String[] args) {
System.out.println("Backing up " + (args.length > 0 ? args[0] : "."));
}
}
chmod +x backup
./backup /home/ada/projects
The first line tells the operating system to run the file with java --source 21. This turns small Java programs into command-line tools without a build step.
JVM Options
Options before the class or -jar configure the JVM itself:
| Option | Purpose |
|---|---|
-cp, --class-path | Where to find classes |
-Dname=value | Set a system property, read with System.getProperty("name") |
-Xmx2g, -Xms512m | Maximum and initial heap size |
-Xss1m | Thread stack size |
-ea | Enable assert statements (disabled by default) |
-verbose:class | Log every class as it is loaded |
-Xlog:gc | Log garbage collection |
-version, --version | Print the Java version (to stderr and stdout respectively) and exit |
-XshowSettings:all | Print JVM settings, system properties, and locale information |
Example: run with a property and assertions enabled:
public class Config {
public static void main(String[] args) {
String env = System.getProperty("app.env", "development");
int workers = Integer.getInteger("app.workers", 2);
assert workers > 0 : "workers must be positive";
System.out.println("Environment: " + env + ", workers: " + workers);
}
}
java -ea -Dapp.env=production -Dapp.workers=8 Config
Environment: production, workers: 8
System.getProperty takes a default used when the property is not set. Integer.getInteger reads a system property and parses it as an integer. Without -ea, the assert line is skipped entirely, which is why assertions are used for development checks rather than for validating real input.
JDK_JAVA_OPTIONS
Options can also come from the JDK_JAVA_OPTIONS environment variable, which the java launcher prepends to its command line:
export JDK_JAVA_OPTIONS="-Xmx1g -Duser.timezone=UTC"
java -jar app.jar
This is useful in containers and CI environments where the launch command is fixed but settings need to vary.
Exit Codes
When a program finishes, the JVM returns an exit code to the operating system. 0 means success; any other value indicates failure. Scripts and CI systems rely on it to decide whether a step succeeded.
| How the program ends | Exit code |
|---|---|
main returns normally (and no non-daemon threads remain) | 0 |
System.exit(n) | n |
| An uncaught exception ends the main thread | 1 |
| The launcher fails (class not found, bad option) | 1 |
public class Validate {
public static void main(String[] args) {
if (args.length == 0) {
System.err.println("Usage: java Validate <file>");
System.exit(2);
}
System.out.println("Validating " + args[0]);
}
}
java Validate
echo $? # macOS/Linux: prints 2
# Windows: echo %ERRORLEVEL% (cmd) or $LASTEXITCODE (PowerShell)
Use System.err for error messages so they stay separate from normal output, and a nonzero exit code so callers can detect failure. Avoid System.exit inside library code or web applications, where it would shut down the entire JVM, including everything else running in it.
Practical Example: A Small Command-Line Tool
// WordStats.java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class WordStats {
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("Usage: java WordStats <file>");
System.exit(2);
}
Path file = Path.of(args[0]);
try {
String text = Files.readString(file);
long lines = text.lines().count();
long words = text.isBlank() ? 0 : text.strip().split("\\s+").length;
boolean verbose = Boolean.getBoolean("verbose");
System.out.println(lines + " lines, " + words + " words");
if (verbose) {
System.out.println("File: " + file.toAbsolutePath());
System.out.println("Size: " + Files.size(file) + " bytes");
}
} catch (IOException e) {
System.err.println("Cannot read " + file + ": " + e.getMessage());
System.exit(1);
}
}
}
java WordStats.java notes.txt
java -Dverbose=true WordStats.java notes.txt
java WordStats.java missing.txt; echo "exit code: $?"
Example output:
12 lines, 148 words
12 lines, 148 words
File: /home/ada/notes.txt
Size: 861 bytes
Cannot read missing.txt: missing.txt
exit code: 1
The program takes the file name as a command-line argument, reads an optional verbose flag as a system property (Boolean.getBoolean returns true only when the property is set to "true"), writes errors to standard error, and reports success or failure through its exit code. Run as a source file, it needs no build step at all, yet it behaves like any other command-line tool and can be used in shell scripts and automated pipelines.