A badly configured environment is the number one reason people give up on learning Java. The error messages are cryptic ('javac' is not recognized as a command, JAVA_HOME is not set, UnsupportedClassVersionError) and have nothing to do with programming. This lesson removes that obstacle once and for all: you will install a JDK, check that it works, understand why JAVA_HOME and PATH exist instead of just copying commands, compile and run your first program from the terminal, and choose the IDE you will work with for the whole course. By the end you will have the bibliotech project created and ready for you to write code.
Contents
- Which JDK to choose and where to download it
- Installing on Windows, macOS and Linux
- Verifying the installation:
java -versionandjavac -version JAVA_HOMEandPATH: what they are and how to configure them- Your first program from the terminal:
HelloBiblioTech.java - JShell: the laboratory for learning
- Choosing an IDE and creating the
bibliotechproject - Recommended folder structure
- Common Mistakes and Tips
- Exercises
- Which JDK to choose and where to download it
The previous lesson made it clear that you need a JDK (not a JRE), because the JDK includes the javac compiler. What you may not have expected is that there are several different JDKs for the same Java version.
They all start from the same source code, OpenJDK, which is the open-source reference implementation. What changes between distributions is who builds it, who signs it, for how long they publish patches and under which licence.
| Distribution | Who publishes it | Licence and cost | Recommendation |
|---|---|---|---|
| Eclipse Temurin | Eclipse Adoptium | Free (GPL+CE), free in production too | The course's recommended option |
| Oracle JDK | Oracle | Free for development; specific terms in production | Valid, but check the licence if it is for a company |
| Amazon Corretto | Amazon | Free, long support | Good if you deploy on AWS |
| Azul Zulu | Azul Systems | Free, with optional commercial support | A good alternative |
| Microsoft Build of OpenJDK | Microsoft | Free | Good if you work on Azure |
Practical recommendation: download Eclipse Temurin 21 (or 17 if you prefer to stick strictly to the course's minimum version) from adoptium.net. It is free with no strings attached, widely used in production and requires no user account to download.
About the version: this course uses Java 17 as its reference and explicitly flags anything that needs a higher version. If you install Java 21, everything will work the same and you will also have the most recent improvements available. Both are LTS.
One detail when downloading: pick the right package for your processor. On Windows and most Linux PCs it will be x64. On a modern Mac (M1, M2, M3, M4) it will be aarch64/ARM64; on an older Mac, x64. Downloading the wrong binary causes baffling startup errors.
- Installing on Windows, macOS and Linux
Windows
- On
adoptium.net, download the Temurin.msiinstaller for Windows x64, version 21 (LTS), JDK (not JRE). - Run the installer. On the custom options screen, tick these two boxes, which will save you the whole of section 4:
- Add to PATH
- Set JAVA_HOME variable
- Finish the installation. The JDK will normally end up in
C:\Program Files\Eclipse Adoptium\jdk-21.x.x-hotspot. - Open a new terminal (PowerShell or Command Prompt). This step is mandatory: terminals that are already open cannot see the new environment variables.
macOS
The most convenient way is Homebrew. If you do not have it, install it from brew.sh. Then:
# Install Temurin 21
brew install --cask temurin@21
# Check which JDK the system has detected
/usr/libexec/java_home -VIf you would rather not use Homebrew, download the .pkg installer from Adoptium and run it; the JDK will be installed under /Library/Java/JavaVirtualMachines/.
macOS ships a very handy utility, /usr/libexec/java_home, which locates the installed JDK. You will use it in section 4.
Linux
On Debian- or Ubuntu-based distributions, the package from the official repositories is usually enough:
On Fedora or RHEL:
Watch the suffixes: on Debian/Ubuntu the -jdk package includes the compiler, and on Fedora it is the -devel suffix. If you install openjdk-21-jre or plain java-21-openjdk, you will have a JVM but no javac.
If your distribution does not offer the version you want, download the .tar.gz from Adoptium and unpack it under /opt:
A note for anyone juggling several versions: tools such as SDKMAN! (sdkman.io) let you install and switch between JDKs with a single command (sdk install java 21-tem, sdk use java 17-tem). It is what many people who work with projects of different ages use. It is not required for the course, but it is worth knowing about.
- Verifying the installation:
java -version and javac -version
java -version and javac -versionThis is the moment of truth. Open a new terminal and run both commands:
Correct output looks something like this (the exact numbers will vary):
openjdk version "21.0.5" 2024-10-15 LTS OpenJDK Runtime Environment Temurin-21.0.5+11 (build 21.0.5+11-LTS) OpenJDK 64-Bit Server VM Temurin-21.0.5+11 (build 21.0.5+11-LTS, mixed mode, sharing) javac 21.0.5
Let's interpret what each command is telling you:
java -versionshows the version of the JVM that will run. If this command responds, you can run Java programs.javac -versionshows the version of the compiler. If this command fails but the previous one works, you have a JRE or an incomplete installation: you will not be able to compile.
It is essential that both numbers match. On machines where several JDKs have been installed over time it is very common to find a java from version 8 and a javac from version 21. In that scenario you compile modern code that your JVM refuses to run, and the dreaded UnsupportedClassVersionError shows up.
If either command answers with "is not recognized as an internal or external command" (Windows) or "command not found" (macOS/Linux), the JDK is installed but the system does not know where to find it. That is exactly what the next section solves.
JAVA_HOME and PATH: what they are and how to configure them
JAVA_HOME and PATH: what they are and how to configure themWhat they really are
They are two environment variables: text values that the operating system makes available to every program it starts.
-
PATHis a list of directories separated by;(Windows) or:(macOS/Linux). When you typejavacin the terminal, the system walks that list in order looking for an executable with that name and runs the first one it finds. If your JDK'sbindirectory is not on thePATH, the terminal cannot findjavaceven though it is perfectly installed. And if there are two JDKs on thePATH, the one that appears first wins: that is the usual cause of mismatched versions. -
JAVA_HOMEpoints to the JDK's root folder (the one containingbin,lib,conf…), not to an executable. It is not used by the terminal but by other tools: Maven, Gradle, application servers, startup scripts and some IDEs read it to know which JDK they should work with. You can have Java working in the terminal withoutJAVA_HOME, but as soon as you use a tool from the ecosystem it will ask for it.
flowchart LR
A["You type<br/>javac Hello.java"] --> B["The system walks PATH<br/>directory by directory"]
B --> C{"Does it find<br/>javac?"}
C -->|"Yes"| D["It runs it<br/>(the first on the list)"]
C -->|"No"| E["command not found"]
F["Maven, Gradle,<br/>servers…"] --> G["They read JAVA_HOME<br/>to locate the JDK"]
Windows
- Press the Windows key and search for "Edit the system environment variables".
- Click Environment Variables….
- Under System variables, click New:
- Name:
JAVA_HOME - Value: the JDK's root path, for example
C:\Program Files\Eclipse Adoptium\jdk-21.0.5.11-hotspot
- Name:
- Select the
Pathvariable, click Edit and add a new entry:%JAVA_HOME%\bin. Using%JAVA_HOME%instead of the full path is good practice: the day you upgrade the JDK, you will only have to changeJAVA_HOME. - If there are old entries from other Java installations (for example
C:\ProgramData\Oracle\Java\javapath), move them below yours or delete them. - Accept everything and open a new terminal.
To check it:
In PowerShell they would be $env:JAVA_HOME and Get-Command java.
macOS and Linux
The configuration goes in your shell's startup file: ~/.zshrc if you use zsh (the default on modern macOS) or ~/.bashrc / ~/.bash_profile if you use bash. Add at the end:
On macOS:
On Linux (adjust the path to your actual installation):
Note the detail in PATH="$JAVA_HOME/bin:$PATH": we put our bin in front of the existing PATH, so that it wins over any other installed Java. Then reload the configuration:
On Linux, if you have several versions installed through the package manager, the sudo update-alternatives --config java command lets you choose which one is the system default.
- Your first program from the terminal:
HelloBiblioTech.java
HelloBiblioTech.javaYou are going to compile and run a program without an IDE. Even if you later always work with IntelliJ or Eclipse, doing it by hand once is what turns "compiling" into something concrete instead of a magic button.
Create a working folder and, inside it, a file named exactly HelloBiblioTech.java:
public class HelloBiblioTech {
public static void main(String[] args) {
System.out.println("BiblioTech - Nexus Software");
System.out.println("Internal technical library management system");
System.out.println("Version 0.1");
}
}We are not going to dissect this structure yet (that is lesson 01-03), but there is one rule you need right now: the file name must match the name of the public class, capital letters included. The class is called HelloBiblioTech, so the file is HelloBiblioTech.java. If you save it as hellobibliotech.java or Hello.java, the compiler will reject it with a message like "class HelloBiblioTech is public, should be declared in a file named HelloBiblioTech.java".
Compiling and running
# 1. Compile: generates HelloBiblioTech.class
javac HelloBiblioTech.java
# 2. Check that the .class was created
ls # on Windows: dir
# 3. Run: CAREFUL, WITHOUT the .class extension
java HelloBiblioTechExpected output:
Both commands deserve a detailed explanation:
javac HelloBiblioTech.javatakes the file name with its extension. It translates the source into bytecode and createsHelloBiblioTech.classin the same directory. If there are no errors it prints nothing: in command-line tools, silence means success.java HelloBiblioTechtakes the class name, without an extension and without.class. This is where almost everybody gets it wrong the first time by typingjava HelloBiblioTech.class, which produces a "class not found" error. The reason is conceptual:javadoes not open a file, it loads a class by name, looking for it on the classpath (by default, the current directory).
Running the source directly (Java 11 and above)
Since Java 11 there is a very convenient shortcut for single-file programs:
Notice that here it does carry the .java extension. This mode compiles the file in memory and runs it on the fly, leaving no .class on disk. It is perfect for quick tests and for learning, but it has limits: it only works for programs contained in a single source file. As soon as your project has several classes (from module 3 onwards), you will go back to javac or, more likely, let the IDE do it for you.
| Command | Extension | What it does | When to use it |
|---|---|---|---|
javac File.java |
yes, .java |
Compiles and generates a .class |
Whenever you want the bytecode |
java ClassName |
no | Runs an already compiled class | Normal execution |
java File.java |
yes, .java |
Compiles in memory and runs | Quick single-file tests (Java 11+) |
- JShell: the laboratory for learning
Java 9 introduced JShell, an interactive console (what other languages call a REPL: read-eval-print loop). It lets you write standalone expressions and see the result instantly, without creating a file, without a class and without a main.
A typical session:
jshell> int daysLate = 12
daysLate ==> 12
jshell> double dailyRate = 0.25
dailyRate ==> 0.25
jshell> double fine = daysLate * dailyRate
fine ==> 3.0
jshell> String title = "Effective Java"
title ==> "Effective Java"
jshell> title.toUpperCase()
$5 ==> "EFFECTIVE JAVA"
jshell> title.length()
$6 ==> 14
jshell> /exit
| GoodbyeNotice a couple of details:
- Semicolons at the end of each line are not required (although you may write them).
- When an expression is not stored in a variable, JShell assigns it an automatic name (
$5,$6) that you can reuse afterwards.
JShell commands start with a slash:
| Command | Purpose |
|---|---|
/vars |
Lists the variables defined in the session |
/list |
Shows all the code you have written |
/imports |
Shows the active imports |
/help |
Full help |
/exit |
Quit |
What it will be useful for during the course: every time you are unsure how something behaves ("what exactly does 5/2 return?", "does trim() also remove tabs?"), opening JShell and testing it in five seconds is infinitely better than guessing. Use it freely in modules 1, 4 and 5.
- Choosing an IDE and creating the
bibliotech project
bibliotech projectAn IDE (Integrated Development Environment) is an editor that also compiles, runs, debugs, autocompletes and spots errors as you type. In Java it is practically essential: the professional ecosystem assumes it.
| IDE | Cost | Strengths | Weaknesses | Recommended for |
|---|---|---|---|---|
| IntelliJ IDEA Community | Free (Apache 2.0) | The best autocompletion and refactoring; excellent debugger; very polished | Uses quite a lot of memory; the free version does not include advanced web/Spring support | The course's recommended option |
| Eclipse IDE | Free | Highly extensible; very fast incremental compiler; standard in many companies | Less modern interface; the initial setup can be confusing | Anyone working at companies that already use it |
| Visual Studio Code | Free | Lightweight; multi-language; instant startup | Needs the Extension Pack for Java; less powerful for large refactorings | Anyone already using VS Code for other languages |
| NetBeans | Free | Everything integrated out of the box | Smaller community | Occasional use |
For this course the recommendation is IntelliJ IDEA Community Edition: its real-time error detection and its suggestions are, in practice, an invisible teacher correcting you as you type. That said, any of the four will do; the course code is identical in all of them.
Creating the project in IntelliJ IDEA Community
- Open IntelliJ and click New Project.
- Fill in:
- Name:
bibliotech - Location: the folder where you keep your projects
- Language: Java
- Build system: IntelliJ (do not pick Maven or Gradle yet; we will see them in module 11)
- JDK: select the 21 (or 17) you installed. If it does not appear, use Add JDK… and browse to the
JAVA_HOMEfolder. - Untick Add sample code so you start from scratch.
- Name:
- Click Create.
- In the left-hand panel, right-click the
srcfolder → New → Java Class and typeHelloBiblioTech. - Paste the code from section 5 and click the green triangle in the margin to run it. You will see the output in the Run window.
In Eclipse
File → New → Java Project, name bibliotech, select JRE/JDK 21 and untick the creation of module-info.java. Then right-click src → New → Class, name HelloBiblioTech, and tick the box that generates the main method.
In VS Code
Install Microsoft's Extension Pack for Java. Then Ctrl+Shift+P → Java: Create Java Project → No build tools → choose a folder and the name bibliotech.
- Recommended folder structure
Even though in this module you work with one or two files, it is worth adopting the standard Java-world structure right away. It is the one Maven expects (module 11), the one every IDE recognises and the one you will see in any professional project:
bibliotech/ ├── src/ │ └── main/ │ ├── java/ <- .java source code │ │ └── com/ │ │ └── nexussoftware/ │ │ └── bibliotech/ │ │ └── BiblioTechApp.java │ └── resources/ <- data and configuration files ├── src/ │ └── test/ │ └── java/ <- automated tests (module 11) ├── out/ (or target/) <- generated .class files └── README.md
Three important ideas about this structure:
- Source code and compiled code live in separate folders. Never mix
.javaand.classin the same place in a real project: the output folder is deleted and regenerated constantly, and it is not kept in version control. - The
com/nexussoftware/bibliotechdirectories mirror the packagecom.nexussoftware.bibliotech. In Java, the package hierarchy must match the folder hierarchy. We will explain it in detail in the next lesson; for now, just remember that it is not decorative. - Separate
src/mainandsrc/testanticipate the arrival of automated tests.
About build tools: Maven and Gradle automate compilation, dependency management and packaging. They are indispensable in any real project and we will devote the whole of lesson 11-05 to them. You do not need them in this module: adding them now would only put noise between you and the language.
Common Mistakes and Tips
'javac' is not recognized as a command. The JDK'sbindirectory is not on thePATH, or you installed a JRE. Review section 4 and remember to open a new terminal after touching environment variables.java -versionandjavac -versionshow different versions. You have more than one Java installed and thePATHis mixing them. Runwhere java/where javac(Windows) orwhich -a java javac(Unix) to see every active path and put the JDK you want at the start of thePATH.Error: Could not find or load main class HelloBiblioTech.class. You typedjava HelloBiblioTech.class. Thejavacommand takes the class name, without an extension.class X is public, should be declared in a file named X.java. The file name does not match the public class name. Java is case-sensitive here too.UnsupportedClassVersionError: ... has been compiled by a more recent version of the Java Runtime. You compiled with a JDK newer than the JVM you are running on. Unify the versions, or compile withjavac --release 17.- Not seeing the real file extensions on Windows. File Explorer hides known extensions by default, and you end up with
HelloBiblioTech.java.txt. Turn on View → File name extensions before going any further. - Tip: keep your projects in a path without spaces or accented characters (
C:\dev\bibliotech, notC:\My Documents\Programming\). Many tools in the Java ecosystem still have trouble with such paths. - Tip: spend fifteen minutes learning three shortcuts in your IDE: run the program, find a class by name, and rename an identifier across the whole project. It is the best time investment of the course.
Exercises
Exercise 1: Auditing your environment
Carry out a full check of your installation and note down the results:
- The version reported by
java -versionandjavac -version. Do they match? - The value of
JAVA_HOME. - The physical path of the
javaexecutable being used. - Is there more than one Java installed on your machine?
Write the exact commands you would use on your operating system and explain what each result tells you.
Exercise 2: Compiling the BiblioTech card by hand
Without using the IDE, create in a folder called tests a file BookCard.java that prints the card for the book "Effective Java" to the console in exactly this format:
Compile it and run it with javac and java. Then run it with the Java 11+ single-file mode. Answer: what difference do you see in the folder contents after each method?
Exercise 3: Exploring with JShell
Open JShell and, without writing any file, calculate the fine for a BiblioTech loan: define a variable daysLate with value 9, a constant dailyRate with value 0.25 and calculate the fine. Then use /vars to list what you have defined. Finally, try typing title.length() on a title variable you have not defined and look at the error message.
Solutions
Solution 1
On Windows (PowerShell):
On macOS / Linux:
java -version
javac -version
echo $JAVA_HOME
which java
which -a java javac # shows ALL the matches on the PATHInterpreting the results:
- If
java -versionandjavac -versiongive the same major number (for example, both 21), your environment is consistent and you can compile and run without surprises. JAVA_HOMEmust point to the JDK's root folder. If it points to.../binor is empty, tools like Maven will fail later on.which -a java(orwhere.exe java) is the key to this exercise: if it returns more than one line, you have several Java installations and the one in charge is the one on the first line. That is where most version inconsistencies come from.
Solution 2
The BookCard.java file:
public class BookCard {
public static void main(String[] args) {
// Each println prints its text and moves to the next line.
System.out.println("=== BiblioTech: book card ===");
System.out.println("Title: Effective Java");
System.out.println("ISBN: 978-0000000001");
System.out.println("Status: available");
}
}Classic compilation and execution:
Running the source directly:
Observable difference: after javac, the folder contains two files: BookCard.java and BookCard.class. After running the source directly, there is still only BookCard.java: the compilation happens in memory and the bytecode is discarded when the program ends. That is why the direct mode is handy for testing but useless for distribution: it produces nothing you can hand over.
Solution 3
jshell> int daysLate = 9
daysLate ==> 9
jshell> final double dailyRate = 0.25
dailyRate ==> 0.25
jshell> double fine = daysLate * dailyRate
fine ==> 2.25
jshell> /vars
| int daysLate = 9
| double dailyRate = 0.25
| double fine = 2.25
jshell> title.length()
| Error:
| cannot find symbol
| symbol: variable title
| title.length()
| ^---^Two observations worth internalising right now:
- The result
2.25comes from multiplying anintby adouble: Java automatically converts the integer to a decimal before operating. It is a widening conversion, and you will study it formally in lesson 01-04. - The
cannot find symbolerror is one of the most frequent messages from the Java compiler and it always means the same thing: you are using a name the compiler does not know. The typical causes are not having declared the variable, having typed it with different capitalisation, or having declared it in a different scope. Learning to recognise it instantly will save you a lot of time.
Conclusion
You now have a working environment: an LTS JDK installed, java and javac responding with the same version, JAVA_HOME and PATH configured and understood, your first HelloBiblioTech program compiled and run from the terminal, JShell available for experimenting and the bibliotech project created in your IDE with the standard folder structure. Everything from here on is programming.
In the next lesson, Basic Syntax and Structure, we will dissect that HelloBiblioTech.java file word by word: what exactly public static void main(String[] args) means, what statements and blocks are, how code is commented, which naming conventions everyone follows in Java and why, how packages work and how they relate to directories, and how to read the compiler's error messages so that you never get stuck.
Java Programming Course
Module 1: Introduction to Java
- Introduction to Java
- Setting Up the Development Environment
- Basic Syntax and Structure
- Variables and Data Types
- Operators
- Console Input and Output
- Your First Complete Program: BiblioTech
Module 2: Control Flow
- Conditional Statements
- Loops
- Switch Statements
- Break and Continue
- Debugging and Execution Traces
- Project: The BiblioTech Interactive Menu
Module 3: Object-Oriented Programming
- Introduction to OOP
- Classes and Objects
- Methods
- Constructors
- Inheritance
- Polymorphism
- Encapsulation
- Abstraction
- The Object Class: equals, hashCode and toString
Module 4: Advanced Object-Oriented Programming
- Interfaces
- Abstract Classes
- Inner Classes
- Anonymous Classes
- Lambda Expressions
- Functional Interfaces and Method References
- Enums and Records
Module 5: Data Structures and Collections
- Arrays
- The Collections Framework
- ArrayList
- LinkedList
- HashMap
- HashSet
- Queue and Deque
- Stack
- Sorting and Searching Collections
Module 6: Exception Handling
- Introduction to Exceptions
- The Try-Catch Block
- Throw and Throws
- Custom Exceptions
- The Finally Block
- Try-with-resources and AutoCloseable
- Error Handling Strategies and Logging
Module 7: File Input/Output
- Reading Files
- Writing Files
- File Streams
- BufferedReader and BufferedWriter
- Serialization
- The NIO.2 API: Path and Files
- Interchange Formats: CSV and Properties
Module 8: Multithreading and Concurrency
- Introduction to Multithreading
- Creating Threads
- Thread Lifecycle
- Synchronization
- Concurrency Utilities
- Concurrent Collections and Atomic Variables
- Asynchronous Tasks with CompletableFuture
Module 9: Networking
- Introduction to Networking
- Sockets
- ServerSocket
- DatagramSocket and DatagramPacket
- URL and HttpURLConnection
- The Modern HTTP Client
Module 10: Advanced Topics
- Generics
- Annotations
- Reflection
- Java 8 Features: Streams and Optional
- Dates and Times with java.time
- Java 9 and Beyond
- Memory, Garbage Collection and Performance
Module 11: Java Frameworks and Libraries
- Introduction to Java Frameworks
- Spring Framework
- Hibernate
- JUnit
- Maven
- Advanced Testing with Mockito
- Essential Ecosystem Libraries
