Chaturmind
LearnDSASystem DesignInterview PrepDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth
  • Java Interview Prep

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Java Interview Prep: 5–8 Years

Advanced Core Java

  • Advanced OOP & Design Scenarios — Interview Questions
  • Advanced Concurrency, Collections & Memory — Interview Questions
  • Modern Java Language Features & Annotations — Interview Questions
  • Class Loading, Reflection, Serialization & Idioms — Interview Questions

Java Design Patterns in Depth

  • Design Patterns Overview & Singleton — Interview Questions
  • Factory & Abstract Factory Patterns — Interview Questions
  • Builder & Prototype Patterns — Interview Questions
  • Adapter & Bridge Patterns — Interview Questions
  • Composite & Decorator Patterns — Interview Questions
  • Facade & Proxy Patterns — Interview Questions
  • Chain of Responsibility & Observer Patterns — Interview Questions
  • Strategy & Template Method Patterns — Interview Questions
  • Command Pattern — Interview Questions

Advanced Spring Boot

  • Logging, Configuration & Actuator (Advanced) — Interview Questions
  • Transactions, Multiple Datasources & Query Tuning — Interview Questions
  • Validation & REST API Design (Advanced) — Interview Questions
  • Reactive, Async & Scheduling in Spring Boot — Interview Questions
  • Deployment, High Availability, Scaling & Caching — Interview Questions
Chaturmind
← Java Interview Prep: 5–8 Years

Advanced Core Java

  • Advanced OOP & Design Scenarios — Interview Questions
  • Advanced Concurrency, Collections & Memory — Interview Questions
  • Modern Java Language Features & Annotations — Interview Questions
  • Class Loading, Reflection, Serialization & Idioms — Interview Questions

Java Design Patterns in Depth

  • Design Patterns Overview & Singleton — Interview Questions
  • Factory & Abstract Factory Patterns — Interview Questions
  • Builder & Prototype Patterns — Interview Questions
  • Adapter & Bridge Patterns — Interview Questions
  • Composite & Decorator Patterns — Interview Questions
  • Facade & Proxy Patterns — Interview Questions
  • Chain of Responsibility & Observer Patterns — Interview Questions
  • Strategy & Template Method Patterns — Interview Questions
  • Command Pattern — Interview Questions

Advanced Spring Boot

  • Logging, Configuration & Actuator (Advanced) — Interview Questions
  • Transactions, Multiple Datasources & Query Tuning — Interview Questions
  • Validation & REST API Design (Advanced) — Interview Questions
  • Reactive, Async & Scheduling in Spring Boot — Interview Questions
  • Deployment, High Availability, Scaling & Caching — Interview Questions
HomeLearnJava Interview PrepJava Interview Prep: 5–8 YearsAdvanced Core Java
✓ FreeAdvanced· 11 min read

Class Loading, Reflection, Serialization & Idioms — Interview Questions

Serializing complex objects securely, how Java handles untrusted code now that the Security Manager is gone, Strategy vs State, Observer in event-driven apps, plugins with class loaders, Class.forName vs loadClass, the class-loader hierarchy since Java 9, circular constructor dependencies, an O(1) LRU cache, LinkedHashSet vs TreeSet, final fields and reflection, enum singletons and strategies, Externalizable, and coding standards.

Published September 25, 2026


How to use this lesson

This lesson mixes JVM internals with design idioms. Several popular answers are outdated: the Security Manager, rt.jar, "the Extension class loader". Giving the Java 17/21 reality marks you as current. Where a question invites it, finish with the safer, modern alternative.

Q1. Scenario: serialize a complex object graph with nested objects and transient fields, with integrity and security. How?

Short answer:

  • Prefer an explicit, schema-based format over Java serialization: JSON with Jackson and DTOs, or Protobuf or Avro. You control exactly what's written, the schema can evolve, and you avoid deserialization gadget attacks.
  • Exclude sensitive or derived fields: transient, or better, omit them from DTOs. Validate on read (compact constructors, Bean Validation).
  • Integrity: sign the payload (an HMAC, or a digital signature), so tampering is detected.
  • Confidentiality: encrypt it with keys from a KMS.
  • If you must use Java serialization:
    • declare serialVersionUID;
    • use a serialization proxy (writeReplace/readResolve), so invariants are re-checked through the constructor;
    • validate in readObject;
    • install an ObjectInputFilter allow-list;
    • never deserialize untrusted data.
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
        "com.acme.orders.*;java.base/*;!*");                    // allow only known classes
try (var in = new ObjectInputStream(bytes)) {
    in.setObjectInputFilter(filter);
    Order order = (Order) in.readObject();
}

Learn it in depth → Exception Design & Serialization Edge Cases

Q2. How does Java enforce security restrictions on code loaded over the network?

Short answer: Historically (applets, RMI), the Security Manager, with a policy file, sandboxed untrusted code through permission checks on file, network and reflection access. It was deprecated for removal in Java 17 (JEP 411), and permanently disabled in Java 24 (JEP 486). Applets are gone too.

The layers that remain are:

  • bytecode verification and type safety (no pointer arithmetic, bounds checks);
  • module encapsulation (strong by default since Java 17);
  • signed JARs.

Today, isolation comes from outside the JVM: separate processes or containers, restricted OS users, seccomp, network policies. Don't load untrusted code into your JVM. If you must run user code, use a sandboxed process or WebAssembly.

Q3. What's the difference between the Strategy and State patterns?

Short answer: Both delegate behaviour to an interchangeable object, but they differ in who chooses it and why:

  • Strategy: the client picks one algorithm from a family: a pricing rule, a sort order, a compression codec. The strategies are usually unaware of each other, and the choice is often made once.
  • State: the object's own state determines its behaviour, and states trigger transitions to other states. An order is PLACED → PAID → SHIPPED, and cancel() behaves differently in each state. It replaces big switch-on-status blocks.

Learn it in depth → State Pattern

Q4. How would you apply the Observer pattern in an event-driven application?

Short answer:

  • Within one service: publish domain events (OrderPlaced), and let independent listeners react (@EventListener, or @TransactionalEventListener + @Async). The publisher doesn't know its subscribers.
  • Across services: the same idea over a broker (Kafka), which is pub/sub.

Design points:

  • Decide synchronous vs asynchronous delivery.
  • Listener failure isolation: one failing observer shouldn't break the others.
  • Ordering, idempotency, and unsubscribing (to avoid leaks from long-lived subjects).
  • Back-pressure for slow observers.

Learn it in depth → Observer Pattern

Q5. How would you load plugins dynamically at runtime with class loaders?

Short answer:

  1. Define a plugin API (an interface) in a shared JAR.
  2. Load each plugin JAR in its own URLClassLoader, with the application's API loader as the parent, for isolation.
  3. Discover the implementations with ServiceLoader (entries in META-INF/services), instead of hard-coded class names.
  4. Instantiate them, and interact only through the API interface.
  5. To unload a plugin, drop every reference to its instances and its loader, and close() the loader.
URL[] urls = { pluginJar.toUri().toURL() };
URLClassLoader loader = new URLClassLoader("plugin-" + name, urls, PaymentPlugin.class.getClassLoader());
ServiceLoader<PaymentPlugin> plugins = ServiceLoader.load(PaymentPlugin.class, loader);
plugins.findFirst().ifPresent(registry::register);

Key points to cover:

  • Risks: class-loader leaks (threads, statics, ThreadLocals from the plugin), version conflicts between plugins (hence the isolation), and security, because plugins run with full privileges. Load only trusted, signed plugins. Frameworks: PF4J, OSGi, or the JPMS ModuleLayer.

Q6. What's the difference between Class.forName() and ClassLoader.loadClass()?

Short answer:

  • Class.forName(name) loads, links and initialises the class (static initialisers run), using the caller's class loader by default. Class.forName(name, initialize, loader) gives you control over both.
  • loader.loadClass(name) loads the class, but doesn't initialise it. Initialisation happens on first active use.

Key points to cover:

  • Old JDBC code used Class.forName("com.mysql.cj.jdbc.Driver") precisely for its initialisation side effect: the driver registered itself. Since JDBC 4, drivers are found automatically through ServiceLoader.
  • In frameworks and application servers, prefer the thread context class loader, or an explicit loader, so classes resolve in the right application's context.

Q7. What types of class loader does Java have?

Short answer (Java 9+):

  • Bootstrap: native. It loads the core modules (java.base…), and appears as null.
  • Platform: other Java SE and JDK modules. It replaced the Extension loader.
  • Application (system): your class path and module path.

Custom loaders sit below these (app servers per web application, OSGi, Spring Boot's LaunchedClassLoader, DevTools' restart loader). Loading follows parent delegation.

Common trap: describing rt.jar and the jre/lib/ext extension directory. Both were removed in Java 9 by the module system.

Learn it in depth → JVM, Memory & Class Loading

Q8. ClassA and ClassB each need the other in their constructors. How do you resolve the circular dependency?

Short answer: First, treat it as a design smell. Two classes that can't exist without each other usually share a responsibility that belongs in a third class, or one of them only needs to notify the other (use events or callbacks). If a bidirectional link really is required:

  • Construct one object, and pass it to the other, which sets the back-reference on the first (a package-private setter, or a factory that wires both).
  • Depend on an interface or a Supplier/provider, resolved lazily.
  • In Spring: @Lazy or ObjectProvider on one side. Spring Boot 2.6+ forbids circular references by default.
final class A { private B b; void attach(B b) { this.b = b; } }
final class B { private final A a; B(A a) { this.a = a; a.attach(this); } }   // wiring in one place (or a factory)

Q9. How do you implement an LRU cache? (The question says "with a LinkedList".)

Short answer: A plain LinkedList makes both lookup and move-to-front O(n). The correct O(1) design is a HashMap from key to node, plus a doubly linked list of nodes: the map finds the node, and the list moves it to the head or evicts the tail in O(1). In Java, LinkedHashMap with accessOrder = true implements exactly this.

final class LruCache<K, V> extends LinkedHashMap<K, V> {
    private final int capacity;
    LruCache(int capacity) { super(16, 0.75f, true); this.capacity = capacity; }   // accessOrder = true
    @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > capacity; }
}

Key points to cover:

  • It isn't thread-safe. Wrap it with locks, or use Caffeine in production (concurrent, with near-optimal eviction and stats).

Learn it in depth → Thread-Safe LRU Cache

Q10. When does a LinkedHashSet outperform a TreeSet, and when is it the other way round?

Short answer:

  • LinkedHashSet (a hash table plus a linked list) gives O(1) add, remove and contains, with insertion-order iteration. It wins for fast membership checks and "unique, in arrival order".
  • TreeSet (a red-black tree) gives O(log n) operations with sorted order, plus navigation: first, ceiling, headSet, range views. It wins when you need sorted iteration, range queries, or nearest-element lookups.

Key points to cover:

  • TreeSet uniqueness uses compareTo, not equals.

Q11. What happens if you change a final field with reflection?

Short answer:

  • For instance final fields of ordinary classes, field.setAccessible(true) followed by set(...) succeeds, but the result is unreliable. The JIT may have constant-folded or cached the old value, and other threads may never see the change, because the final-field memory guarantees assume no mutation.
  • Compile-time constants (static final primitives or Strings with constant initialisers) are inlined at compile time, so readers never see the change.
  • static final fields can't be modified through Field.set (it throws IllegalAccessException).
  • Record fields and hidden classes refuse modification.
  • Module encapsulation blocks deep reflection into non-open packages.

Future JDKs are moving to forbid final-field mutation by default ("integrity by default").

Key points to cover:

  • Treat it as unsupported. Libraries that need it (deserializers) use constructors, or MethodHandles with sanctioned access, instead.

Q12. How do you implement Singleton and Strategy with an enum?

Short answer:

  • Singleton: a single-constant enum, INSTANCE. It's thread-safe (initialised with the class), serialization-safe, and reflection-proof.
  • Strategy: each constant overrides an abstract method, or holds a lambda. You get a closed, type-safe set of strategies that are easy to select by name.
public enum ShippingStrategy {
    STANDARD { public BigDecimal cost(Order o) { return new BigDecimal("40"); } },
    EXPRESS  { public BigDecimal cost(Order o) { return new BigDecimal("120"); } },
    FREE_OVER_999 { public BigDecimal cost(Order o) {
        return o.total().compareTo(new BigDecimal("999")) >= 0 ? BigDecimal.ZERO : new BigDecimal("40"); } };

    public abstract BigDecimal cost(Order o);
}
ShippingStrategy chosen = ShippingStrategy.valueOf(request.shippingMode());

Key points to cover:

  • Enum strategies are a closed set, so adding one means changing the enum. For extensible strategies (plugins, per-tenant rules), use an interface plus Spring beans instead.

Q13. What are the differences between Externalizable and Serializable?

Short answer:

SerializableExternalizable
ControlAutomatic (with optional writeObject/readObject hooks)Full manual control: writeExternal/readExternal
ConstructorNot called (the first non-serializable superclass's no-arg constructor runs)Public no-arg constructor required, called before readExternal
Metadata writtenClass descriptors plus all non-transient fieldsOnly what you write (smaller, often faster)
Evolution / versioningserialVersionUID, with default compatibility rulesEntirely your responsibility
RiskGadget attacks if deserializing untrusted dataThe same risks, plus hand-written bugs

Key points to cover:

  • Modern systems rarely use either. Prefer JSON, Protobuf or Avro, or Kryo for JVM-internal caching.

Q14. What coding standards do you follow as a Java developer?

Short answer (show that standards are automated and agreed):

  • Style: a team-wide formatter (Spotless with google-java-format or Palantir), enforced in CI, so style never comes up in review.
  • Naming and structure: intention-revealing names, small focused methods and classes, package-by-feature, and minimal visibility.
  • Correctness: immutability by default, Optional for absent return values, no null collections, equals/hashCode contracts, and careful concurrency.
  • Errors: meaningful exceptions with context, and no swallowed exceptions.
  • Tests: unit tests plus slice and integration tests (Testcontainers), and test names that describe behaviour.
  • Static analysis: SpotBugs, Error Prone, SonarQube quality gates, and dependency scanning.
  • Reviews: small PRs, checklists, and ADRs for significant decisions.
  • Guides: Effective Java, and a team style guide or Google Java Style.

Follow-up questions this topic invites — and their answers

Q: What is ServiceLoader? A: The JDK's built-in plugin discovery mechanism. Providers are declared in META-INF/services/<interface>, or with provides in module-info, and loaded lazily with ServiceLoader.load(Interface.class).

Q: What is the thread context class loader, and why do frameworks use it? A: A class loader attached to each thread (Thread.getContextClassLoader()). Framework code, loaded by a parent loader, uses it to load application classes and resources that it can't see through its own loader. That's typical in application servers and plugin systems.

Q: Why is reflection slower, and has it improved? A: Reflection used to bypass JIT optimisations, and needed access checks. Since Java 18 it's implemented on method handles (JEP 416), which performs much better once warmed up. MethodHandles and LambdaMetafactory offer near-direct-call speed for hot paths.

Q: How do you prevent a class from being deserialized at all? A: Throw InvalidObjectException from readObject / readObjectNoData, or use a serialization proxy that rejects direct deserialization. Better still, don't implement Serializable.

Previous

Modern Java Language Features & Annotations — Interview Questions

Next

Design Patterns Overview & Singleton — Interview Questions

AI Tutor

Lesson: Class Loading, Reflection, Serialization & Idioms — Interview Questions

Quick actions

AI responses can be inaccurate. Verify critical information.