Appearance
How Java Works: JVM, JRE, JDK, and Writing Your First Program
What Java Actually Is
Before you write a single line of code, you need to understand what Java is at its core. Java is a platform independent, object oriented programming language. You have already seen what object oriented means: inheritance, polymorphism, encapsulation, and abstraction. But the platform independent part is the big deal, and understanding it will change how you think about every Java program you ever write.
Most languages compile your code into machine code that runs on one specific operating system and one specific chip architecture. Write a C program on Windows and compile it: you get a Windows executable. Take that same binary to a Mac and it will not run. You need to recompile it for Mac, and possibly change the code too. Every target platform is a new job.
Java solved this problem in a fundamentally different way, and that solution is the story of how Java runs.
The Journey from Source Code to Running Program
Imagine you write a Java file called Student.java. What happens between you saving that file and a program actually running on your machine? There are two completely separate stages, and most beginners blur them together.
Stage One: Compilation
You run a command called javac (Java Compiler):
javac Student.javaThe Java compiler reads your .java source file and produces a new file: Student.class. This .class file does not contain machine code. It contains something called bytecode.
Bytecode is not instructions for your Intel chip, or your Apple M1, or your ARM processor. Bytecode is instructions for a machine that does not physically exist: the Java Virtual Machine (JVM). The JVM is an imaginary, universal computer. Because it is imaginary and you get to define what its instruction set looks like, you can make the same bytecode mean the same thing everywhere.
So after stage one, you have a Student.class file sitting on disk. This file is platform independent. You can copy it to any computer on the planet and it carries its meaning with it.
Stage Two: Execution
Now you run:
java StudentThis launches the JVM on your specific machine. The JVM reads Student.class and begins executing it. Here is the key: every operating system has its own JVM. Windows has a Windows JVM. macOS has a macOS JVM. Linux has a Linux JVM. Each of those JVMs is platform dependent, meaning it knows how to talk to its specific operating system and chip.
But all of them read the same bytecode. The same Student.class file that compiled on your Windows laptop runs on your friend's Mac without any changes. Write once, run anywhere. This is not a marketing slogan. It is a precise technical description of a two stage translation process.
The interview question you will definitely face: "Why is Java platform independent?"
The answer is: Java compiles not to machine code but to bytecode. Bytecode is instructions for the JVM, which is a virtual machine. Every real operating system has its own JVM installed. Every JVM on every OS reads the exact same bytecode. So you compile once, and the bytecode runs on any machine that has a JVM. Only the JVM itself is platform dependent, not your Java program.
What the JVM Actually Does Inside
When you run java Student, the JVM does not just blindly execute your bytecode. Several things happen in order.
First, the Class Loader finds your Student.class file and loads it into memory. The JVM works with classes, not source files. It never sees your .java file. It only ever sees the .class bytecode.
Second, the Bytecode Verifier runs a security pass over the loaded bytecode. It checks for things like illegal memory accesses, invalid type casts, and stack manipulations that could crash the runtime. This is why Java has a real security model at the language level. Bytecode could theoretically come from anywhere, including untrusted sources, so this verification step is a guard.
Third, the Interpreter begins executing the bytecode instructions one at a time. For many programs, especially short ones, this is fine. But interpretation is slower than running native code directly.
Fourth, the JIT Compiler, which stands for Just In Time Compiler, enters the picture. The JVM watches which methods get called frequently. A method that gets called thousands of times in a loop is called a "hot" method. Once a method crosses a threshold for call frequency, the JIT compiles it from bytecode to native machine code for your specific CPU. Every call to that method after this point runs the native machine code directly, not the interpreted bytecode. This is why Java performance improves the longer a server application runs: the JIT progressively compiles the hot paths, and the program gets faster as it warms up.
This also explains why the claim that Java is always slow is not accurate for real applications. A Java web server that has been running for an hour has had all its hot code JIT compiled and is running native speed for those paths.
Fifth, the Garbage Collector runs in the background, tracking which objects in memory are still reachable and freeing memory from objects that are not. You do not manually free memory in Java.
A Trap Every Beginner Falls Into
You edit Student.java and save it. Then you run java Student. The output looks wrong. You make more changes, run again. Still wrong.
The trap: you forgot to recompile. The JVM reads only .class files. It never touches your .java source file. If you edit the source and run java without first running javac, you are executing the old bytecode from the last time you compiled.
Always run both steps:
javac Student.java
java StudentOr chain them in one terminal command so you cannot forget:
javac Student.java && java StudentThis is not an edge case. It is one of the most common sources of confusion for people who are new to Java.
JVM, JRE, and JDK: The Russian Dolls
These three acronyms appear on every Java interview, every job description, and every installation page. Most beginners treat them as vague synonyms. They are not. They are nested containers, like Russian dolls, where each outer doll contains everything inside it.
Think of it this way. You have the smallest doll first. Then a larger one that contains the small one. Then the largest one that contains both.
The JVM (Java Virtual Machine)
The JVM is the innermost doll. It is the execution engine. Its job is to take bytecode and run it. Inside the JVM you have the Class Loader, the Bytecode Verifier, the Interpreter, the JIT Compiler, and the Garbage Collector.
The JVM alone is not a standalone download. You do not install "just the JVM." It lives inside the JRE.
The JVM is platform dependent. A Windows JVM is different from a Linux JVM because each one knows how to talk to its own operating system. Your bytecode is the same; the JVM adapts it to where it runs.
The JRE (Java Runtime Environment)
The JRE is the middle doll. It contains the JVM plus a large set of class libraries. These are the standard Java APIs: java.lang, java.util, java.io, and many others. When your bytecode calls Math.abs() or Arrays.sort(), those implementations come from the class libraries in the JRE. The JVM needs these libraries at runtime to complete execution.
The JRE is what you install if you only want to run Java programs but not write them. A computer that only needs to run a Java application needs the JRE. It does not need a compiler.
If you have only the JRE: you can run any compiled Java program. You cannot write new Java code or compile it.
The JDK (Java Development Kit)
The JDK is the outermost doll. It contains the entire JRE, plus the development tools you need to write Java programs. The most important tool in the JDK is javac, the Java compiler. It also includes jar for packaging compiled programs, javadoc for generating documentation from your code comments, jshell which is an interactive REPL for experimenting with Java code, and jdb the debugger.
If you have the JDK: you can both write Java code and run it. As a developer, the JDK is what you install.
Here is how to remember the distinction: a user who just runs your application needs the JRE. You, the developer who writes and compiles the application, need the JDK.
The interview question: "What is the difference between JVM, JRE, and JDK?"
JVM executes bytecode. JRE is JVM plus the standard class libraries needed at runtime. JDK is JRE plus the compiler and development tools. Every outer container includes everything inside it.
Java Editions: JSE, JEE, and JME
When you download Java, you might see references to JSE, JEE, and JME. These are not different languages. They are different sets of APIs built on top of core Java.
JSE stands for Java Standard Edition. This is the core language: all the fundamentals, the standard library, everything you are learning in this course. When people say "Java," they usually mean JSE.
JEE stands for Java Enterprise Edition, now called Jakarta EE. This includes JSE plus additional APIs for large scale enterprise applications: servlets, transactions, JPA for database access, and APIs used in big ecommerce systems and web applications built for companies.
JME stands for Java Micro Edition. This is a trimmed down set of APIs designed for resource constrained devices like mobile phones and embedded systems. It has less memory and processing power available, so the API set is smaller.
You are learning JSE. Everything else is built on top of it.
Your First Java Program
Now you understand the machinery. Time to write actual code. The canonical first program:
java
// File must be saved as: Employee.java
public class Employee {
// The JVM looks for this exact method signature to start the program
public static void main(String[] args) {
int a = 10; // declare an integer variable named a, assign it the value 10
System.out.println("output of a is " + a); // print the value to the console
}
}Save this file as Employee.java. The filename must match the public class name exactly, including capitalization. This is not a convention or a style preference. The JVM enforces it. If your class is named Employee and your file is named employee.java or Employee2.java, the compiler will refuse with an error.
Now compile and run:
javac Employee.java
java EmployeeYou will see printed to the console:
output of a is 10The first line of output is the result of the System.out.println call. System is a class in the Java standard library. out is a static field on System that represents the standard output stream connected to your terminal. println is a method on that stream that prints the text you give it and then moves to a new line.
The + operator between "output of a is " and a concatenates the string with the value of a. Java automatically converts the integer a to its string representation for concatenation.
Comments in Java
Java has two comment styles. Both appear in real code constantly.
Single line comments start with // and run to the end of the line. The compiler ignores everything after // on that line.
java
// This entire line is a comment
int x = 5; // This part is code; this part after the slashes is a commentMulti line comments start with /* and end with */. Everything between those markers is ignored by the compiler, and they can span as many lines as you need.
java
/* This is a multi line comment.
You can write as many lines as you want here.
The compiler skips all of it. */Comments do not affect compilation or execution at all. They exist purely for humans reading the code.
Dissecting Every Word of public static void main(String[] args)
This method signature is the entry point of every Java program. The JVM specifically looks for this exact signature to know where to start executing your code. Every word in it is required, and every word means something specific.
java
public static void main(String[] args)public
public is an access modifier. It controls who can call this method. When public is applied to a method, it means anyone can call it: code in the same class, code in the same package, code in different packages, and even code outside your application entirely.
The JVM calls main from outside your class. It is external to your code. Without public, the JVM cannot reach the method and your program will not start. Access control is enforced even for the JVM itself.
static
static is one of the most important keywords you will encounter in Java. Understanding it here will save you confusion for months.
Normally, to call a method on a class, you first need to create an object (an instance) of that class. A nonstatic method belongs to an object. You cannot call it without first constructing an object.
Here is the problem: when you run java Employee, no Employee object has been created yet. The program has not even started. The JVM needs an entry point it can call before any object exists. If main were a regular instance method, the JVM would be stuck: it cannot create an Employee to call main, because creating an Employee requires code to already be running, and that code cannot run because there is no Employee yet. A circular problem.
static breaks this circle. A static method belongs to the class itself, not to any instance. The JVM can call Employee.main(args) directly on the class without constructing any object at all. The program starts, and from that point you can create objects as you need them.
This is the interview question you will be asked almost certainly: "Why is main static?" Your answer: because when the JVM starts a program, no objects exist yet. The JVM needs to call the entry point before any object is created. Static methods belong to the class, not to instances, so the JVM can call them without needing an object first.
void
void is the return type. It means this method does not return any value to its caller. When main finishes executing and returns, the JVM terminates the process. There is no meaningful value to return to the JVM from main.
If you need to signal a success or error code to the operating system when your Java program exits, you use System.exit(0) for success or System.exit(1) for an error condition. That is not the same as a Java return value; it is an operating system level exit code.
main
main is not a keyword in Java. It is just a name, specifically the name the JVM specification requires for the program entry point. The JVM searches for a method with the exact name main and the exact signature public static void main(String[] args) to begin execution.
If you name your method Main or mian or anything else, the program will compile without error but will not run. The JVM will print: "Main method not found in class Employee, please define the main method as: public static void main(String[] args)."
You can actually define other methods also named main with different parameter lists. This is called overloading. Those overloaded versions compile fine. The JVM ignores them as entry points: it only starts from the version that takes String[] args.
String[] args
String[] args is the parameter of the main method. It receives command line arguments.
When you run a Java program, you can pass additional values on the command line after the class name:
java Employee 100000 developerInside main, args[0] would be "100000" and args[1] would be "developer". These values are always strings. Even if you pass a number like 100000, Java receives it as the string "100000". If you need to use it as an integer, you convert it: Integer.parseInt(args[0]).
If you run java Employee with no arguments, the args array is not null. It is an empty array with length zero. You can always safely check args.length to see how many arguments were passed. Checking args == null is unnecessary and misleading because it will never be null.
The name args is conventional but not required. You could write String[] arguments or String[] cmdArgs and the program would work identically. The type String[] is what the JVM requires, not the name.
What a Class Can Contain
A Java class is a container that holds four kinds of things. Each of these is a topic that goes deep, and you will explore each one in detail in future articles. For now, you need to know they all exist inside a class.
Variables hold data. A class can have static variables that belong to the class itself, and instance variables that belong to individual objects created from the class.
Methods define behavior. They are functions inside the class. main is a method.
Constructors are special methods that run when you create a new object. They set up the initial state of the object.
Nested classes are classes defined inside other classes. Java allows this for organizing tightly related code.
Understanding that a class is a structured container for all of these things is the right mental model. A class is not just a collection of code: it is a description of a type of object, with data (variables), behavior (methods), and a creation process (constructors).
The Full Pipeline in One Picture
Here is everything together:
Employee.java (your source code, human readable text)
|
| javac Employee.java
v
Employee.class (bytecode, platform independent binary)
|
| java Employee (launches JVM)
v
JVM reads Employee.class
|
| Class Loader loads the bytecode into memory
| Bytecode Verifier checks safety
| Interpreter starts executing
| JIT Compiler optimizes hot methods to native code
| Garbage Collector manages memory
v
Output appears on your terminalThe JVM contains all of this execution machinery. The JRE wraps the JVM and adds the standard class libraries your code needs at runtime. The JDK wraps the JRE and adds javac and the other tools you need to write and compile code in the first place.
As a developer, you install the JDK. You write .java files. You compile with javac to produce .class bytecode. You execute with java and the JVM runs the bytecode. That is the complete picture.
Interview Questions to Lock In
Every concept in this article shows up in Java interviews at every level. Here are the exact questions and the answers that show you understand the material:
"What is bytecode?" Bytecode is the intermediate binary format that javac compiles Java source code into. It is not machine code for any real CPU. It is instructions for the JVM, which is a virtual machine. Any JVM on any platform can read the same bytecode.
"Why is Java platform independent?" Because Java compiles to bytecode, not to machine code. Bytecode runs on the JVM, and every major operating system has its own JVM. The same bytecode file runs on Windows, macOS, and Linux without recompilation. Only the JVM itself is platform dependent; your Java code is not.
"Is the JVM platform independent or platform dependent?" The JVM is platform dependent. There is a separate JVM for Windows, macOS, Linux, and other systems. Each JVM is tuned for its own OS and CPU. But the bytecode that the JVM reads is platform independent.
"What is the difference between JVM, JRE, and JDK?" JVM is the virtual machine that executes bytecode. JRE is the JVM plus the Java class libraries needed at runtime. JDK is the JRE plus development tools like the compiler. JVM is inside JRE. JRE is inside JDK.
"Why is main static?" Because when the JVM starts your program, no objects have been created. The JVM needs to call an entry point before any instantiation happens. Static methods belong to the class itself, not to instances, so the JVM can invoke main without first constructing an object.
"What happens if you edit a .java file and run java without recompiling?" The JVM executes the old bytecode from the previously compiled .class file. It never reads your source code. Your changes have no effect until you run javac again.
"What is JIT compilation?" Just In Time compilation is when the JVM detects that a method is being called very frequently (a "hot" method) and compiles it from bytecode to native machine code for the current CPU. After JIT compilation, every call to that method runs native code directly instead of interpreted bytecode. This is why Java performance improves over time in long running applications.
"What is the difference between JSE, JEE, and JME?" JSE is core Java, the standard edition with the fundamental language and standard library. JEE (Jakarta EE) is JSE plus enterprise APIs for large scale web and business applications. JME is a lightweight edition for mobile and embedded devices. All three are built on core Java.