DEV Community

Md Jamilur Rahman
Md Jamilur Rahman

Posted on

How the Java Launcher Works — Running Programs Without Compiling First

You know the drill. Write code, compile it, run it. Write code, compile it, run it. Over and over. What if you could skip the middle step entirely?

Since JDK 11, Java lets you run a source file directly — no separate javac step needed. The java launcher handles compilation behind the scenes. And as of JDK 22, this works with multi-file programs too.

Let me walk through how it works, when to use it, and where it breaks down.

Single-File Source-Code Programs

Create a file called HelloWorld.java:

public class HelloWorld {
    public static void main(String[] args) {
        IO.println("Hello World!");
    }
}
Enter fullscreen mode Exit fullscreen mode

Instead of running javac HelloWorld.java followed by java HelloWorld, you just run:

$ java HelloWorld.java
Enter fullscreen mode Exit fullscreen mode

That's it. The launcher compiles the file in memory and runs it. No .class file gets written to disk.

This works because the java launcher automatically invokes the Java compiler when you pass it a .java file. The compiled bytecode lives in memory for the duration of the program.

Passing Arguments

You can pass arguments the same way you would with a compiled class:

public class HelloJava {
    public static void main(String[] args) {
        IO.println("Hello " + args[0]);
    }
}
Enter fullscreen mode Exit fullscreen mode
$ java HelloJava.java World!
Enter fullscreen mode Exit fullscreen mode

Output:

Hello World!
Enter fullscreen mode Exit fullscreen mode

Nothing special here — the launcher forwards arguments to your main method just like normal.

Multiple Classes in One File

Sometimes you want a helper class alongside your main class. That works fine:

public class MultipleClassesInSameFile {
    public static void main(String[] args) {
        IO.println(GenerateMessage.generateMessage());
        IO.println(AnotherMessage.generateAnotherMessage());
    }
}

class GenerateMessage {
    static String generateMessage() {
        return "Here is one message";
    }
}

class AnotherMessage {
    static String generateAnotherMessage() {
        return "Here is another message";
    }
}
Enter fullscreen mode Exit fullscreen mode
$ java MultipleClassesInSameFile.java
Enter fullscreen mode Exit fullscreen mode

Output:

Here is one message
Here is another message
Enter fullscreen mode Exit fullscreen mode

The first class in the file must have the public static void main method. The other classes can be package-private.

Multi-File Programs (JDK 22+)

JDK 22 added JEP 458, which lets the java launcher work with programs spread across multiple files and directories.

Say you have this structure:

project/
├── Main.java
├── model/
│   └── Person.java
└── service/
    └── PersonService.java
Enter fullscreen mode Exit fullscreen mode

Main.java:

import model.Person;
import service.PersonService;

public class Main {
    public static void main(String[] args) {
        PersonService service = new PersonService();
        Person person = service.createNewPerson();
        IO.println(person.printName() + " has been created!");
    }
}
Enter fullscreen mode Exit fullscreen mode

As long as model/Person.java and service/PersonService.java exist in subdirectories matching their package structure, the launcher will compile and load everything:

$ java Main.java
Enter fullscreen mode Exit fullscreen mode

You can structure your code properly without setting up a build system. That matters more than it sounds — the jump from single-file programs to multi-file projects used to require a build tool right away.

Using JDK Classes

Any class from the standard JDK libraries works out of the box. No classpath configuration needed:

import java.util.Scanner;
import java.util.regex.MatchResult;

public class ScannerExample {
    public static void main(String... args) {
        String wordsAndNumbers = """
            Longing rusted furnace
            daybreak 17 benign
            9 homecoming 1
            freight car
            """;
        try (Scanner scanner = new Scanner(wordsAndNumbers)) {
            scanner.findAll("benign").map(MatchResult::group).forEach(IO::println);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
$ java ScannerExample.java
Enter fullscreen mode Exit fullscreen mode

The Scanner and MatchResult classes come from java.util, which is part of the JDK, so no extra setup is required.

External Libraries Need the Classpath

If your code depends on a library that is not part of the JDK, you still need to add it to the classpath manually:

import org.apache.commons.lang3.RandomUtils;

public class ReferenceNonJDKClass {
    public static void main(String[] args) {
        IO.println(RandomUtils.nextInt());
    }
}
Enter fullscreen mode Exit fullscreen mode
$ java -cp /path/to/commons-lang3-3.12.0.jar ReferenceNonJDKClass.java
Enter fullscreen mode Exit fullscreen mode

The -cp flag tells the launcher where to find the external JAR. This is one limitation of the direct-launch approach — it does not resolve dependencies for you. For anything beyond simple programs, you will eventually want a build tool like Maven or Gradle.

Shebang Files: Java as a Script

On Unix-like systems (Linux, macOS), you can run a single-file Java program like a shell script. Add a shebang line as the first line of your file:

#!/path/to/your/bin/java --source 26

public class HelloJava {
    public static void main(String[] args) {
        IO.println("Hello " + args[0]);
    }
}
Enter fullscreen mode Exit fullscreen mode

Two requirements:

  1. The file cannot have a .java extension (try something like hello)
  2. You need to make it executable: chmod +x hello

Then run it like any other script:

$ ./hello Maria
Enter fullscreen mode Exit fullscreen mode

This is handy for small utility scripts. Instead of writing bash or Python, you can write a quick Java program and run it directly from the terminal.

Be careful though — if you forget to pass an argument when the program expects one, you will get an ArrayIndexOutOfBoundsException. There is no safety net here.

When to Use This

Direct source-file execution is great for:

  • Learning and experimentation. Trying out a new API or Java feature without creating a full project.
  • Quick scripts. Shebang files turn Java into a viable scripting language for small tasks.
  • Teaching. No build tool setup means beginners can focus on the language, not the toolchain.
  • Prototyping. Multi-file support (JDK 22+) lets you structure code properly while still skipping the build step.

It is not a replacement for a proper build system. Anything that needs external dependencies, tests, or packaging should use Maven or Gradle. But for quick, self-contained programs, the java launcher saves you real time.

Summary

  • The java launcher can compile and run .java files directly without a separate javac step.
  • The compiler runs in memory — no .class files are written to disk.
  • You can pass arguments, define multiple classes in one file, and import JDK classes freely.
  • JDK 22 added multi-file support, letting you structure code across directories.
  • External libraries still need the -cp classpath flag.
  • On Unix systems, shebang files let you run Java programs like shell scripts.
  • Use this for learning, scripting, and prototyping. Use a build tool for everything else.

Based on dev.java/learn — https://dev.java/learn/launch-simple-source-code-programs/

Top comments (0)