The JDK, javac, java and Your First Program
Installing a JDK, compiling with javac and running with java, launching a single source file directly, compact source files with void main(), reading input and trying code in JShell.
Installing a JDK
To write Java you need a JDK (see The JDK and the JRE). Pick a free OpenJDK build of the current LTS version; as of October 2026 that is Java 25. Common ways to install one:
- Any system: download Eclipse Temurin from adoptium.net as an installer or as an archive you unpack anywhere, also without administrator rights.
-
Windows: the installer, or a package manager such
as
winget. - macOS: the installer, or Homebrew.
-
Linux: the distribution's
package manager
(package names such as
openjdk-25-jdkorjava-25-openjdkdiffer per distribution, and older distributions may not offer Java 25 yet), or the vendor's own repository.
Then check the installation in a terminal:
java --version
javac --version
openjdk 25.0.4.1 2026-08-18 LTS OpenJDK Runtime Environment Temurin-25.0.4.1+1 (build 25.0.4.1+1-LTS) OpenJDK 64-Bit Server VM Temurin-25.0.4.1+1 (build 25.0.4.1+1-LTS, mixed mode, sharing) javac 25.0.4.1
The first line gives the version (25.0.4.1, an extra security update released in August 2026) and the "LTS" mark; the other lines name the vendor's build. "Mixed mode" means the JVM both interprets and JIT-compiles.
PATH and JAVA_HOME
The terminal finds java through the
PATH
environment variable: a list of folders that are searched for commands. Installers
usually set it; with an unpacked archive you add the JDK's
bin folder yourself. Many tools (Maven, Gradle,
application servers, IDEs) look at a second variable,
JAVA_HOME, which points to the JDK folder itself, not
to its bin:
# Linux or macOS, for example in ~/.bashrc
export JAVA_HOME="$HOME/.jdk/jdk-25.0.4.1+1"
export PATH="$JAVA_HOME/bin:$PATH"
Compiling with javac, running with java
The classic first program is a class with a
main method. Save it as Hello.java; the
file name must match the class name, including capital letters:
public class Hello {
public static void main(String[] args) {
System.out.println("Hello from Kestrel Energy!");
}
}
Hello from Kestrel Energy!
Building and running it takes two commands. First go to the folder that contains the file (your working directory):
cd ~/projects/java # the folder that contains Hello.java
javac Hello.java # compile: writes Hello.class
ls
java Hello # run: give the class name, not a file name
Hello.class Hello.java Hello from Kestrel Energy!
javac is the Java compiler. It checks
the source code and writes one
class file per class: a
.class file with the class's Java bytecode, its fields
and methods, and the Java version that compiled it. Class files
start with the four bytes CA FE BA BE ("cafe babe", a
joke from Java's early days), followed by that version: 69 for Java
25. You never edit a class file; you recompile.
java is the launcher. It starts a JVM, loads the class
you name, and calls its main method. It looks for
classes in the current folder, or in the folders and
.jar files listed in the classpath (see
Packages, Modules and Build Tools).
The main method
Every program needs an entry point, the place where execution starts. In Java that is the main method. In the classic form, every word of its heading has a meaning:
| Part | Meaning |
|---|---|
public |
Visible from outside the class, so the launcher can call it |
static |
Belongs to the class itself; no object has to be created first |
void |
Returns nothing |
main |
The name the launcher looks for |
String[] args |
The command-line arguments, as an array of strings |
For many years this heading was required exactly like this, and it is still what you will see in almost all existing code. The arguments are whatever follows the class name on the command line:
public class Main {
public static void main(String[] args) {
System.out.println("Arguments given: " + args.length);
for (String arg : args) {
System.out.println("Region: " + arg);
}
}
}
Arguments given: 0
Run without arguments, as here, the array is empty (never
null). Run as java Main North East, the
same program prints Arguments given: 2 and two
Region lines. Small mistakes are reported by the
compiler with file and line. A forgotten semicolon is the classic
one:
public class Main {
public static void main(String[] args) {
System.out.println("Meter report")
System.out.println("Readings processed: 3");
}
}
Main.java:3: error: ';' expected
The compiler reports the line where it noticed the problem, and
under it the source line with a ^ right after the
closing parenthesis. With several errors, fix the first one and
compile again: later messages are often consequences of the first.
Running a single file directly
Since Java 11 you can skip the separate compile step. Give the
launcher a file name ending in .java and it runs in
source-file mode: it
compiles the file in memory and runs it straight away, without
writing any .class file.
java Hello.java
Hello from Kestrel Energy!
This is ideal for learning, small tools and scripts, and it is how
every example on these pages is checked: each one is saved as
Main.java and run with java Main.java on
Java 25. That is why error messages here say Main.java.
Some details of source-file mode:
-
The file is compiled every time, which takes about a second on a
small machine. For a real application you compile once and run the
class files, usually packed in a
.jar. -
Since Java 22 the program may use other
.javafiles in the same folder:java Main.javaalso finds a classTariffinTariff.javanext to it. - Compiler warnings are printed too, but the program still runs.
-
On Linux and macOS a file can run as a script with a
shebang line such as
#!/usr/bin/env -S java --source 25. Such a script file has no.javaextension. If its name is not a valid Java identifier, such ascheck-meters, the compact form below fails (error: bad file name), so put the code inside a named class.
Compact source files
The classic heading asks beginners to accept five unexplained words
before printing one line. Java 25 removed that hurdle. A
compact source file is a
.java file with methods and fields at the top level and
no class around them; it needs only a main method:
void main() {
IO.println("Hello from Kestrel Energy!");
}
Hello from Kestrel Energy!
Run it the same way, with java Hello.java, or compile
it with javac Hello.java. Three things are new here:
-
No class. The compiler wraps the file in an
implicitly declared class for you (it has no name you can use in
code; on disk,
javacnames it after the file:Hello.class). -
An instance
main. Nopublic, nostatic, noString[] args; addString[] argsif you need the arguments. -
The
IOclass (injava.langsince Java 25, so it works in every Java file) offersIO.println,IO.printandIO.readln. In addition, a compact source file automatically imports every package of the modulejava.base, soList,LocalDateorBigDecimalneed noimportline.
Helper methods, fields, records and other classes go in the same
file, before or after main:
void main() {
printHeader("Outage summary");
IO.println(new Region("North", 1250));
IO.println(LocalDate.of(2026, 10, 5).getDayOfWeek());
}
// A helper method, used by main
void printHeader(String title) {
IO.println(title);
IO.println("-".repeat(title.length()));
}
// Types can be declared below main, in the same file
record Region(String name, int customersAffected) {}
Outage summary -------------- Region[name=North, customersAffected=1250] MONDAY
The relaxed main also works in ordinary named classes:
this class runs fine with java Report.java, because the
launcher creates a Report object and calls its
main:
class Report {
void main() {
IO.println("Instance main in a named class");
IO.println(title());
}
String title() {
return "Meter report";
}
}
Instance main in a named class Meter report
A compact source file is meant for small programs and for learning.
It cannot be part of a named package, which every larger program
uses, so the compiler refuses a package line:
package kestrel.billing;
void main() {
IO.println("Billing run");
}
Main.java:1: error: compact source file should not have package declaration
When a program grows, you turn it into a normal class by putting
class Billing { … } around the code and adding the
imports it needs. Nothing else changes.
Writing output
Two families of methods write to the terminal (the standard output stream):
-
System.out.println(…)andSystem.out.print(…): available in every Java version and used in nearly all existing code. -
IO.println(…)andIO.print(…): shorter, since Java 25. They write to the sameSystem.out.
println writes its argument followed by a line break;
print writes without the line break, so the next output
continues on the same line. Without an argument,
println() prints an empty line.
void main() {
String ticket = "INC-20417";
int customers = 1250;
IO.print("Ticket: ");
IO.println(ticket);
IO.println();
IO.println("Customers affected: " + customers);
System.out.println(ticket + " affects " + customers + " customers");
IO.println(customers / 1000.0);
}
Ticket: INC-20417 Customers affected: 1250 INC-20417 affects 1250 customers 1.25
Both accept any value and print its text form. The
+ operator joins strings, and converts a number to text
when one side is a string. Formatted output with
String.format is covered in
Strings and Text. Error messages
belong on the standard error stream:
System.err.println("…"). That separation matters in
operations: a service manager or container platform can collect the
two streams separately.
The rest of this topic uses one style per page: compact source files
with IO.println where the code is short, and classic
classes with System.out.println where classes are the
subject.
Reading input
IO.readln(prompt) prints the prompt, waits until the
user types a line and presses Enter, and returns that line as a
String, without the line break. In the examples below
the typed input is fed in automatically and, unlike in a real
terminal, does not appear in the output. Because the prompt is
printed without a line break, the next output continues on the same
line:
void main() {
String name = IO.readln("Your name: ");
IO.println("Hello, " + name + ". Welcome to the service desk.");
}
Your name: Hello, Amara. Welcome to the service desk.
At the end of the input, for example when input comes from a file
that has ended, IO.readln returns null
(no value) instead of a string:
void main() {
String line = IO.readln();
IO.println(line == null ? "No input: end of stream." : "Read: " + line);
}
No input: end of stream.
Converting input to numbers
Input is always text. To calculate with it, convert it:
Integer.parseInt for whole numbers,
Double.parseDouble for numbers with decimals, or
new BigDecimal(text) for money (see
BigDecimal for money). Unlike Python or JavaScript, Java will not let you multiply a
string by a number at all; the compiler refuses.
void main() {
BigDecimal tariff = new BigDecimal("0.27"); // euros per kWh
String input = IO.readln("Meter reading this month (kWh): ");
int kwh = Integer.parseInt(input.strip());
BigDecimal cost = tariff.multiply(BigDecimal.valueOf(kwh));
IO.println();
IO.println("Usage: " + kwh + " kWh, cost: EUR " + cost);
}
Meter reading this month (kWh): Usage: 342 kWh, cost: EUR 92.34
strip() removes spaces around the input, a common
source of errors. Integer.parseInt throws an exception
when the text is not a whole number, and the program stops:
void main() {
String input = IO.readln("Meter reading (kWh): ");
int kwh = Integer.parseInt(input);
IO.println("Usage: " + kwh + " kWh");
}
Meter reading (kWh): Exception in thread "main" java.lang.NumberFormatException: For input string: "twelve"
Java has no "try-parse" method that returns a success flag, as C# does. For input from people you catch the exception instead, and a loop asks again until the input is valid:
void main() {
int kwh = -1;
while (kwh < 0) {
String input = IO.readln("Meter reading (kWh): ");
try {
kwh = Integer.parseInt(input.strip());
if (kwh < 0) {
IO.println("'" + input + "' is negative, try again.");
}
} catch (NumberFormatException e) {
IO.println("'" + input + "' is not a whole number, try again.");
}
}
IO.println("Accepted: " + kwh + " kWh");
}
Meter reading (kWh): 'abc' is not a whole number, try again. Meter reading (kWh): '-5' is negative, try again. Meter reading (kWh): Accepted: 120 kWh
Each prompt and the message after it end up on one line here,
because the typed answers are not shown. Loops are explained in
Loops and Iteration,
try and catch in
Exceptions and Error Handling.
The older way: Scanner
Before Java 25, the usual way to read from the keyboard was a
Scanner on System.in. You will find it in
most tutorials and exam questions:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Customer number: ");
int customer = scanner.nextInt();
System.out.print("Postal code: ");
String postalCode = scanner.next();
System.out.println();
System.out.println("Customer " + customer + " lives in " + postalCode);
}
}
Customer number: Postal code: Customer 10042 lives in 2511
The output shows Scanner's classic trap: next() reads
one word, so the postal code "2511 CV" lost its letters (a
UK postcode such as "SW1A 1AA" would lose its second half the same
way). nextLine() reads the rest of the line, but after
nextInt() it first returns the empty remainder of the
previous line. IO.readln plus an explicit conversion
avoids both problems. Don't mix the two in one program: after the
first IO.readln, other use of System.in is
unpredictable.
Trying code in JShell
JShell is Java's interactive shell, a
REPL included
in every JDK since Java 9. Start it with jshell, type
an expression or statement, and see the result at once; no class, no
main, no semicolon needed at the end of a line:
$ jshell | Welcome to JShell -- Version 25.0.4.1 | For an introduction type: /help intro jshell> 7 / 2 $1 ==> 3 jshell> int kwh = 342 kwh ==> 342 jshell> kwh * 0.27 $3 ==> 92.34 jshell> LocalDate.of(2026, 10, 5).getDayOfWeek() $4 ==> MONDAY jshell> int x = "12" | Error: | incompatible types: java.lang.String cannot be converted to int | int x = "12"; | ^--^ jshell> /exit | Goodbye
A result you did not store in a variable gets a scratch name such as
$1, which you can use in later lines. Commands start
with a slash: /vars lists your variables,
/imports the imports (in Java 25 the whole
java.base module, which is why
LocalDate works without one), /help
everything else. JShell is ideal for checking what a method returns
or how an operator behaves, as in the first line:
7 / 2 is 3, because both numbers are whole
numbers (see
Variables, Types and Operators).
Editors and certification
Any text editor will do for single files, but an editor that understands Java shows errors while you type, completes names, reorganizes imports and lets you step through code in a debugger. The common choices are IntelliJ IDEA (by JetBrains), Eclipse IDE, Visual Studio Code with the Java extensions, and Apache NetBeans. All of them use the JDK you installed and understand Maven and Gradle projects, so a project made in one opens in the others. For finding and fixing errors in general, see Debugging and Testing.
Oracle's certification for current Java is Oracle Certified Professional: Java SE 25 Developer (exam 1Z0-831), introduced around mid-2026. It is a professional-level exam that tests detailed knowledge of the language and its core library, not a beginner certificate; as of 2026 it is reported to have 50 questions in 120 minutes with a passing score of 68%, but check Oracle's exam page for the current details. Pages in this topic that cover its subjects carry the tag OCP Java SE 25.