A Java app runs when source code is compiled into bytecode, loaded by the JVM, and started through the main method.
Java can feel split into two worlds at first. You write plain text in a .java file. Then that file turns into something the Java Virtual Machine, or JVM, can run. Once that clicks, the whole flow starts to make sense.
A Java program works in a chain. You write a class, compile it, start it with the Java launcher, and the JVM takes over. It loads the class, checks the bytecode, finds main, creates memory for the work ahead, and runs each instruction in order. That flow is the reason Java code can move across many systems with little change.
How Does Java Program Work? From Source To JVM
The life of a Java program starts with source code. That source code holds classes, methods, variables, and statements written with Java syntax. A file named Hello.java will usually contain a class named Hello. That name match matters because the compiler and launcher depend on it.
After you save the file, the compiler reads it and turns it into bytecode. Bytecode is not raw machine code for Windows, macOS, or Linux. It is an intermediate form made for the JVM. Oracle’s compiler docs say the compiler reads Java source files and compiles them into class files that run on the Java Virtual Machine.
When you run java Hello, the launcher starts a JVM process. The JVM loads the class, checks that the bytecode follows Java rules, links the class, and starts execution. The Java Language Specification states that the JVM starts a program by invoking the main method of a specified class.
It Starts With A Class And A Main Method
Most beginner programs start with one class and one entry point:
public class Hello {
public static void main(String[] args) {
System.out.println("Hello");
}
}
That small file already shows the full path of execution. The class gives the compiler a structure. The main method gives the JVM a place to begin. The print statement calls library code, which writes text to standard output.
What The Compiler Produces
When javac Hello.java runs, it creates Hello.class. That class file stores bytecode plus metadata about fields, methods, constants, and the class name. The JVM spec lays out the rules for that format and for how the JVM reads it.
That step is easy to miss when you are new. You do not run the .java file straight in the usual flow. You run the compiled class through the launcher. Newer Java releases add single-file source launch features, yet the runtime still turns source into something the JVM can execute before the program begins.
What Happens Each Time You Run A Java App
Once the JVM starts, several moving parts begin working together. Some run only once at startup. Others keep working until the process ends.
- The launcher locates the class you asked to run.
- The class loader pulls that class into memory.
- The verifier checks the bytecode for illegal structure or unsafe stack use.
- The JVM links the class and gets static data ready.
- The
mainmethod starts on a new thread. - Objects are created on the heap as code requests them.
- Methods push stack frames as calls nest and return.
- Garbage collection frees memory from objects no longer reachable.
That may sound like a lot, yet it happens in a blink for small apps. You type one command. The runtime handles the rest. Oracle’s javac command page and Chapter 12 on execution map that handoff from compile step to JVM startup.
| Stage | What Happens | What You Notice |
|---|---|---|
| Write Source | You save Java code in a .java file with classes and methods. |
The file is readable text. |
| Compile | javac checks syntax and creates one or more .class files. |
Compile errors appear here if the code is broken. |
| Launch | The java command starts a JVM process for the chosen class. |
The terminal waits for program output. |
| Load Class | The class loader finds bytecode for the main class and any classes it needs. | Missing classes throw runtime errors. |
| Verify | The JVM checks bytecode rules before running it. | Broken or tampered class files are rejected. |
| Initialize | Static fields and static blocks run before normal method calls. | Startup work may happen before the first print line. |
| Run Main | The JVM invokes main(String[] args) and follows each statement. |
Your app starts doing visible work. |
| Manage Memory | Objects live on the heap, and garbage collection reclaims unused ones. | Memory use rises and falls during the run. |
Why Bytecode Matters
Bytecode is the bridge between your source code and each machine that runs the JVM. That bridge is why one compiled program can run on many systems without a fresh rewrite. You still need a fitting Java runtime on the target machine, yet the class file itself stays the same in many cases.
The JVM can interpret bytecode and can also compile hot sections into native machine code during execution. That means Java is not stuck in one rigid model. It starts with a portable format, then the runtime can speed up code paths that get hit again and again. The Java Virtual Machine Specification sets out the runtime rules behind that model.
Parts Of Java That Work Together During Execution
New learners often blend the names together. Here is the plain split:
- JDK: The kit used to write and compile Java code. It includes tools such as
javac. - JVM: The engine that loads classes and runs bytecode.
- Java standard library: Ready-made classes such as
String,System, andArrayList. - Class loader: The part that finds class definitions when code refers to them.
- Garbage collector: The part that reclaims heap memory from unreachable objects.
Stack, Heap, And Method Calls
Every thread gets its own call stack. Each method call adds a frame to that stack with local variables, argument values, and data needed for the return. When the method finishes, that frame disappears. That is why local variables do not live forever.
The heap is different. It holds objects and arrays created with new. If a method builds an object and returns it, the object can outlive the method that created it. That split between stack frames and heap objects is one of the clearest ways to read Java behavior.
| Common Stumble | Why It Happens | Plain Fix |
|---|---|---|
Could not find or load main class |
The launcher cannot match the class name or classpath. | Check the class name, package name, and working folder. |
Main method not found |
The entry method signature does not match what the launcher expects. | Use public static void main(String[] args). |
| Compile error before run | The source file has syntax or type problems. | Fix the code, then compile again. |
NoClassDefFoundError |
A needed class was present at compile time but missing at run time. | Check build output and classpath entries. |
NullPointerException |
Code tries to use an object reference that points to nothing. | Create the object first or guard against null. |
Why Public Static Void Main Looks So Odd
That line feels dense because it carries several jobs in a small space. public lets the launcher reach the method. static lets the JVM call it without building an object first. void says the method does not return a value. String[] args gives the program a place to receive command-line text.
Once you know those parts, the line stops looking magical. It is just a startup contract between your class and the Java launcher.
One Run Can Trigger Far More Than One File
A short Java program may still pull in many classes. Calling System.out.println touches classes behind the scenes. Creating a list brings in collection classes. Reading a file brings in I/O classes. Your main file is only the front door.
That is another reason Java feels larger than a tiny script language. The runtime is built around classes talking to other classes. You write one line, and the JVM may load several class files before that line finishes.
What Sticks After The First Read
If you want one mental model, use this: Java source becomes bytecode, the JVM loads that bytecode, and execution begins at main. From there, methods call methods, objects live on the heap, stack frames come and go, and garbage collection cleans up memory that is no longer reachable.
Once that model is firm, error messages get less mysterious. You can tell whether a problem came from the source file, the compile step, class loading, or runtime logic. That shift is where Java starts feeling clear instead of bulky.
References & Sources
- Oracle.“The javac Command.”Defines how the Java compiler reads source files and produces class files for the Java Virtual Machine.
- Oracle.“Chapter 12. Execution.”States that the JVM starts a program by invoking the main method of a specified class.
- Oracle.“The Java Virtual Machine Specification.”Lays out the class file format and the runtime rules used by the JVM.
