Class loaders and class loading

Your program doesn't load all its classes at start-up. The JVM pulls each class in the first time it is needed, checks it, gives its static fields default values, and only then runs its static initializer. You'll see every phase, learn the exact rules for when a static block runs, meet the three built-in class loaders and how they delegate, tell ClassNotFoundException from NoClassDefFoundError, and then practise with 8 problems.

Module 8 · JVM Memory⏱ 24 minIntermediate8 practice problems
1 The story

The static block that printed “too late”

Riya's team wrote a Config class whose static block prints “Config loaded” and reads settings. She expected the message as soon as the app started. Instead it appeared halfway through the logs, right before the first payment. And on the test server, a missing JAR made the app fail with NoClassDefFoundError, an error she had never seen in college.

Config.class Class loaders Bootstrap (java.base) Platform (java.sql …) Application (your code) Linking, then initialisation verifyprepareresolveinitialise statics = 0 / null static { … } on first use “Config loaded” prints only here, the first time Config is really used myphatshala.in
Riya

Sir, why did “Config loaded” print so late? The app had been running for a minute.

Arjun Sir

Because the JVM loads classes lazily. A class's static block runs on its first active use: creating an object, calling a static method, or reading a non-constant static field. Nobody touched Config until the first payment.

Riya

And the NoClassDefFoundError on the test server?

Arjun Sir

The class was there when you compiled, but missing (or broken) when the JVM tried to load it. Let's follow a class from the .class file to “ready”, and both mysteries will make sense.

2 Definition

What is class loading?

Formal definition (write this in exams)

Class loading is the process by which the JVM makes a class available at run time. It has three phases. Loading: a class loader finds the binary representation (the .class bytes) and creates the class in the method area. Linking: verification checks the bytecode is valid and safe, preparation allocates static fields and sets them to default values, and resolution replaces symbolic references with direct ones (this may happen lazily). Initialization: the class's static initializer (<clinit>: static field initializers and static blocks, in source order) runs exactly once, on the class's first active use, after its superclass has been initialized.

In simple words

Before a new employee starts work: HR finds their file (loading), checks the documents (verify), gives them an empty desk (prepare), adds their phone number to the directory (resolve), and on their first real day they set up their desk (initialise), only once.

Words you'll meet in this lesson

Class loaderAn object that finds and loads classes. Every class remembers which loader loaded it.
Parent delegationA loader first asks its parent to load a class, and only tries itself if the parent can't.
Static initializer<clinit>: the static field initializers and static { } blocks of a class.
Active useAn action that forces initialisation: new, static method call, non-constant static field access…
Compile-time constantA static final primitive or String with a constant value. javac copies it into the user's code.
CDSClass Data Sharing: a pre-parsed archive of JDK classes that speeds up start-up.

Class loading facts at a glance

PhasesLoad → Link (verify, prepare, resolve) → Initialise
Initialised whennew, a static method call, reading or writing a non-constant static field, Class.forName(name), a subclass being initialised, or being the main class
NOT initialised byReading a compile-time constant, creating an array of the type (new Config[3]), a class literal (Config.class), accessing a parent's static field through a child
Built-in loadersBootstrap (core JDK, shown as null) → Platform (Java 9+, other JDK modules; was “Extension” in Java 8) → Application/System (your class path)
ErrorsClassNotFoundException (checked, from Class.forName/loadClass); NoClassDefFoundError (class present at compile time, missing or failed at run time); ExceptionInInitializerError (static initializer threw)
Watch itjava -verbose:class Main prints every class as it is loaded

Exam tip: a class is loaded and initialised at different moments. new Config[3] may load Config but does not initialise it, so its static block doesn't run. Initialisation happens at most once per class (per class loader), and a superclass is always initialised before its subclass.

3 Types and ways to use it

Three class loaders, one delegation rule

Bootstrap

Built into the JVM (native code). Loads the core classes of java.base: String, Object, ArrayList. Java shows it as null.

getClassLoader() == null

Platform

Java 9+ (replaced the Java 8 “extension” loader). Loads other JDK modules such as java.sql and java.xml.

"platform"

Application

Also called the system class loader. Loads your classes and libraries from the class path (-cp) or module path.

"app"

Watching loading vs initialisation

public class Main {
    static class AppConfig {
        static final int MAX_USERS = 100;          // compile-time constant
        static String env = load();

        static {
            System.out.println("  AppConfig static block, env = " + env);
        }

        static String load() {
            System.out.println("  AppConfig.load() runs");
            return "prod";
        }
    }

    static {
        System.out.println("Main initialised");
    }

    public static void main(String[] args) {
        System.out.println("main starts");
        System.out.println("MAX_USERS = " + AppConfig.MAX_USERS);
        AppConfig[] slots = new AppConfig[3];
        System.out.println("array of " + slots.length + " created");
        Class<?> c = AppConfig.class;
        System.out.println("class literal: " + c.getSimpleName());
        System.out.println("env = " + AppConfig.env);
        System.out.println("env again = " + AppConfig.env);
    }
}
Main initialised main starts MAX_USERS = 100 array of 3 created class literal: AppConfig AppConfig.load() runs AppConfig static block, env = prod env = prod env again = prod

The same program with -verbose:class, keeping only our two classes (about 530 JDK classes, mostly from the CDS archive, and a few generated helper classes are left out; the path is shortened):

java -verbose:class Main
[0.034s][info][class,load] Main source: file:/home/riya/app/ Main initialised main starts MAX_USERS = 100 [0.035s][info][class,load] Main$AppConfig source: file:/home/riya/app/ array of 3 created class literal: AppConfig AppConfig.load() runs AppConfig static block, env = prod env = prod env again = prod

Notice: Main$AppConfig is loaded when the array is created, but its static block runs only when env is read.

ClassNotFoundException vs NoClassDefFoundError

ClassNotFoundExceptionNoClassDefFoundError
TypeChecked exceptionError (a LinkageError)
Thrown whenYou load a class by name (Class.forName, loadClass) and no loader finds itCode compiled against a class runs, but the class is missing at run time, or its initialisation failed earlier
Typical causeTypo in a class name, driver JAR not on the class pathMissing JAR at deploy time; a static initializer threw (“Could not initialize class X”)
Example messagejava.lang.ClassNotFoundException: com.shop.PaymentGatewayjava.lang.NoClassDefFoundError: Helper with Caused by: java.lang.ClassNotFoundException: Helper
Handle it?Yes, catch it and report or fall backFix the deployment or the initializer; don't catch and continue

Static initializers are fragile: if one throws, the JVM wraps the exception in ExceptionInInitializerError and marks the class as failed. Every later use throws NoClassDefFoundError: Could not initialize class … until the JVM restarts. Keep static blocks small and never let them depend on things that can fail, like a network call.

4 Step by step

From .class file to a ready class

  1. Loading: find the bytes, with delegation

    new TaxConfig();   // first time TaxConfig is needed
    The application loader first asks its parent (platform), which asks the bootstrap loader. Neither has TaxConfig, so the application loader reads TaxConfig.class from the class path and defines the class. Delegation stops anyone from replacing core classes such as java.lang.String with their own.
  2. Verify

    The verifier checks the bytecode: correct types on the operand stack, no jumps outside the method, valid references to fields and methods.

    This is why a hand-edited or corrupted .class file fails with a VerifyError instead of crashing the JVM. JDK classes from the CDS archive are already verified, which helps start-up.
  3. Prepare

    static String state = loadState();   // state = null for now
    static int gstPercent;               // 0
    Memory for static fields is allocated and set to defaults: 0, 0.0, false, null. Compile-time constants (static final int MAX = 100) already get their real value here. No Java code runs in this phase.
  4. Resolve

    Symbolic names in the constant pool (“method TaxConfig.loadState”) are turned into direct references.

    HotSpot resolves lazily: each reference is resolved the first time the instruction using it runs. That's also when a missing class referenced from your code surfaces as NoClassDefFoundError.
  5. Initialise: run <clinit> once

    static {
        gstPercent = state.equals("MH") ? 18 : 12;
    }
    The superclass is initialised first. Then the static field initializers and static blocks run in source order, in one compiler-generated method <clinit>. The JVM takes a lock while doing this, so even with many threads it runs exactly once.
5 See it happen

One class, every phase: both views

Left: the code and the console. Right: the class loaders and the class's progress through the phases.

The life of class AppConfig 6 steps
What you see AppConfig[] slots = new AppConfig[3]; // still nothing printed by AppConfig System.out.println(AppConfig.env); // if load() had thrown … [info][class,load] Main$AppConfig source: file:… array of 3 created // loaded, linked, not initialised AppConfig.load() runs AppConfig static block, env = prod env = prod // second read: nothing runs again java.lang.ExceptionInInitializerError // next use of AppConfig: java.lang.NoClassDefFoundError: Could not initialize class Main$AppConfig The array needs the class, so it is loaded. Linked: checked and statics set to defaults. No static block yet: not an active use. Reading env is an active use: initialise! Initialisation happens only once. A failed initializer breaks the class for good. myphatshala.in
Behind the screen Class loaders Bootstrapjava.base Platformjava.sql, java.xml … Applicationclass path ask parent ask parent not found above: app loads it class Main$AppConfig 1 Load 2 Verify 3 Prepare (statics = defaults) 4 Resolve (lazily, as used) 5 Initialise: <clinit> failed running done MAX_USERS = 100 (constant) env = null MAX_USERS = 100 env = "prod" env = null (never set) class marked erroneous Delegation: parents first, then the app loader. Bytecode checked; static fields get defaults. Prepared, but no Java code of the class has run. getstatic env → first active use → <clinit> runs. Initialised once; all code shares these statics. The JVM never retries a failed initialisation. myphatshala.in
6 Real-world use

Class loading in real systems

You rarely write a class loader yourself, but every framework you use depends on these rules.

Watch it with an example: a shop loading its UPI payment plug-in

The checkout loads payment providers by name from a plug-in JAR. Watch delegation find the class, linking prepare it, and the static block run only when the first payment arrives.

Loaded on first payment, initialised once 6 steps
Animated example java -cp shop.jar:upi.jar Shop Class.forName("pay.UpiProvider") // app loader reads upi.jar // link: verify, prepare // initialise: static block Class.forName("pay.UpiProvider") // 2nd Bootstrap Platform Application ask parent ask parent upi.jar pay.UpiProvider not loaded yet parents: not found loaded by: app verify ✓ prepare ✓ static int feePaise = 0 200 static block runs: prints "UPI ready" already initialised: nothing runs again CardProvider (no JAR): ClassNotFoundException myphatshala.in
app Aapp B

1. Application servers

A server such as Tomcat gives each deployed web app its own class loader, so two apps can use different versions of the same library. Undeploying an app should make its loader (and all its classes) unreachable; if something still references it, metaspace leaks.

WAR deployed→its own ClassLoader→Isolated classes
Class.forName("org.postgresql.Driver")

2. Database drivers and plug-ins

Old JDBC code loaded drivers with Class.forName("…Driver"), whose static block registered the driver. Modern drivers are found automatically through ServiceLoader, which also loads classes by name from JARs.

Driver JAR→ServiceLoader / forName→Registered driver
1 instance

3. Lazy, thread-safe singletons

The holder-class idiom puts a singleton in a static field of a nested class. Because a class is initialised once, under a JVM lock, on first use, you get lazy creation and thread safety without synchronized.

getInstance()→Holder.INSTANCE→Created once
start-up time

4. Faster start-up

Loading and verifying thousands of classes takes time, which matters for serverless functions and command-line tools. CDS archives (on by default for JDK classes) and application CDS (-XX:SharedArchiveFile) store pre-parsed classes to cut it down.

Cold start→CDS archive→Fewer ms to ready
NoClassDefFoundError: com/acme/util/JsonHelper

5. Dependency problems at deploy time

A library compiled against version 2 of a JAR but deployed with version 1 fails with NoClassDefFoundError or NoSuchMethodError. Build tools (Maven, Gradle) can show the dependency tree to find the conflict.

Runtime error→mvn dependency:tree→Align versions

Examples are simplified. Frameworks such as Spring and Hibernate also generate classes at run time (proxies), which are loaded by the same rules.

7 Run the code

Who loads what, and when

public class Main {
    static class Account {
        static int accounts = 0;
        static {
            System.out.println("Account class initialised");
        }
        Account() { accounts++; }
    }

    static class Savings extends Account {
        static double rate = 3.5;
        static {
            System.out.println("Savings class initialised");
        }
    }

    public static void main(String[] args) {
        System.out.println("main starts");
        Savings s = new Savings();
        Savings t = new Savings();
        System.out.println(Account.accounts + " accounts at " + Savings.rate + "%");
        System.out.println("Savings loaded by: " + Savings.class.getClassLoader().getName());
        System.out.println("String loaded by:  " + String.class.getClassLoader());
    }
}
main starts Account class initialised Savings class initialised 2 accounts at 3.5% Savings loaded by: app String loaded by: null
  • new Savings()First active use of Savings. Its superclass Account is initialised first.
  • Savings t = new Savings();No static block runs the second time; only constructors.
  • getClassLoader().getName()Your classes come from the application loader, named "app".
  • String.class.getClassLoader()null means the bootstrap loader, which is part of the JVM itself.
8 Line by line

Parent class first, then child, then objects

Recorded from the real JVM. The static entries appear with default values as soon as the classes are prepared; watch the static initializer frames fill them in, once.

Worked examples: real problems, solved and traced in memory

Three business programs where the order of class initialisation matters. Watch the static entries: a class's static fields appear with default values when it is prepared, get their real values when its static initializer runs, and that happens only once.

Example 1Billing · tax configuration

A config class that loads itself on first use

Problem statement

A billing service keeps tax settings in a TaxConfig class whose static fields are filled by a static initializer (here the state code is hard-coded; in real life it would come from a file). Show exactly when the configuration is loaded, and compute the GST on a Rs 2,000 bill.

Input
state = MH, GST 18% for MH, bill = Rs 2000
Expected output
Billing starts
TaxConfig loaded for MH
Tax: Rs 360

Approach

  1. Put the settings in static fields and compute them in a static block, so they are prepared once for the whole program.
  2. The static initializer runs on the first active use of the class, here new TaxConfig(), not when the program starts.
  3. After that, every object and method reads the same already-initialised static fields.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 2Payments · gateway client

Lazy singleton with a holder class

Problem statement

Creating a payment-gateway client is expensive, so the app wants exactly one, created only when the first payment happens. Use the initialization-on-demand holder idiom and show that two calls return the same object.

Input
getInstance() called twice
Expected output
App up, no client yet
Same client? true, created 1

Approach

  1. Put the single instance in a static field of a private nested class Holder.
  2. getInstance() reads Holder.INSTANCE. The first read is Holder's first active use, so the JVM initialises Holder then, creating the client.
  3. The JVM guarantees a class is initialised only once, even with many threads, so there is no need for synchronized.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 3Billing · invoice numbers

Constants are not fields: what triggers initialisation

Problem statement

Invoices are numbered from 1001 using a static counter, and GST is a constant 18%. Print the GST rate first, then create two invoices. Trace when class Invoice is initialised.

Input
amounts: Rs 500, Rs 1200
Expected output
GST is 18%
1001: Rs 590
1002: Rs 1416

Approach

  1. static final int GST_PERCENT = 18 is a compile-time constant: javac copies 18 into the code that uses it, so reading it does not initialise Invoice.
  2. static int nextNumber = 1001 is an ordinary static field. The first new Invoice triggers initialisation and sets it.
  3. Each constructor takes the next number with nextNumber++: one counter shared by all invoices.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

9 Quiz and revision

Check yourself

Quick revision

Lesson 8.4 in one screen

  • Phases: load → link (verify, prepare, resolve) → initialise. Classes are loaded lazily, when first needed.
  • Prepare = static fields get defaults; initialise = static initializers and blocks run, in source order, once.
  • Initialised on first active use: new, static method, non-constant static field, Class.forName, subclass init, main class.
  • Not on: compile-time constants, arrays of the type, class literals, parent field via child.
  • Loaders: bootstrap (null) → platform (Java 9+) → application; parent delegation.
  • ClassNotFoundException = loading by name failed; NoClassDefFoundError = was there at compile time, missing or broken now.
  • See it: java -verbose:class; a failed static block gives ExceptionInInitializerError then NCDFE.

Interview and viva questions

Explain the class loading process in Java.

A class loader finds the class's bytes and defines the class (loading). The JVM then links it: verifies the bytecode, prepares static fields with default values, and resolves symbolic references (often lazily). Finally it initialises the class by running its static field initializers and static blocks, once, on the first active use, after initialising its superclass.

What are the built-in class loaders and what is delegation?

Bootstrap (native, loads core java.base classes), platform (Java 9+, other JDK modules; the extension loader in Java 8) and application or system (the class path). Each loader first delegates to its parent; only if the parent can't find the class does it load it itself. This keeps core classes from being replaced and avoids loading the same class twice.

ClassNotFoundException vs NoClassDefFoundError?

ClassNotFoundException is a checked exception thrown when code explicitly loads a class by name and it isn't found. NoClassDefFoundError is an error thrown when a class that was available at compile time can't be found or initialised at run time, for example a missing JAR or a static initializer that failed earlier.

When exactly does a static block run?

Once, when the class is initialised: on its first active use, such as creating an instance, calling a static method, accessing a non-constant static field, calling Class.forName, initialising a subclass, or when it is the main class. Not when merely declaring a variable of the type, creating an array of it or reading a compile-time constant.

Can two classes with the same name exist in one JVM?

Yes, if different class loaders load them. A class's identity is its name plus its defining loader, so they are different types; casting one to the other throws ClassCastException. Application servers use this to isolate web applications.

Practice problems

Class loading questions are mostly about order: what is initialised, when, and by whom. Predict every line of output before running. The last three are business tasks.

P1Easy

Static block vs main

Put a static block in Main and a print as the first line of main. Which prints first?

Input
(no input)
Expected output
Main initialised, visits = 10
main starts
visits = 11
Show solution and explanation
public class Main {
    static int visits;

    static {
        visits = 10;
        System.out.println("Main initialised, visits = " + visits);
    }

    public static void main(String[] args) {
        System.out.println("main starts");
        visits++;
        System.out.println("visits = " + visits);
    }
}
Main initialised, visits = 10 main starts visits = 11

How it works: before calling main, the JVM must initialise class Main, because invoking a static method is an active use. So the static block always runs first, exactly once.

P2Easy

Which loader loaded it?

Print the class loader of String, java.sql.Date and your own Main class.

Input
String, java.sql.Date, Main
Expected output
String       -> bootstrap (shown as null)
java.sql.Date -> platform
Main         -> app
Parent of app -> platform
Show solution and explanation
public class Main {
    public static void main(String[] args) {
        ClassLoader forString = String.class.getClassLoader();
        ClassLoader forSqlDate = java.sql.Date.class.getClassLoader();
        ClassLoader forMain = Main.class.getClassLoader();

        System.out.println("String       -> " + (forString == null ? "bootstrap (shown as null)" : forString.getName()));
        System.out.println("java.sql.Date -> " + forSqlDate.getName());
        System.out.println("Main         -> " + forMain.getName());
        System.out.println("Parent of app -> " + forMain.getParent().getName());
    }
}
String -> bootstrap (shown as null) java.sql.Date -> platform Main -> app Parent of app -> platform

How it works: core classes in java.base are loaded by the bootstrap loader, which is part of the JVM itself and is represented by null. Other platform modules such as java.sql use the platform loader (Java 9+). Your classes on the class path use the application ("app") loader, whose parent is the platform loader.

P3Easy

Reading a constant doesn't initialise

A class has a static final constant and a static block. Show that reading the constant does not run the block, but reading a normal static field does.

Input
Limits.MAX_ITEMS, then Limits.discount
Expected output
Max items: 20
Limits initialised
Discount: 5%
Show solution and explanation
public class Main {
    static class Limits {
        static final int MAX_ITEMS = 20;     // compile-time constant
        static int discount = 5;             // ordinary static field
        static {
            System.out.println("Limits initialised");
        }
    }

    public static void main(String[] args) {
        System.out.println("Max items: " + Limits.MAX_ITEMS);
        System.out.println("Discount: " + Limits.discount + "%");
    }
}
Max items: 20 Limits initialised Discount: 5%

How it works: a static final primitive or String initialised with a constant expression is a compile-time constant: javac copies its value into the code that reads it, so the class isn't touched. discount is a real field read with getstatic, which is an active use and triggers initialisation.

P4Medium

Child.field initialises only the parent

A static field is declared in Parent and read through Child. Which static blocks run?

Input
Child.appName
Expected output
Parent initialised
Name: ShopEasy
---
Child initialised
Version: 2
Show solution and explanation
public class Main {
    static class Parent {
        static String appName = "ShopEasy";
        static { System.out.println("Parent initialised"); }
    }

    static class Child extends Parent {
        static { System.out.println("Child initialised"); }
        static int version = 2;
    }

    public static void main(String[] args) {
        System.out.println("Name: " + Child.appName);
        System.out.println("---");
        System.out.println("Version: " + Child.version);
    }
}
Parent initialised Name: ShopEasy --- Child initialised Version: 2

How it works: only the class that declares a static field is initialised when it is accessed, even if you write Child.appName. Child is initialised later, when its own field version is read. A favourite interview trick question.

P5Medium

Load a class by name, safely

Load classes by their names at run time with Class.forName. Report which exist instead of crashing.

Input
"java.util.ArrayList", "com.shop.PaymentGateway", "java.time.LocalDate"
Expected output
found:   ArrayList
missing: com.shop.PaymentGateway
found:   LocalDate
Show solution and explanation
public class Main {
    public static void main(String[] args) {
        String[] names = {"java.util.ArrayList", "com.shop.PaymentGateway", "java.time.LocalDate"};
        for (String name : names) {
            try {
                Class<?> c = Class.forName(name);
                System.out.println("found:   " + c.getSimpleName());
            } catch (ClassNotFoundException e) {
                System.out.println("missing: " + e.getMessage());
            }
        }
    }
}
found: ArrayList missing: com.shop.PaymentGateway found: LocalDate

How it works: Class.forName asks the class loaders to find a class by its fully qualified name. If no loader can find it, you get the checked exception ClassNotFoundException, which you are expected to handle. That differs from NoClassDefFoundError, which happens when a class that existed at compile time is missing or broken at run time.

P6Business problem

A bad config value breaks the class for good

A tax table reads its GST rate from a config string in a static initializer. The config contains "18%", which isn't a number. Show what happens on the first and second attempts to use the class.

Input
config value = "18%"
Expected output
attempt 1: java.lang.ExceptionInInitializerError
  cause: java.lang.NumberFormatException: For input string: "18%"
attempt 2: java.lang.NoClassDefFoundError: Could not initialize class Main$TaxTable
Show solution and explanation
public class Main {
    static class TaxTable {
        static int gstPercent = Integer.parseInt("18%");
    }

    public static void main(String[] args) {
        for (int attempt = 1; attempt <= 2; attempt++) {
            try {
                System.out.println("GST: " + TaxTable.gstPercent);
            } catch (Throwable e) {
                System.out.println("attempt " + attempt + ": " + e);
                if (e instanceof ExceptionInInitializerError) {
                    System.out.println("  cause: " + e.getCause());
                }
            }
        }
    }
}
attempt 1: java.lang.ExceptionInInitializerError cause: java.lang.NumberFormatException: For input string: "18%" attempt 2: java.lang.NoClassDefFoundError: Could not initialize class Main$TaxTable

How it works:

  1. The first use runs the static initializer, which throws NumberFormatException. The JVM wraps it in ExceptionInInitializerError.
  2. The class is now marked as failed. Every later use throws NoClassDefFoundError: Could not initialize class …, without retrying. In a server, this means one bad config value breaks the feature until a restart.
  3. Validate configuration with clear errors before it reaches static initializers, or load it in a normal method that can be retried.
P7Business problem

The discount that came out as zero

A shop's pricing class computes FESTIVE_PRICE from BASE_PRICE, but declares them in the wrong order. Show the bug, then the fixed order.

Input
base price Rs 1000, festive discount 20%
Expected output
Buggy festive price: Rs 0
Fixed festive price: Rs 800
Show solution and explanation
public class Main {
    static class BuggyPricing {
        static int festivePrice = discounted();   // runs first
        static int basePrice = 1000;              // not set yet!
        static int discounted() {
            return basePrice * 80 / 100;
        }
    }

    static class FixedPricing {
        static int basePrice = 1000;              // declared first
        static int festivePrice = discounted();
        static int discounted() {
            return basePrice * 80 / 100;
        }
    }

    public static void main(String[] args) {
        System.out.println("Buggy festive price: Rs " + BuggyPricing.festivePrice);
        System.out.println("Fixed festive price: Rs " + FixedPricing.festivePrice);
    }
}
Buggy festive price: Rs 0 Fixed festive price: Rs 800

How it works:

  1. During preparation, every static field gets its default value (0). During initialisation, the initializers run top to bottom.
  2. In BuggyPricing, discounted() runs while basePrice is still 0, so the festive price becomes 0, and stays 0 because it is computed only once.
  3. Declaring basePrice first fixes it. Making it static final int BASE_PRICE = 1000 (a constant) would also fix it, since constants are set before any initializer runs.
P8Business problem

Payment providers as plug-ins

A checkout reads the list of enabled payment providers (class names) from configuration and creates each one by name. Unknown providers must be skipped with a warning.

Input
enabled = "Main$UpiProvider", "Main$CardProvider", "Main$CryptoProvider"
Expected output
UPI paid Rs 499
Card paid Rs 499
skipped unknown provider Main$CryptoProvider
Show solution and explanation
public class Main {
    public interface Provider {
        String pay(int amount);
    }

    public static class UpiProvider implements Provider {
        public String pay(int amount) { return "UPI paid Rs " + amount; }
    }

    public static class CardProvider implements Provider {
        public String pay(int amount) { return "Card paid Rs " + amount; }
    }

    public static void main(String[] args) throws Exception {
        String[] enabled = {"Main$UpiProvider", "Main$CardProvider", "Main$CryptoProvider"};
        for (String className : enabled) {
            try {
                Class<?> c = Class.forName(className);
                Provider p = (Provider) c.getDeclaredConstructor().newInstance();
                System.out.println(p.pay(499));
            } catch (ClassNotFoundException e) {
                System.out.println("skipped unknown provider " + e.getMessage());
            }
        }
    }
}
UPI paid Rs 499 Card paid Rs 499 skipped unknown provider Main$CryptoProvider

How it works:

  1. Nested classes have binary names with a $, such as Main$UpiProvider.
  2. Class.forName loads and initialises the class; getDeclaredConstructor().newInstance() creates an object without a new in the source. This is how JDBC drivers, logging back-ends and framework plug-ins were traditionally wired in.
  3. A missing provider produces ClassNotFoundException, which we turn into a warning instead of a failed checkout.

Real-world scenario questions (asked in company interviews)

Class loading problems usually appear at deploy time, under pressure. Decide your answer first, then open the model answer.

Banking · deploymentFresher

Works on my machine, NoClassDefFoundError on the server

A loan-calculator service runs fine in the IDE. On the test server, the first EMI request fails with java.lang.NoClassDefFoundError: com/acme/util/MoneyFormatter. Start-up was fine. What is the likely cause and how do you check?

The interviewer is checking: do you know classes load lazily and what NCDFE means?

Show a strong answer
  • The code was compiled with MoneyFormatter available, but at run time the JAR containing it is missing from the class path. Because classes load lazily, the error appears only when the first EMI request needs the class, not at start-up.
  • Check the deployed package or class path (for example list the JAR's contents), and the build's dependency scope: a library marked “provided” or “test” is not packaged.
  • Also check the full stack trace: if it says “Could not initialize class”, the class exists but its static initializer failed earlier; look for the first ExceptionInInitializerError in the logs.

Avoid: catching the error and continuing.

E-commerce · configuration1–3 years

One bad property killed checkout until restart

A PricingRules class loads a properties file in a static block. After an ops change, a value became "15 percent". The first checkout failed with ExceptionInInitializerError; every checkout after that failed with NoClassDefFoundError, even after the file was corrected. Why didn't fixing the file help, and how would you redesign it?

The interviewer is checking: do you know a class is initialised only once, and that failure is permanent?

Show a strong answer
  • The static initializer threw NumberFormatException, so the JVM marked PricingRules as failed. It never re-runs a class's initializer, so the corrected file was never read; only a restart helps.
  • Redesign: load configuration in an ordinary method or a managed bean at start-up, validate every value with clear messages, and fail fast at start-up rather than on the first customer request.
  • For values that can change, support reloading with validation, keeping the last good configuration if the new one is invalid.

Avoid: putting I/O or parsing that can fail inside a static block.

SaaS · app server3+ years

Metaspace grows with every redeploy

An internal SaaS platform hot-redeploys a web app on Tomcat several times a day. After about 20 redeploys the server dies with OutOfMemoryError: Metaspace. Heap usage looks normal. Explain the mechanism and how you would find the cause.

The interviewer is checking: do you understand that classes are unloaded only when their class loader becomes unreachable?

Show a strong answer
  • Each deployment gets a new class loader, which loads its own copies of all the app's classes into metaspace. The old classes can be unloaded only when the old loader, every class it loaded and every object of those classes are unreachable.
  • Something outside the app still references the old loader: a JDBC driver registered in DriverManager, a thread the app started and never stopped, a ThreadLocal value on a server thread, or a static cache in a shared library.
  • Take a heap dump after a few redeploys, look for multiple web-app class loader instances, and follow the path to GC roots. Fix by deregistering drivers, stopping threads and clearing thread-locals on shutdown. Cap metaspace with -XX:MaxMetaspaceSize to fail early and visibly.

Avoid: simply raising the metaspace limit.

Payments · singleton design1–3 years

Lazy and thread-safe without locks

A payment SDK must create its expensive GatewayClient only when the first payment happens, and many threads may call getInstance() at the same moment. A colleague wrote a synchronized method with an if (instance == null) check. What alternative uses class loading rules, and why is it safe?

The interviewer is checking: can you apply initialisation guarantees to a real design?

Show a strong answer
  • Use the holder idiom: private static class Holder { static final GatewayClient INSTANCE = new GatewayClient(); } and getInstance() returns Holder.INSTANCE.
  • Holder is initialised on its first active use, the first call to getInstance(), so creation is lazy.
  • The JVM initialises a class under a lock and only once; other threads wait and then see the fully built instance. No synchronized on every call, so no lock cost afterwards.
  • An enum with a single constant is another safe option.

Avoid: double-checked locking without volatile; it is broken.