Step 5 · Ubuntu
Java: javac, java and jshell
A current Temurin JDK, and the three commands you need to actually use it: javac to compile, java to run, and jshell to try an idea without writing a file at all.
Run this
$ curl -fsSL https://aikaryashala.com/system_setup/scripts/install_java.sh | bash
Then reload your shell so JAVA_HOME is set:
$ source ~/.bashrc
$ java -version
It also writes two sample files into ~/java-samples for the
exercise below.
What gets installed
javacThe compiler. Turns.javasource into.classbytecode.javaThe runtime. Executes bytecode — and, these days, single source files directly.jshellAn interactive Java prompt. Type an expression, see the result.
jar and javap come along inside the same JDK
package. Maven and Gradle do not — they are
step 9, installed separately when a
project actually needs a build tool.
Ubuntu packages a JDK, but which versions it offers depends on your release and they can lag well behind. Eclipse Adoptium publishes Temurin — the same OpenJDK, built and tested by the community that maintains it — through its own APT repository, so every LTS is available and upgrades are clean. The script adds that repository, and falls back to Ubuntu's package if it cannot be reached.
Try it without writing a file
jshell is the fastest way to test an idea. No class, no
main, no compile step.
$ jshell
| Welcome to JShell. For an introduction type: /help intro
jshell> int x = 21
x ==> 21
jshell> x * 2
$2 ==> 42
jshell> System.out.println("hello")
hello
jshell> "abc".toUpperCase()
$4 ==> "ABC"
jshell> int[] nums = {12, 7, 3, 21}
nums ==> int[4] { 12, 7, 3, 21 }
jshell> java.util.Arrays.stream(nums).sum()
$6 ==> 43
jshell> /exit
Useful commands inside jshell: /list shows what you have typed,
/vars the variables you have defined, /imports what
is already imported, /open File.java loads a file, and
/exit leaves.
The two sample files
One class calling another is where javac stops being trivial, so
the exercise uses two files. Both land in ~/java-samples, and are
downloadable here: Report.java and
Stats.java.
public class Stats {
/** The average. Returns 0 for an empty array. */
public static double mean(int[] numbers) {
if (numbers.length == 0) {
return 0;
}
int total = 0;
for (int n : numbers) {
total += n;
}
return (double) total / numbers.length;
}
/** The largest value. */
public static int max(int[] numbers) {
int best = numbers[0];
for (int n : numbers) {
if (n > best) {
best = n;
}
}
return best;
}
/** The smallest value. */
public static int min(int[] numbers) {
int worst = numbers[0];
for (int n : numbers) {
if (n < worst) {
worst = n;
}
}
return worst;
}
/** How far apart the largest and smallest values are. */
public static int spread(int[] numbers) {
return max(numbers) - min(numbers);
}
}
public class Report {
public static void main(String[] args) {
int[] readings = parse(args);
System.out.println("--- readings ---");
System.out.printf(" count: %d%n", readings.length);
System.out.printf(" mean: %.2f%n", Stats.mean(readings));
System.out.printf(" max: %d%n", Stats.max(readings));
System.out.printf(" min: %d%n", Stats.min(readings));
System.out.printf(" spread: %d%n", Stats.spread(readings));
}
/** Use the numbers given on the command line, or a default set. */
private static int[] parse(String[] args) {
if (args.length == 0) {
return new int[] {12, 7, 3, 21, 9, 15};
}
int[] numbers = new int[args.length];
for (int i = 0; i < args.length; i++) {
numbers[i] = Integer.parseInt(args[i]);
}
return numbers;
}
}
Compile and run them
$ cd ~/java-samples
$ javac Report.java
$ java Report
--- readings ---
count: 6
mean: 11.17
max: 21
min: 3
spread: 18
$ java Report 4 8 15 16 23 42
--- readings ---
count: 6
mean: 18.00
max: 42
min: 4
spread: 38
javac Report.java produced both
Report.class and Stats.class. javac follows the
references it finds and compiles what it needs, as long as the source is in
the same directory. Run ls *.class to see both.
Skip the compile step entirely
Modern Java runs source files directly — it compiles in memory and leaves no
.class file behind. Ideal while learning:
$ java Report.java
$ java Report.java 4 8 15 16
Load them into jshell
$ jshell
jshell> /open Stats.java
jshell> int[] nums = {12, 7, 3, 21, 9, 15}
jshell> Stats.mean(nums)
$3 ==> 11.166666666666666
jshell> Stats.spread(nums)
$4 ==> 18
This is the fastest way to poke at one method without writing a
main around it.
A public class Report has to live in a file called
Report.java — same spelling, same capitalisation. Getting this
wrong is the first error nearly everyone hits.
JAVA_HOME
Many Java tools locate the JDK through the JAVA_HOME environment
variable. The script works out the right path and adds it to
~/.bashrc:
$ echo $JAVA_HOME
/usr/lib/jvm/temurin-25-jdk-amd64
If you install more than one JDK, switch between them with:
$ sudo update-alternatives --config java
$ sudo update-alternatives --config javac
Check it worked
$ java -version
$ javac -version
$ echo $JAVA_HOME
$ ls ~/java-samples
One thing left
Ubuntu is running, and you have the core commands plus working toolchains for C, Python and Java. Now check they all really work and report it: step 5.5 tests every tool from steps 2 to 5 by actually using it, and sends the result.
$ curl -fsSL https://aikaryashala.com/system_setup/scripts/report_completion.sh | bash
After that, everything else is optional — including Maven and Gradle for when a Java project needs a real build tool.