javac and java, by hand
Compiling and running without an IDE, the errors each step produces, and the source launcher that skips the first step.
An IDE hides this behind a green triangle, and hiding it is fine right up until something fails and the button gives you no vocabulary to describe what. Doing it by hand once means every error afterwards has a shape you recognise.
Two commands, two stages
javac Greet.java # compile: source in, Greet.class out
java Greet # run: the JVM loads Greet.class and calls mainNote what is different about the second one. You compile a file and you run a class. javac takes Greet.java because it is reading a file from disk. java takes Greet — no extension, no path — because it is asking the JVM to find and load a class by name.
That distinction is the source of the most common beginner error, and it is worth meeting deliberately:
$ java Greet.class
Error: Could not find or load main class Greet.class
$ java greet
Error: Could not find or load main class greetThe first fails because there is no class named Greet.class. The second fails because class names are case-sensitive and greet is not Greet.
Where the class file goes
By default, beside the source:
$ javac Greet.java
$ ls
Greet.java Greet.classThat gets messy quickly, so real projects separate them:
$ javac -d out Greet.java
$ find out -name "*.class"
out/Greet.class-d says put output under this directory. Every build tool does exactly this, and now you know what it is doing.
Reading the three failures
Three ways this goes wrong, and each names its own stage — the same three you met in the foundations course, now with Java's wording.
The source is not legal Java. javac refuses and produces nothing:
Greet.java:3: error: ';' expectedA line number and a column. Fix the file; nothing was built.
The class cannot be found. javac succeeded; the JVM cannot find what you asked for:
Error: Could not find or load main class GreetThis is about where things are, not about your code. Wrong directory, wrong name, wrong case, or you compiled with -d out and then ran from the wrong place.
It ran and threw. Compilation was fine, loading was fine, the logic was not:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
at Greet.main(Greet.java:4)A stack trace, with your file and line in it. This is the category the compiler cannot help with.
Running a single file without compiling
Since Java 11 you can skip the first step entirely for a one-file program:
$ java Greet.java one two
args.length = 2
args[0] = one
args[1] = twoNo javac, no .class file on disk. The launcher compiles it in memory and runs it.
This is genuinely useful for a scratch program, a script, or checking what a snippet does — and it is why "Java needs a compile step before you can try anything" is no longer true. It does not replace a build: one file only, and nothing is produced to ship.
Try it yourself
Break each stage on purpose
Write a small program, then cause each of the three failures deliberately and read what comes back.
- Remove a semicolon. Compile. Which stage complained, and does the message point at a line?
- Fix it, compile, then run with the wrong case (
java greet). What exactly does it say? - Compile with
-d out, then runjava Greetfrom where you are. Why does it fail, and what is the fix? - Make it read
args[5]with no arguments passed. Which stage fails now?
Answers
- Compile.
Greet.java:3: error: ';' expected— a file and a line, becausejavacis reading your text. - Startup.
Error: Could not find or load main class greet. No line number, because there is no source involved — the JVM looked for a class and did not find one. - Startup, for the same reason: the class is now in
out/, which is not where the JVM is looking. Eithercd outor tell it where:java -cp out Greet. That-cpis the classpath, and it is the thing that "class not found" is usually about. - Run.
ArrayIndexOutOfBoundsException, with a stack trace naming your line. Everything compiled and loaded correctly; the values were wrong.
Four failures, three stages, and the message tells you which every time.
Misconceptions
- "
javaruns the file." It loads a class by name. The file isjavac's business. - "
.classfiles are portable,.javafiles are not." Both are. The class file is the one designed for it — same bytecode, any CPU. - "An IDE does something different." It runs the same two commands with a classpath it worked out for you. That is the whole difference, and it is why an IDE can succeed where your terminal fails.