Understand Java's architecture, platform independence via the JVM, and set up a professional development environment with JDK 17+, IntelliJ IDEA, and your first compiled program.
Java has powered enterprise software for over 25 years, running everything from Android apps and financial trading systems to cloud-native microservices at Netflix and Amazon. Its enduring relevance comes from one fundamental design decision: compile once to bytecode, run anywhere a JVM exists. Unlike C or C++ where code must be recompiled per platform, Java code compiles to a platform-neutral intermediate format that the JVM interprets and JIT-compiles at runtime.
In this lesson you will understand why Java remains the most widely deployed enterprise language, how the compilation and execution pipeline works, and how to set up a productive local development environment. By the end you will have JDK 17 installed, understand the relationship between JDK, JRE, and JVM, and have successfully compiled and run your first Java program from the command line. This foundation underpins everything that follows in the course.
Understanding the three-layer architecture is essential before writing any code.
JVM (Java Virtual Machine) is an abstract computing machine that executes Java bytecode. It is platform-specific — there is a different JVM implementation for Windows x64, macOS ARM, and Linux x86 — but the bytecode it executes is identical across all platforms. The JVM also manages memory through garbage collection, enforces security via bytecode verification, and applies JIT (Just-In-Time) compilation to hot code paths for near-native performance.
JRE (Java Runtime Environment) packages the JVM together with the Java standard library (java.lang, java.util, java.io, etc.) and supporting files. End users who only run Java applications need the JRE. Since Java 9, the JRE is no longer distributed separately — you use the JDK directly.
JDK (Java Development Kit) is the superset: JRE plus the compiler (javac), documentation generator (javadoc), archiver (jar), debugger (jdb), and other tools developers need. You always install the JDK for development.
1JDK
2├── JRE
3│ ├── JVM (platform-specific execution engine)
4│ └── Java Standard Library (rt.jar / modules)
5└── Development Tools
6 ├── javac (compiler)
7 ├── jar (archiver)
8 ├── javadoc (doc generator)
9 └── jdb (debugger)Key relationship: JDK ⊃ JRE ⊃ JVM. Always install the JDK when developing.
Java 17 is the current Long-Term Support release (supported until September 2029), making it the recommended choice for new projects. Adoptium (formerly AdoptOpenJDK) provides free, production-grade OpenJDK builds.
Windows:
1# Option 1: winget (Windows 11/10)
2winget install EclipseAdoptium.Temurin.17.JDK
3
4# Option 2: Manual
5# Download .msi from https://adoptium.net
6# Run installer, check "Set JAVA_HOME" and "Add to PATH"
7
8# Verify
9java -version
10javac -versionmacOS:
1# Homebrew (recommended)
2brew install --cask temurin@17
3
4# Add to ~/.zshrc
5export JAVA_HOME=$(/usr/libexec/java_home -v 17)
6export PATH="$JAVA_HOME/bin:$PATH"
7
8source ~/.zshrc
9java -versionLinux (Ubuntu/Debian):
1sudo apt update
2sudo apt install temurin-17-jdk # after adding Adoptium apt repo
3
4# Or via SDKMAN (works on any POSIX shell)
5curl -s "https://get.sdkman.io" | bash
6sdk install java 17.0.9-temAfter installation, java -version should print openjdk version "17.x.x" and javac -version should print javac 17.x.x. If either command is not found, your PATH is not set correctly.
Many build tools (Maven, Gradle) and application servers read JAVA_HOME to locate the JDK. Setting it correctly avoids subtle build failures.
Windows (System Properties → Environment Variables):
1Variable name: JAVA_HOME
2Variable value: C:\Program Files\Eclipse Adoptium\jdk-17.0.9.9-hotspot
3
4Append to Path: %JAVA_HOME%\binVerify in a new Command Prompt:
1echo %JAVA_HOME%
2java -version
3javac -versionmacOS/Linux (~/.zshrc or ~/.bashrc):
1export JAVA_HOME=/usr/lib/jvm/temurin-17 # Linux path
2# macOS: export JAVA_HOME=$(/usr/libexec/java_home -v 17)
3export PATH="$JAVA_HOME/bin:$PATH"SDKMAN makes version switching trivial:
1sdk list java # see all available versions
2sdk install java 17.0.9-tem
3sdk install java 21.0.1-tem
4sdk use java 21.0.1-tem # switch for current session
5sdk default java 17.0.9-tem # set global default
6java -versionSDAKMAN is strongly recommended for developers who work with multiple Java versions across projects.
Understanding what happens between source code and program output demystifies error messages and performance characteristics.
1Source Code (.java)
2 │
3 ▼ javac (compiler)
4Bytecode (.class)
5 │
6 ▼ java (JVM launcher)
7 Class Loader
8 │
9 ▼
10 Bytecode Verifier
11 │
12 ▼
13 Interpreter / JIT Compiler
14 │
15 ▼
16 Native Machine Code → ExecutionStep 1 — Compile: javac HelloWorld.java reads the source, performs type checking, and emits HelloWorld.class containing JVM bytecode.
Step 2 — Load: The JVM's class loader reads .class files from the filesystem, network, or JAR archives into memory.
Step 3 — Verify: The bytecode verifier checks that the loaded class won't violate JVM security constraints (no out-of-bounds array access, no type violations).
Step 4 — Execute: The interpreter executes bytecode line by line. As the JIT compiler detects hot paths (code executed frequently), it compiles them to native machine code, dramatically improving throughput.
1# Compile
2javac HelloWorld.java # produces HelloWorld.class
3
4# Run
5java HelloWorld # no .class extension!
6
7# Inspect bytecode (educational)
8javap -c HelloWorld.classThe separation of compilation and execution is why the same HelloWorld.class runs identically on Windows, macOS, and Linux — each has its own JVM implementation, but they all execute the same bytecode specification.
Every Java program starts with a class. The main method is the entry point the JVM calls when you run the program.
1// HelloWorld.java — filename MUST match the public class name
2public class HelloWorld {
3
4 // Entry point: JVM calls this method to start execution
5 // 'public' — accessible by JVM from outside the class
6 // 'static' — belongs to the class, not an instance
7 // 'void' — returns nothing
8 // 'String[] args' — command-line arguments array
9 public static void main(String[] args) {
10 System.out.println("Hello, World!"); // print + newline
11 System.out.print("No newline here"); // print only
12 System.out.printf("Formatted: %s%n", "Java"); // printf-style
13
14 // Reading command-line arguments
15 if (args.length > 0) {
16 System.out.println("Hello, " + args[0] + "!");
17 }
18 }
19}1# Compile
2javac HelloWorld.java
3
4# Run without arguments
5java HelloWorld
6# Output: Hello, World!
7
8# Run with arguments
9java HelloWorld Alice
10# Output: Hello, World!\nHello, Alice!Rules that cause compiler errors if violated:
public class name exactly (case-sensitive): HelloWorld.java for public class HelloWorld;{} delimit code blocksSystem ≠ systemA complete Java program that prints system information, demonstrating compilation, execution, command-line arguments, and standard library usage.
1import java.util.Arrays;
2
3/**
4 * EnvironmentInfo.java
5 * Verifies your Java setup and demonstrates basic program structure.
6 * Compile: javac EnvironmentInfo.java
7 * Run: java EnvironmentInfo arg1 arg2
8 */
9public class EnvironmentInfo {
10
11 public static void main(String[] args) {
12 printHeader();
13 printJavaInfo();
14 printSystemInfo();
15 printCommandLineArgs(args);
16 printMemoryInfo();
17 }
18
19 private static void printHeader() {
20 System.out.println("======================================");
21 System.out.println(" Java Environment Verification Tool ");
22 System.out.println("======================================");
23 }
24
25 private static void printJavaInfo() {
26 System.out.println("\n--- Java Version Info ---");
27 System.out.printf("Java Version : %s%n",
28 System.getProperty("java.version"));
29 System.out.printf("Java Vendor : %s%n",
30 System.getProperty("java.vendor"));
31 System.out.printf("JVM Name : %s%n",
32 System.getProperty("java.vm.name"));
33 System.out.printf("Class Version : %s%n",
34 System.getProperty("java.class.version"));
35 System.out.printf("JAVA_HOME : %s%n",
36 System.getProperty("java.home"));
37 }
38
39 private static void printSystemInfo() {
40 System.out.println("\n--- System Info ---");
41 System.out.printf("OS Name : %s%n",
42 System.getProperty("os.name"));
43 System.out.printf("OS Version : %s%n",
44 System.getProperty("os.version"));
45 System.out.printf("OS Architecture : %s%n",
46 System.getProperty("os.arch"));
47 System.out.printf("User Name : %s%n",
48 System.getProperty("user.name"));
49 System.out.printf("Working Dir : %s%n",
50 System.getProperty("user.dir"));
51 }
52
53 private static void printCommandLineArgs(String[] args) {
54 System.out.println("\n--- Command-Line Arguments ---");
55 if (args.length == 0) {
56 System.out.println("No arguments provided.");
57 System.out.println("Try: java EnvironmentInfo hello world");
58 } else {
59 System.out.printf("Argument count : %d%n", args.length);
60 System.out.printf("Arguments : %s%n",
61 Arrays.toString(args));
62 for (int i = 0; i < args.length; i++) {
63 System.out.printf(" args[%d] : %s%n", i, args[i]);
64 }
65 }
66 }
67
68 private static void printMemoryInfo() {
69 Runtime runtime = Runtime.getRuntime();
70 long maxMemory = runtime.maxMemory() / (1024 * 1024);
71 long totalMemory = runtime.totalMemory() / (1024 * 1024);
72 long freeMemory = runtime.freeMemory() / (1024 * 1024);
73
74 System.out.println("\n--- JVM Memory Info ---");
75 System.out.printf("Max Heap : %d MB%n", maxMemory);
76 System.out.printf("Total Heap : %d MB%n", totalMemory);
77 System.out.printf("Free Heap : %d MB%n", freeMemory);
78 System.out.printf("Used Heap : %d MB%n", totalMemory - freeMemory);
79
80 System.out.println("\n======================================");
81 System.out.println(" Setup verified successfully!");
82 System.out.println("======================================");
83 }
84}This program demonstrates: (1) static helper methods as the Java way to decompose logic, (2) System.getProperty() to read JVM system properties, (3) printf-style formatting with %s and %n (platform-neutral newline), (4) the Runtime class for JVM introspection, (5) command-line argument handling via String[] args, and (6) integer arithmetic for MB conversion. Compile it with javac EnvironmentInfo.java, then run java EnvironmentInfo. You should see your JDK version confirm as 17+ and the correct OS details. This single file captures the full compile-execute cycle.
With your environment verified, the next lesson explores Java's type system in depth: primitive types (int, long, double, boolean, char), their wrapper class counterparts (Integer, Long, Double), widening and narrowing conversions, autoboxing, and variable scoping rules. Understanding types precisely is the foundation for writing bug-free Java code.
Your progress is saved in this browser, on this device only.