What exactly does Class.forName do regarding class loading and initialization, and how can you control it?
answer
- phases: load → link (verify/prepare/resolve) → initialize
- 1-arg forName = load + INITIALIZE + caller's loader + checked exception
- 3-arg forName(name, initialize, loader) controls both
- loadClass = load only, no init
- JDBC driver static block was the reason forName initialized
basics
~20 sClass.forName("name") finds a class by its String name, loads it, and runs its static initializers (initialization), using the caller's class loader. The 3-argument version lets you turn initialization off and pick which class loader to use.
solid answer
~40 sClass loading has phases: loading (reading the bytecode), linking (verify/prepare/resolve), and initialization (running static initializers and static field assignments). The single-argument Class.forName(name) performs loading and forces initialization, using the class loader that loaded the calling class, and throws checked ClassNotFoundException. The three-argument overload, forName(name, initialize, loader), separates these concerns: passing initialize=false loads and links the class but does NOT run its static initializers, and you supply the exact class loader. By contrast, classLoader.loadClass(name) only loads (no initialization). This matters when a static block has side effects you do not want to trigger merely from looking the class up, when you must load through a specific loader (plugin isolation, web apps, OSGi), or when you want lazy initialization. The classic forName side effect was JDBC driver registration via the driver's static block.
code
java · 13 linesclass WithSideEffect {
static { System.out.println("initialized!"); }
}
// Forces initialization -> prints "initialized!"
Class.forName("com.example.WithSideEffect");
// Loads WITHOUT initializing -> prints nothing yet
ClassLoader ld = Thread.currentThread().getContextClassLoader();
Class<?> c = Class.forName("com.example.WithSideEffect", false, ld);
// Static block runs only now, on first active use:
c.getDeclaredConstructor().newInstance(); // prints "initialized!"go deeper
Know that forName loads a class by name and can run its static code, and that it throws ClassNotFoundException.
Explain the load-vs-initialize distinction and that 1-arg forName initializes while loadClass does not.
Walk through the load/link/initialize phases, the 3-arg overload's initialize and loader parameters, and concrete uses (JDBC, plugins, avoiding side effects).
Reason about class-loader topology and isolation, context-class-loader pitfalls in containers/OSGi, eager vs lazy initialization for failure surfacing, and the security risk of initializing attacker-named classes.
## Background: the lifecycle of a class in the JVM Before a class can be used, the JVM moves it through phases (JLS/JVMS): 1. **Loading** — a *class loader* finds the `.class` bytecode and creates the `Class` object. 2. **Linking** — *verification* (bytecode is well-formed and safe), *preparation* (allocate static fields with default values), *resolution* (resolve symbolic references; may be lazy). 3. **Initialization** — run the class's **static initializers** and **static field initializers**, in textual order. This is the first time *your* static code actually executes. The JVM does this lazily, on first *active use* (first instance creation, first static method call, first non-constant static field access, etc.). A **class loader** is the component responsible for phase 1; different loaders create *distinct* `Class` objects even for the same bytes, which is how isolation (web apps, plugins, OSGi bundles) works. ## What `Class.forName(String name)` does The 1-argument form is defined as: > `Class.forName(name)` ≡ `Class.forName(name, true, callerClassLoader)` So it: - **Loads** the class with the **caller's class loader** (the loader that loaded the class containing the call). - **Forces initialization** (`initialize = true`) — it runs the static initializers right now, even though normal active-use rules might defer them. - Throws the **checked** `ClassNotFoundException` if the name cannot be resolved (and can propagate `ExceptionInInitializerError` if a static initializer throws, or `LinkageError` on link failures). The name is the **binary name** (`com.example.Outer$Inner`, array encoding `[Ljava...;`). ## The 3-argument overload: `forName(name, initialize, loader)` This gives you two extra controls: - **`initialize` (boolean):** when `false`, the class is loaded and linked but its **static initializers do NOT run** until the class is later actively used. Use this when you only need the `Class` object for inspection and must not trigger static side effects. - **`loader` (ClassLoader):** the exact loader to use. Critical for plugin systems, app servers, and any place where the same class name may resolve differently per loader, or where the caller's loader cannot see the target. ## Contrast with `ClassLoader.loadClass` ```java Class<?> c = someLoader.loadClass("com.example.Foo"); // load only, NO initialization ``` `loadClass` performs loading (and linking as needed) but, like `forName(name, false, loader)`, does **not** initialize. So: | Call | Loads? | Initializes? | Loader | |---|---|---|---| | `Class.forName(name)` | yes | **yes** | caller's | | `Class.forName(name, false, ld)` | yes | **no** | `ld` | | `Class.forName(name, true, ld)` | yes | **yes** | `ld` | | `ld.loadClass(name)` | yes | **no** | `ld` | ## Why this matters in practice - **JDBC drivers (the canonical example):** old code wrote `Class.forName("com.mysql.jdbc.Driver")` precisely *because* forName initializes — the driver's `static { DriverManager.registerDriver(...); }` block ran as a side effect. Since JDBC 4.0, the `ServiceLoader` mechanism auto-registers drivers, so this call is obsolete. - **Avoiding unwanted side effects:** if a class's static block does heavy or stateful work, use `initialize=false` to inspect it reflectively without triggering that work. - **Class-loader isolation:** in web/app servers, plugins, and OSGi, you frequently must pass the **context class loader** (`Thread.currentThread().getContextClassLoader()`) so the right copy of the class is found. - **Lazy vs eager:** forcing initialization can surface `ExceptionInInitializerError` early; deferring it can hide it until first use. ## Security note Resolving and **initializing** a class from an attacker-controlled string is dangerous — initialization runs arbitrary static code, and reflective loading can reach unexpected types. Never pass untrusted input to `forName` without strict allow-listing.
- When does a static initializer block actually run?At the initialization phase, triggered by first active use (instance creation, static method call, non-constant static field access) or explicitly by Class.forName with initialize=true. Plain loading and forName(name,false,loader) do not run it.
- Why might you pass Thread.currentThread().getContextClassLoader() to forName?In app servers, plugins, and frameworks the caller's own loader may not see the target class; the thread context class loader points at the application/module loader that can, restoring correct visibility across class-loader boundaries.
saying these in an interview costs you the question
- Saying forName never initializes by default
- Confusing loading with initialization (static blocks run at initialization, not loading)
- Forgetting forName uses the caller's class loader by default
- Passing untrusted strings to forName without allow-listing