Running Single-File Java Programs: What Changed Since `javac` + `java`
For years, the first lesson in every Java course was a two-step ritual: compile with javac Hello.java, then run with java Hello. Forget the second step and nothing happens; run it from the wrong folder and you get Could not find or load main class. Beginners spent their first hour fighting the toolchain instead of writing code.
Modern Java quietly removed most of that friction. This post walks through what a single-file Java program looks like today, the errors people still hit, and a few habits that make small experiments faster, whether you run them locally or in a browser editor.
Source-file mode: java Hello.java
Since JDK 11 (JEP 330), the java launcher can run a source file directly:
java Hello.java
The launcher compiles the file in memory and runs the first top-level class it finds. No .class file is written to disk. For scratch work, katas and interview practice, this is all you need.
A few rules apply:
- The file must contain everything the program needs. Source-file mode is for one file (JDK 22 extended it to multi-file programs, but a single file is still the common case).
- The class with
mainshould be the first class declared in the file. - The file name does not have to match the class name in this mode, but keeping them the same avoids surprises when you later switch to
javac.
Less boilerplate in newer JDKs
The classic hello world needs a class, a public static void main(String[] args) signature and System.out.println. Recent JDKs added simpler entry points. Implicitly declared classes and instance main methods arrived as a preview in JDK 21 and were finalised in JDK 25, so on a current JDK this is a complete program:
void main() {
IO.println("Hello, Java");
}
If you are on an older version or teaching code that must run everywhere, stick to the traditional form:
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, Java");
}
}
Both are worth knowing. The short form is great for teaching concepts without ceremony; the long form is what you will see in almost every existing codebase.
The errors everyone still hits
class X is public, should be declared in a file named X.java. With javac, a public class must live in a file with the same name. Rename the file or drop public from the class.
Could not find or load main class. Usually you ran java Hello.class (drop the extension), ran from the wrong directory, or the class is inside a package and you did not use the fully qualified name. Source-file mode sidesteps most of this.
cannot find symbol. A typo, a missing import, or a variable used outside the block where it was declared. Read the line and column the compiler points to; it is almost always exact.
error: missing return statement. Every path through a non-void method must return a value, including the else branch you assumed could never happen.
Scanner waits forever. If your program reads from System.in, it blocks until input arrives. In an online editor, look for a separate stdin box and fill it in before you run.
Reading input cleanly
Small programs usually read a few numbers or lines. Scanner is the friendliest option:
import java.util.Scanner;
public class Sum {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
long total = 0;
for (int i = 0; i < n; i++) {
total += in.nextLong();
}
System.out.println(total);
}
}
For competitive programming with large inputs, BufferedReader with StringTokenizer is noticeably faster, because Scanner does regex parsing for every token.
One common trap: mixing nextInt() and nextLine(). nextInt() leaves the newline in the buffer, so the following nextLine() returns an empty string. Call in.nextLine() once to consume it, or read every line with nextLine() and parse it yourself.
Habits that make experiments faster
Keep one scratch file per idea. Strings.java, Streams.java, Records.java. When you need to remember how Collectors.groupingBy behaves, you rerun a ten-line file instead of searching.
Print types, not just values. System.out.println(x.getClass()) settles a lot of arguments about what a stream or generic method actually returned.
Use records for quick data. record Point(int x, int y) {} gives you a constructor, accessors, equals, hashCode and a readable toString in one line, which keeps practice code short.
Use var for local variables, not for clarity-critical ones. var list = new ArrayList<String>(); is fine; var result = service.process(input); hides the type a reader needs.
Test edge cases on purpose. Empty input, a single element, negative numbers, Integer.MAX_VALUE. Most interview bugs live there, and a scratch file is the cheapest place to find them.
Local JDK or browser editor?
Installing a JDK is the right move once you are building real projects: you want an IDE, a build tool like Maven or Gradle, a debugger and tests. But for a lot of everyday Java work, a full setup is overkill:
- checking how a language feature behaves before using it at work
- practising algorithm problems on a shared or locked-down computer
- teaching a class where students have mixed laptops, or none at all
- reproducing a snippet from a Stack Overflow answer or code review
For those cases, a browser editor that compiles and runs Java on a server is enough. I use the free online Java editor on ToolSphare for quick checks: paste the code, fill in the standard input box if the program reads input, press Run (Ctrl/Cmd+Enter), and read the compiler errors or output in the same window. Drafts are saved automatically and there is no account or install. Like any online compiler it is not a replacement for a real project setup with a build tool, dependencies and a debugger, but for scratch programs it removes all the setup.
Whatever you use, do not paste secrets, API keys or proprietary code into any online compiler. Scratch code and practice problems only.
A short checklist
- Know your JDK version (
java -version); it decides which syntax you can use. - For one-off programs, run the source file directly with
java File.java. - Keep the
mainclass first in the file and its name matching the file. - Read compiler errors from the first one down; later errors are often caused by the first.
- Feed stdin explicitly and watch for the
nextInt()/nextLine()trap. - Move to a proper project with a build tool once you need more than one file or any dependency.
Java has a reputation for ceremony, but for small programs most of it is optional now. Spend your first hour on the code, not on the classpath.
About the author
- I build ToolSphare, a privacy-minded collection of free browser utilities for developers and everyday workflows.
Comments
Join the discussionNo comments yet. Be the first to comment.