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.

4 min read🔧 Setting Up Java

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

bash
javac Greet.java      # compile: source in, Greet.class out
java Greet            # run: the JVM loads Greet.class and calls main

Note 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:

plaintext
$ java Greet.class
Error: Could not find or load main class Greet.class
 
$ java greet
Error: Could not find or load main class greet

The 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:

bash
$ javac Greet.java
$ ls
Greet.java  Greet.class

That gets messy quickly, so real projects separate them:

bash
$ 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:

plaintext
Greet.java:3: error: ';' expected

A 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:

plaintext
Error: Could not find or load main class Greet

This 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:

plaintext
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:

plaintext
$ java Greet.java one two
args.length = 2
  args[0] = one
  args[1] = two

No 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.

  1. Remove a semicolon. Compile. Which stage complained, and does the message point at a line?
  2. Fix it, compile, then run with the wrong case (java greet). What exactly does it say?
  3. Compile with -d out, then run java Greet from where you are. Why does it fail, and what is the fix?
  4. Make it read args[5] with no arguments passed. Which stage fails now?
Answers
  1. Compile. Greet.java:3: error: ';' expected — a file and a line, because javac is reading your text.
  2. 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.
  3. Startup, for the same reason: the class is now in out/, which is not where the JVM is looking. Either cd out or tell it where: java -cp out Greet. That -cp is the classpath, and it is the thing that "class not found" is usually about.
  4. 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

  • "java runs the file." It loads a class by name. The file is javac's business.
  • ".class files are portable, .java files 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.
Progress is saved on this device and to your account when signed in.