The Two Phases of Java
Java programs go through two distinct phases: compilation (when source code becomes bytecode) and runtime (when bytecode executes on the JVM). Understanding what happens in each phase is fundamental to Java development.
- Compile Time - Source code (.java) → Bytecode (.class)
- Runtime - JVM executes the bytecode
Compile Time
Compile time is when the Java compiler (javac) processes your source code. The compiler performs syntax checking, type checking, and generates bytecode.
What Happens at Compile Time
// 1. Syntax checking
int x = 5 // Error: missing semicolon
// 2. Type checking
String name = 42; // Error: incompatible types
// 3. Symbol resolution
System.out.printn("Hi"); // Error: cannot find symbol 'printn'
// 4. Access checking
private int secret = 10;
obj.secret = 20; // Error: secret has private access
// 5. Constant folding (optimization)
int result = 2 + 3; // Compiled as: int result = 5;
Compile-Time Constants
public class Constants {
// Compile-time constant (known at compilation)
static final int MAX_SIZE = 100;
// NOT a compile-time constant (computed at runtime)
static final int RANDOM = new Random().nextInt();
// Compile-time constant expression
static final int DOUBLE_MAX = MAX_SIZE * 2; // Evaluated at compile time
}
// Usage - MAX_SIZE is inlined at compile time
int limit = Constants.MAX_SIZE; // Compiled as: int limit = 100;
Compile-Time Errors
// These errors prevent compilation:
// Syntax error
public class { } // Missing class name
// Type mismatch
List<String> names = new ArrayList<Integer>(); // Error!
// Unchecked exception not caught
public void read() {
new FileReader("file.txt"); // Error: unhandled IOException
}
// Missing return statement
public int getValue() {
int x = 5;
// Error: missing return statement
}
Runtime
Runtime is when the JVM loads and executes the compiled bytecode. Many operations that can't be checked at compile time happen here.
What Happens at Runtime
// 1. Object creation
Object obj = new MyClass(); // Memory allocated at runtime
// 2. Method dispatch (polymorphism)
Animal animal = getAnimal(); // Could be Dog, Cat, etc.
animal.speak(); // Actual method determined at runtime
// 3. Array bounds checking
int[] arr = new int[5];
arr[10] = 1; // ArrayIndexOutOfBoundsException at runtime
// 4. Null checks
String s = null;
s.length(); // NullPointerException at runtime
// 5. Class loading
Class<?> clazz = Class.forName("com.example.MyClass"); // Loaded at runtime
Runtime Exceptions
// These errors occur during execution:
// NullPointerException
String name = null;
name.toUpperCase(); // Crashes at runtime
// ClassCastException
Object obj = "Hello";
Integer num = (Integer) obj; // Crashes at runtime
// ArithmeticException
int result = 10 / 0; // Crashes at runtime
// OutOfMemoryError
List<byte[]> list = new ArrayList<>();
while (true) {
list.add(new byte[1_000_000]); // Eventually crashes
}
Type Erasure: Compile Time vs Runtime
Generics in Java are a compile-time feature. Due to type erasure, generic type information is removed at runtime.
// At compile time - type checking happens
List<String> strings = new ArrayList<>();
strings.add("hello");
strings.add(42); // Compile error!
// At runtime - type is erased
List<String> strings = new ArrayList<>();
List<Integer> ints = new ArrayList<>();
// At runtime, both are just "List" (raw type)
System.out.println(strings.getClass() == ints.getClass()); // true!
// This is why you can't do:
if (list instanceof List<String>) { } // Compile error!
if (list instanceof List<?>) { } // OK - unbounded wildcard
Generic type parameters are not available at runtime. You cannot create generic arrays, check instanceof with generic types, or get generic type info via reflection (without extra tricks).
Annotations: Compile vs Runtime
Annotations can be retained for different phases based on their retention policy.
// SOURCE - discarded after compilation
@Retention(RetentionPolicy.SOURCE)
public @interface Todo { } // Not in .class file
// CLASS - in .class file, not available at runtime (default)
@Retention(RetentionPolicy.CLASS)
public @interface InBytecode { }
// RUNTIME - available via reflection at runtime
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation { }
// Common examples:
@Override // SOURCE - compiler checks, then discarded
@Deprecated // RUNTIME - available for reflection
@Entity // RUNTIME - JPA reads at runtime
Static vs Dynamic Binding
Static Binding (Compile Time)
public class Example {
// Method overloading - resolved at compile time
public void print(String s) {
System.out.println("String: " + s);
}
public void print(Integer i) {
System.out.println("Integer: " + i);
}
// Static methods - bound at compile time
public static void staticMethod() { }
// Private methods - bound at compile time
private void privateMethod() { }
// Final methods - bound at compile time
public final void finalMethod() { }
}
Dynamic Binding (Runtime)
class Animal {
public void speak() {
System.out.println("Animal speaks");
}
}
class Dog extends Animal {
@Override
public void speak() {
System.out.println("Dog barks");
}
}
// Dynamic binding - actual method determined at runtime
Animal animal = new Dog();
animal.speak(); // Prints "Dog barks" (determined at runtime)
Reflection: Runtime Introspection
// Reflection provides runtime access to class information
Class<?> clazz = MyClass.class;
// Get methods at runtime
Method[] methods = clazz.getDeclaredMethods();
// Create instance at runtime
Object instance = clazz.getDeclaredConstructor().newInstance();
// Invoke method at runtime
Method method = clazz.getMethod("myMethod");
method.invoke(instance);
// Check annotations at runtime
if (clazz.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation ann = clazz.getAnnotation(MyAnnotation.class);
}
Practical Comparison
| Aspect | Compile Time | Runtime |
|---|---|---|
| Type Checking | Generics, method signatures | instanceof, casting |
| Method Resolution | Overloading, static methods | Overriding (polymorphism) |
| Errors | Syntax, type mismatches | Null pointers, out of bounds |
| Optimization | Constant folding, dead code | JIT compilation, inlining |
| Information | Full source code | Bytecode, limited generics |