AI InterviewSearch…Search problems, topics, or pages…Ctrl KGet started
Skip to content

01 Java & the JVM / Lesson 3 of 6

From source to running program

14 min · +10 XP · 2 exercises · 5 questions

Read the text14 min
Exercises0/2
Quiz5 q

Quick answer

How do you compile and run a Java program?

Run javac Hello.java to compile the source into bytecode in Hello.class, then java Hello to run it — the class name, not the file name. The java launcher starts a JVM, finds the class on the classpath, verifies it and calls its public static void main(String[] args). Since Java 11, java Hello.java compiles a single file in memory and runs it in one step.

You will spend most of this track inside an editor with a Run button, but the button hides four steps you should be able to perform by hand and explain in an interview: write, compile, package, run. This lesson walks the whole pipeline on the command line.

Step 1 — a source file

// Hello.java
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello from " + System.getProperty("java.version"));
    }
}

Rules the compiler enforces about files:

  • A source file may contain at most one public top-level class, and the file must be named after it: Hello.java.
  • It may contain other, non-public top-level classes; each becomes its own .class file.
  • The file is UTF-8 by default since Java 18 (earlier, the platform encoding — a classic source of mojibake on Windows).

Step 2 — compile with javac

$ javac Hello.java
$ ls
Hello.class  Hello.java

javac parses the source, checks every type, resolves every name, and writes bytecode into Hello.class. A compile error stops here, with a file name, a line number, a caret under the problem, and a message:

Hello.java:4: error: ';' expected
        System.out.println("Hello")
                                   ^
1 error

Learn to read these top to bottom: the first error is the real one; later ones are often the compiler recovering badly from it.

Useful flags:

FlagMeaning
-d outPut .class files in out/ (keeps the tree clean)
-cp lib.jarWhere to find other classes you depend on (the classpath)
--release 17Compile for Java 17: refuses newer APIs and language features
-Xlint:allTurn on every warning — do this
-gInclude debugging info (line numbers, local variable names)

Step 3 — run with java

$ java Hello
Hello from 18.0.2

Note: java Hello, not java Hello.class. You name the class, and the launcher finds Hello.class on the classpath (the current directory by default), loads it, verifies it, and calls public static void main(String[]). If that method is missing you get:

Error: Main method not found in class Hello, please define the main method as:
   public static void main(String[] args)

Arguments after the class name arrive in args:

$ java Hello one "two words" 3

gives args = {"one", "two words", "3"} — all strings; convert with Integer.parseInt when you need a number.

The single-file shortcut (Java 11+)

$ java Hello.java

compiles in memory and runs in one step. Handy for experiments; not a substitute for a build in real projects, because nothing is written to disk and only the one file is compiled.

jshell

$ jshell
jshell> int x = 40;
jshell> x + 2
$2 ==> 42

The REPL lets you try an expression without a class or a main. Use it constantly while learning.

Step 4 — package into a JAR

Real programs have many classes. A JAR (Java ARchive) is a zip file of .class files plus a manifest:

$ javac -d out src/com/example/*.java
$ jar --create --file app.jar --main-class com.example.Main -C out .
$ java -jar app.jar

--main-class writes Main-Class: into META-INF/MANIFEST.MF, which is how java -jar knows where to start. Libraries are JARs without a main class; you put them on the classpath.

The classpath, demystified

The classpath is the list of places the JVM searches for classes: directories and JAR files, separated by : on Unix and ; on Windows.

$ java -cp out:lib/gson.jar com.example.Main

Three things go wrong with it constantly:

  1. ClassNotFoundException — a class was asked for by name at run time (reflection, Class.forName) and is not on the classpath.
  2. NoClassDefFoundError — the class was there at compile time and is not there now, or its static initialiser threw the first time it was loaded. This is an Error, not an Exception, and it is the one people search for most.
  3. Wrong version on the classpath — two JARs contain the same class; whichever comes first wins, silently.

Build tools (Maven, Gradle) exist largely to compute this list for you. Under the hood they still call javac and java with a very long -cp.

What is in a .class file

$ javap -c -p Hello
Compiled from "Hello.java"
public class Hello {
  public Hello();
    Code:
       0: aload_0
       1: invokespecial #1   // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #7   // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #13  // String Hello
       5: invokevirtual #15  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

Two things to notice. First, a default constructor Hello() was generated because you wrote none — it calls Object's constructor. Second, main is four instructions: push System.out, push the string constant, call println (a virtual call, resolved by the receiver's runtime class), return. When Module 8 talks about dynamic dispatch, this is the instruction it is talking about.

Run-time flags you will actually use

$ java -Xmx512m -Xss1m -ea Hello
  • -Xmx — maximum heap size. Too small → OutOfMemoryError: Java heap space.
  • -Xss — thread stack size. Deep recursion → StackOverflowError; raising this is a band-aid, fixing the recursion is the cure.
  • -ea — enable assert statements (they are off by default, a trap covered in Module 12).
  • -D name=value — set a system property, read with System.getProperty("name").

Interview angle

  • "What does javac produce?" Bytecode in .class files, one per class (including nested classes: Outer$Inner.class).
  • "Why can't I run java Hello.class?" The launcher takes a class name and searches the classpath; the .class suffix makes it look for a class literally named Hello.class.
  • "ClassNotFoundException vs NoClassDefFoundError?" The first is a checked exception from an explicit lookup by name; the second is an error when a class present at compile time is missing (or failed to initialise) at run time.

Key takeaways

  • javac → .class (bytecode); java → loads and runs by class name.
  • The classpath is where classes are found; most "it worked on my machine" problems are classpath problems.
  • A JAR is a zip with a manifest; Main-Class makes it runnable.
  • javap -c shows the bytecode — use it to check your mental model.

Common questions

Why can't I run java Hello.class?

The java launcher takes a class name, not a file name, and searches the classpath for it. Given Hello.class it looks for a class literally named Hello.class, finds none and fails; run java Hello instead.

What is the classpath in Java?

The classpath is the list of directories and JAR files the JVM searches for classes, set with -cp and separated by : on Unix and ; on Windows. It defaults to the current directory, and most "works on my machine" failures are classpath problems.

How do I create a runnable JAR file?

Compile into a directory, then run jar --create --file app.jar --main-class com.example.Main -C out . — the --main-class option writes Main-Class: into META-INF/MANIFEST.MF, which tells java -jar app.jar which class's main to call.

What causes "Error: Main method not found in class"?

The launcher found the class but no public static void main(String[] args) in it. Every part of that signature is checked, so a missing static, a different parameter type or a misspelled main all produce this error.

How do I see the bytecode of a Java class?

Run javap -c -p Hello on the compiled class. It disassembles the .class file and prints each method's bytecode instructions, including the default constructor the compiler generated and calls such as invokevirtual.

Mark as readSign in to keep your place.

How to Compile and Run Java: javac, java and JAR Files

Compile Java source into bytecode with javac, then run the class by name with java. The classpath, runnable JAR files, jshell, javap and the errors they cause.

  • Course: Java study plan
  • Module: Java & the JVM
  • Kind: Lesson
  • Reading time: 14 min
  • Runtime: OpenJDK 18

How do you compile and run a Java program?

Run javac Hello.java to compile the source into bytecode in Hello.class, then java Hello to run it — the class name, not the file name. The java launcher starts a JVM, finds the class on the classpath, verifies it and calls its public static void main(String[] args). Since Java 11, java Hello.java compiles a single file in memory and runs it in one step.

Lesson

You will spend most of this track inside an editor with a Run button, but the button hides four steps you should be able to perform by hand and explain in an interview: write, compile, package, run. This lesson walks the whole pipeline on the command line.

Step 1 — a source file

// Hello.java
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello from " + System.getProperty("java.version"));
    }
}

Rules the compiler enforces about files:

  • A source file may contain at most one public top-level class, and the file must be named after it: Hello.java.
  • It may contain other, non-public top-level classes; each becomes its own .class file.
  • The file is UTF-8 by default since Java 18 (earlier, the platform encoding — a classic source of mojibake on Windows).

Step 2 — compile with javac

$ javac Hello.java
$ ls
Hello.class  Hello.java

javac parses the source, checks every type, resolves every name, and writes bytecode into Hello.class. A compile error stops here, with a file name, a line number, a caret under the problem, and a message:

Hello.java:4: error: ';' expected
        System.out.println("Hello")
                                   ^
1 error

Learn to read these top to bottom: the first error is the real one; later ones are often the compiler recovering badly from it.

Useful flags:

FlagMeaning
-d outPut .class files in out/ (keeps the tree clean)
-cp lib.jarWhere to find other classes you depend on (the classpath)
--release 17Compile for Java 17: refuses newer APIs and language features
-Xlint:allTurn on every warning — do this
-gInclude debugging info (line numbers, local variable names)

Step 3 — run with java

$ java Hello
Hello from 18.0.2

Note: java Hello, not java Hello.class. You name the class, and the launcher finds Hello.class on the classpath (the current directory by default), loads it, verifies it, and calls public static void main(String[]). If that method is missing you get:

Error: Main method not found in class Hello, please define the main method as:
   public static void main(String[] args)

Arguments after the class name arrive in args:

$ java Hello one "two words" 3

gives args = {"one", "two words", "3"} — all strings; convert with Integer.parseInt when you need a number.

The single-file shortcut (Java 11+)

$ java Hello.java

compiles in memory and runs in one step. Handy for experiments; not a substitute for a build in real projects, because nothing is written to disk and only the one file is compiled.

jshell

$ jshell
jshell> int x = 40;
jshell> x + 2
$2 ==> 42

The REPL lets you try an expression without a class or a main. Use it constantly while learning.

Step 4 — package into a JAR

Real programs have many classes. A JAR (Java ARchive) is a zip file of .class files plus a manifest:

$ javac -d out src/com/example/*.java
$ jar --create --file app.jar --main-class com.example.Main -C out .
$ java -jar app.jar

--main-class writes Main-Class: into META-INF/MANIFEST.MF, which is how java -jar knows where to start. Libraries are JARs without a main class; you put them on the classpath.

The classpath, demystified

The classpath is the list of places the JVM searches for classes: directories and JAR files, separated by : on Unix and ; on Windows.

$ java -cp out:lib/gson.jar com.example.Main

Three things go wrong with it constantly:

  1. ClassNotFoundException — a class was asked for by name at run time (reflection, Class.forName) and is not on the classpath.
  2. NoClassDefFoundError — the class was there at compile time and is not there now, or its static initialiser threw the first time it was loaded. This is an Error, not an Exception, and it is the one people search for most.
  3. Wrong version on the classpath — two JARs contain the same class; whichever comes first wins, silently.

Build tools (Maven, Gradle) exist largely to compute this list for you. Under the hood they still call javac and java with a very long -cp.

What is in a .class file

$ javap -c -p Hello
Compiled from "Hello.java"
public class Hello {
  public Hello();
    Code:
       0: aload_0
       1: invokespecial #1   // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #7   // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #13  // String Hello
       5: invokevirtual #15  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

Two things to notice. First, a default constructor Hello() was generated because you wrote none — it calls Object's constructor. Second, main is four instructions: push System.out, push the string constant, call println (a virtual call, resolved by the receiver's runtime class), return. When Module 8 talks about dynamic dispatch, this is the instruction it is talking about.

Run-time flags you will actually use

$ java -Xmx512m -Xss1m -ea Hello
  • -Xmx — maximum heap size. Too small → OutOfMemoryError: Java heap space.
  • -Xss — thread stack size. Deep recursion → StackOverflowError; raising this is a band-aid, fixing the recursion is the cure.
  • -ea — enable assert statements (they are off by default, a trap covered in Module 12).
  • -D name=value — set a system property, read with System.getProperty("name").

Interview angle

  • "What does javac produce?" Bytecode in .class files, one per class (including nested classes: Outer$Inner.class).
  • "Why can't I run java Hello.class?" The launcher takes a class name and searches the classpath; the .class suffix makes it look for a class literally named Hello.class.
  • "ClassNotFoundException vs NoClassDefFoundError?" The first is a checked exception from an explicit lookup by name; the second is an error when a class present at compile time is missing (or failed to initialise) at run time.

Key takeaways

  • javac → .class (bytecode); java → loads and runs by class name.
  • The classpath is where classes are found; most "it worked on my machine" problems are classpath problems.
  • A JAR is a zip with a manifest; Main-Class makes it runnable.
  • javap -c shows the bytecode — use it to check your mental model.

Common questions

Why can't I run java Hello.class?

The java launcher takes a class name, not a file name, and searches the classpath for it. Given Hello.class it looks for a class literally named Hello.class, finds none and fails; run java Hello instead.

What is the classpath in Java?

The classpath is the list of directories and JAR files the JVM searches for classes, set with -cp and separated by : on Unix and ; on Windows. It defaults to the current directory, and most "works on my machine" failures are classpath problems.

How do I create a runnable JAR file?

Compile into a directory, then run jar --create --file app.jar --main-class com.example.Main -C out . — the --main-class option writes Main-Class: into META-INF/MANIFEST.MF, which tells java -jar app.jar which class's main to call.

What causes "Error: Main method not found in class"?

The launcher found the class but no public static void main(String[] args) in it. Every part of that signature is checked, so a missing static, a different parameter type or a misspelled main all produce this error.

How do I see the bytecode of a Java class?

Run javap -c -p Hello on the compiled class. It disassembles the .class file and prints each method's bytecode instructions, including the default constructor the compiler generated and calls such as invokevirtual.

Exercises

Echo the arguments

When you run java Main a b c, the launcher hands main the array {"a", "b", "c"}. Simulate that from standard input: read an integer n, then n tokens (whitespace-separated, possibly across several lines), and print each one as args[i] = token, then a final line args.length = n.

Input: n, then n tokens. Output: n + 1 lines.

Example: input 3 one two three →

args[0] = one
args[1] = two
args[2] = three
args.length = 3

A tiny stack machine

The JVM is a stack machine: bytecode pushes values onto an operand stack and instructions pop their inputs from it. Simulate one. Read an integer n and n instructions: push <int>, add, sub, mul (each pops two values b then a and pushes a op b), dup (pushes a copy of the top) and print (prints the top without popping). Use an ArrayDeque<Integer>.

Example: 6 then push 6, push 7, mul, print, push 2, sub then print (7 instructions) →

42
40

In this module: Java & the JVM

← JDK, JRE and JVM — the three layers · Anatomy of a Java program →