No history yet

Java Ecosystem Fundamentals

The Virtual Machine Approach

Coming from Rust, you're used to compiling code directly into a native binary. The compiler, rustc, takes your .rs files and produces an executable that the operating system can run directly. Python, on the other hand, typically uses an interpreter that executes your .py files on the fly, often creating .pyc bytecode files behind the scenes.

Java takes a middle path. When you compile Java code, you don't create a native executable. Instead, the Java compiler (javac) transforms your .java files into a platform-independent format called and saves it in .class files. This bytecode is a set of instructions designed not for your CPU, but for a piece of software: the Java Virtual Machine (JVM).

The JVM acts as a translation layer. It takes the universal bytecode and translates it into the specific machine code that your computer's processor understands. This is the foundation of Java's "write once, run anywhere" philosophy. You can compile your code on a Mac and run the exact same .class files on a Windows or Linux machine, as long as it has a JVM installed.

The JVM is clever. For code that runs only a few times, it uses an interpreter to execute the bytecode directly. But for code that runs frequently—so-called "hot spots"—the JVM kicks in its Just-In-Time (JIT) compiler to compile that bytecode into highly optimized native machine code. This allows Java applications to start up quickly (thanks to the interpreter) and achieve performance competitive with natively compiled languages like Rust over time (thanks to the JIT).

Your Development Kit

To write and run Java code, you need a Java Development Kit (JDK). The JDK includes the compiler (javac), the JVM, and a standard library of pre-built code. It's the whole toolbox. This is different from the Java Runtime Environment (JRE), which only contains the JVM and is meant for users who just want to run Java applications, not develop them.

When choosing a JDK, you'll encounter two main flavors: Oracle JDK and OpenJDK. For years, Oracle's was the official implementation, but it now has a more restrictive license for commercial use. OpenJDK is the open-source reference implementation, and it's what most developers use today. Distributions like Eclipse Temurin or Amazon Corretto provide free, production-ready builds of OpenJDK. For this course, any JDK version 17 or higher will work perfectly.

Java's Memory Model

Java manages memory differently than both Rust and Python. Instead of Rust's compile-time ownership and borrowing rules, or Python's reference counting, Java uses an automatic memory management system called a Garbage Collector (GC). To understand how it works, we first need to look at where Java stores data: the Stack and the Heap.

Memory AreaStoresLifetime
StackPrimitive types (int, boolean, etc.) and references to objects.Scoped to the method call. Cleared when method returns.
HeapAll objects (new String(), new User(), etc.).Lives until it is no longer referenced and collected by the GC.

Every time you call a method, a new frame is pushed onto the stack. This frame holds the method's local primitive variables and references to any objects it uses. The objects themselves live on the heap. When the method finishes, its stack frame is popped, and the local variables and references disappear. The objects on the heap, however, remain.

A key takeaway: In Java, object references live on the stack, but the objects themselves always live on the heap.

So what happens to the objects on the heap when no more references point to them? They become eligible for garbage collection (GC). The GC is a process that runs in the background, periodically scanning the heap for objects that are no longer reachable from any part of the application. It then deallocates the memory used by these objects, making it available for new ones.

For a Rust developer, this means you don't need to worry about lifetimes or the borrow checker. The JVM handles freeing memory for you. For a Python developer, the concept is similar to reference counting, but more sophisticated. Instead of deallocating an object the instant its reference count hits zero, Java's GC typically runs in cycles, which helps it handle complex object graphs and circular references that can trip up simple reference counting systems.

Let's test your understanding of these core concepts.

Quiz Questions 1/5

What is the primary output of the Java compiler (javac)?

Quiz Questions 2/5

Which component is necessary for developing Java applications but not strictly required for just running them?

Understanding the JVM, the compilation process, and the memory model is foundational to writing effective Java code. It explains why Java can be both portable and performant, and it clarifies the trade-offs it makes in memory management compared to languages like Rust and Python.