JAVA / GETTING STARTED
Compiling with javac and running with java
Compile Java sources with javac and launch them with java, controlling output directories, the classpath, and the arguments that reach main.
What you will learn
- Compile with javac Hello.java, then run with java Hello - no extension, no path
- Use javac -d out and java -cp out to keep class files out of the source tree
- Put JVM options before the class name; everything after it becomes main's args
- Run one throwaway file with java Hello.java, which writes no class file at all
Understanding Compiling with javac and running with java
The two commands look symmetric, but they address different worlds. The operands of javac are file paths, so javac Launch.java means compile that file, and the only naming rule it enforces is that a public class must sit in a file named after it with a .java suffix. The operand of java is a class's binary name, so java Launch means load a class called Launch: no extension, no slashes, dots between package parts. The launcher turns that name into the resource path Launch.class and searches the classpath for it, and when you set no classpath the classpath is the current directory.
The launcher's command line is java, then options, then the main class, then arguments, and the first token that is not an option ends option parsing. Everything to the left of the class name is consumed by the JVM; everything to the right is copied into the String array handed to main. That is why java -Dmode=fast App -Dmode=slow sets one system property and passes the other as ordinary text, and why java App -Xmx512m hands your program a string instead of enlarging the heap.
By default javac writes each class file next to its source, which mixes generated files into your source tree; javac -d out sends them under out instead, recreating package directories there, and the run then needs -cp out to point the launcher at that root. Since Java 11 you can skip the artifact entirely with java Launch.java, which compiles in memory and runs the first top-level class in the file, which is convenient for one file and useless for two. Because javac's result is a file on disk, an edited source changes nothing until you compile again: the launcher will keep running the class file you built an hour ago.
// javac -d out Launch.java
// java -cp out Launch alpha beta
public class Launch {
public static void main(String[] args) {
System.out.println("launched class: " + Launch.class.getName());
System.out.println("argument count: " + args.length);
for (int i = 0; i < args.length; i++) {
System.out.println(" arg " + i + " = " + args[i]);
}
}
}
javac addresses files and writes class files, while java addresses a class by its binary name and finds it through the classpath, so the file name and the class name are resolved by two separate mechanisms.
Worked examples
A packaged class needs a package root, not a folder path
Shows that the java operand is a dotted binary name resolved from the directory named by -cp.
// file: tools/Report.java
// javac -d out tools/Report.java
// java -cp out tools.Report
package tools;
public class Report {
public static void main(String[] args) {
System.out.println("running " + Report.class.getName());
System.out.println("simple name " + Report.class.getSimpleName());
}
}
Example explained
Line 1javac is given the path tools/Report.java, because its operands are files on disk.
Line 2The -d out option makes javac create out/tools/Report.class, mirroring the package declaration as directories.
Line 3The launcher is given tools.Report with a dot, and -cp out tells it that out is the root the package path starts from.
Line 4getName prints the binary name tools.Report that the launcher resolved, while getSimpleName drops the package part.
Where an option sits decides who reads it
Demonstrates that the class name is the boundary between JVM options and program arguments.
// javac Order.java
// java -Dmode=fast Order -Dmode=slow
import java.util.Arrays;
public class Order {
public static void main(String[] args) {
System.out.println("mode property = " + System.getProperty("mode"));
System.out.println("args = " + Arrays.toString(args));
}
}
Example explained
Line 1The first -Dmode=fast comes before the class name, so the launcher consumes it and defines a system property.
Line 2The second -Dmode=slow comes after the class name, so option parsing has already stopped and it is just a string.
Line 3System.getProperty returns the value set by the launcher; with no -D at all this line would print mode property = null.
Line 4Arrays.toString reveals the raw contents of the args array, which contains exactly one element here.
Important notes
In single-file source mode the launcher runs the first top-level class in the file and the file name does not have to match any class, two relaxations that disappear as soon as javac writes real class files.
javac may compile more than the files you named, because it implicitly compiles referenced sources it finds and writes extra files such as Outer$Inner.class, so the class file count can exceed the source file count.
Common mistakes
Typing java Launch.class or java Launch.java on Java 8: the launcher reads the dot as a package separator, looks for a class named class inside package Launch, and stops with 'Could not find or load main class Launch.class'.
Editing Launch.java and re-running java Launch without compiling again: the previous class file is still on disk, so the program behaves exactly as before and the change appears to have been ignored.
Running java tools.Report from inside the tools directory: the launcher resolves the package from the classpath root, so it looks for tools/tools/Report.class and fails; run it from the parent directory or pass -cp .. instead.
Try it yourself
Change, predict, then run
In one source file, declare two top-level classes, Alpha and Beta, each with a main that prints its own name, and run the file. Then move Beta above Alpha, run it again, and note which main the launcher chose.
Open the Java workspaceCheck your understanding
You compile with javac -d build App.java from the project root, then run java App from that same root. What happens and why?
- It works, because javac recorded build as the output directory and java reuses that setting.
- It works, because the launcher scans the current directory and its subdirectories for App.class.
- It fails, because with no -cp the classpath is just the current directory, and App.class now lives in build.
- It fails, because -d build makes the class part of a package, so it must be launched as build.App.
Show answer
When neither -cp nor CLASSPATH is set, the classpath is the single directory '.', and the launcher looks for App.class directly there rather than walking into subdirectories, so java -cp build App is the fix. The build.App option is tempting because the directory does appear in the path, but the binary name comes from the package declaration in the source, and -d only chooses where javac writes; build is a classpath root, not a package.