skip to content

Reflection, MethodHandles & VarHandle

Runtime introspection and dynamic invocation: the Class object, member access, setAccessible under the module system, annotations and generics via reflection, dynamic proxies, MethodHandles and VarHandle. This is the machinery every Java framework runs on, which is why interviewers ask.

part ofJavaoverview, primer and where to startread it →
on this pageshow

explore

questions

page 1 of 2

What are the three ways to obtain a java.lang.Class object in Java, and how do they differ?

level: juniorimportance: must knowfreq 70%

answer

  1. literal = compile-time, no init
  2. getClass() = runtime type of an instance
  3. forName = by String, loads + initializes, checked exception
  4. only forName runs static initializers
  5. literal works for primitives and arrays

basics

~20 s

Use the .class literal (e.g. String.class) when you know the type at compile time, obj.getClass() to get the real runtime type of an existing object, and Class.forName("name") to look up a class by its String name at runtime.

solid answer

~40 s

There are three entry points to a Class object. The class literal Type.class is resolved at compile time, needs no instance, and does not trigger class initialization. obj.getClass() returns the actual runtime type of an existing object, so for a variable typed as the supertype it returns the concrete subclass. Class.forName("fully.qualified.Name") looks the class up by String at runtime; by default it loads AND initializes the class (runs static initializers), uses the caller's class loader, and throws a checked ClassNotFoundException if the name is unknown. A three-argument overload lets you control initialization and the class loader. You pick the literal for known types, getClass() for runtime inspection of an instance, and forName when the type is only known as a string (plugins, JDBC drivers, config).

code

java · 16 lines
java
// 1. class literal — compile-time, no initialization
Class<String> a = String.class;

// 2. getClass() — runtime type of an instance
Object o = "hello";
Class<?> b = o.getClass();          // class java.lang.String

// 3. forName — by name, loads + initializes, checked exception
try {
    Class<?> c = Class.forName("java.util.ArrayList");
    // load WITHOUT initializing:
    Class<?> d = Class.forName("java.util.ArrayList", false,
                              Thread.currentThread().getContextClassLoader());
} catch (ClassNotFoundException e) {
    // must handle the checked exception
}

go deeper

for a junior

Name the three ways and give one example of each (String.class, obj.getClass(), Class.forName("...")).

for a middle

Explain the differences: compile-time vs runtime, type safety, and that forName loads and initializes and throws a checked exception.

for a senior

Discuss initialization semantics precisely, the 3-arg forName overload, class-loader implications, and when each is the right tool (e.g. plugin loading).

for a principal

Reason about class-loader selection and isolation, lazy vs eager initialization trade-offs, security implications of forName on attacker-controlled names, and how frameworks abstract this.

## What is a Class object? In Java, every loaded type (class, interface, enum, annotation, array, even primitives) is represented at runtime by exactly one immutable instance of `java.lang.Class<T>`. This object is your handle for **reflection**: asking the JVM about a type's name, fields, methods, constructors, modifiers, superclass, and so on. The generic parameter `T` is the type it describes, so `Class<String>` describes `String`. There are three ways to get hold of one. ### 1. The class literal: `Type.class` ```java Class<String> c = String.class; ``` - **Resolved at compile time.** The compiler knows the type, so it is type-safe (`Class<String>`, not raw `Class`). - **Needs no instance.** You write the type's name directly. - **Does NOT initialize the class.** Merely referencing `Foo.class` does not run `Foo`'s static initializers. (Loading may still happen, but the JLS guarantees no *initialization* from a class literal alone.) - Works for primitives too: `int.class`, `void.class`, and array types: `int[].class`, `String[].class`. ### 2. `obj.getClass()` ```java Object o = "hello"; Class<?> c = o.getClass(); // class java.lang.String ``` - A method on `java.lang.Object`, so every object has it. - Returns the **actual runtime type**, not the declared/static type. If a variable is declared `Object` but holds a `String`, you get `String.class`. - Requires a non-null instance — calling it on `null` throws `NullPointerException`. - Its static type is `Class<? extends |the declared type|>` (a wildcard), because the runtime type may be a subtype of what you declared. ### 3. `Class.forName(String)` ```java Class<?> c = Class.forName("java.util.ArrayList"); ``` - Looks the class up by its **fully qualified name as a String** at runtime — the only option when the name is data (plugins, drivers, frameworks, config files). - **Loads and INITIALIZES** the class by default: it runs the static initializers and assigns static fields. This is why the old JDBC idiom `Class.forName("com.mysql.jdbc.Driver")` worked — loading the driver class ran its static block that registered it. - Uses the **caller's class loader**. - Throws the **checked** `ClassNotFoundException` if no such class is found, so you must handle or declare it. - A 3-arg overload, `Class.forName(name, initialize, classLoader)`, lets you suppress initialization (`false`) and choose a specific class loader. ### How to choose | Situation | Use | |---|---| | Type known at compile time | `Type.class` | | You have an instance and want its real type | `obj.getClass()` | | Type known only as a String at runtime | `Class.forName(...)` | ### Key differences summarized - **Initialization:** only `forName` (default) runs static initializers; the literal and `getClass()` do not (the class behind an existing object is, of course, already initialized). - **Compile-time safety:** the literal is fully type-safe; the other two yield wildcard `Class<?>`. - **Exceptions:** only `forName` throws a checked exception. - **Class loader:** `forName` uses the caller's loader (or one you pass); the others use whatever loaded the type already.

  • Why did old JDBC code call Class.forName("com.mysql.jdbc.Driver")?
    Because the 1-arg forName initializes the class, running the driver's static block that registered it with DriverManager. Since JDBC 4.0, ServiceLoader auto-registration makes this call unnecessary.
  • How do you load a class by name WITHOUT initializing it?
    Use the three-argument Class.forName(name, false, classLoader) and pass false for the initialize flag, or use classLoader.loadClass(name), which does not initialize.

saying these in an interview costs you the question

  • Saying the .class literal initializes the class (it does not)
  • Claiming getClass() returns the declared/static type rather than the runtime type
  • Forgetting ClassNotFoundException is checked
  • Thinking forName never initializes by default (the 1-arg form does)

context

open as a page

Why does an annotation need RUNTIME retention to be readable via reflection, and what happens if it doesn't have it?

level: juniorimportance: must knowfreq 70%

basics

~10 s

Reflection can only see annotations marked to survive into the running program. If an annotation isn't kept at runtime, reflection methods like getAnnotation return nothing, even though the annotation was in the source.

open as a page

How do you call a method on an object dynamically at runtime using reflection, and what does Method.invoke return?

level: juniorimportance: must knowfreq 60%

basics

~20 s

Get a Method object from the class (e.g. clazz.getMethod("name", paramTypes)), then call method.invoke(target, args). The first argument is the object to call it on (null for static methods); it returns the method's result as an Object.

open as a page

What is a dynamic proxy in Java, and how do you create one with java.lang.reflect.Proxy?

level: middleimportance: must knowfreq 55%

basics

~20 s

A dynamic proxy is a class Java builds at runtime that implements one or more interfaces. You create it with Proxy.newProxyInstance, passing a class loader, the interfaces, and an InvocationHandler whose invoke method runs for every call.

open as a page

When does setAccessible(true) throw InaccessibleObjectException, and how do you fix it?

level: middleimportance: must knowfreq 60%

basics

~20 s

Since Java 9, setAccessible(true) throws InaccessibleObjectException when the target type lives in a module whose package isn't opened for deep reflection. You fix it by opening that package — via an 'opens' directive in module-info or an --add-opens flag at launch.

open as a page

Given type erasure removes generic types at runtime, how can reflection still recover that a field is declared as List<String>?

level: middleimportance: must knowfreq 68%

basics

~20 s

Erasure removes generics from the values (objects) at runtime, but the generic types written in declarations (fields, method signatures, superclasses) are kept in the class file. Reflection reads them via getGenericType, and you inspect the result as a ParameterizedType to get String.

open as a page

Why is Class.newInstance() deprecated, and what is the modern way to create an instance reflectively?

level: middleimportance: must knowfreq 55%

basics

~10 s

Class.newInstance() is deprecated because it silently passes through any checked exception the constructor throws, breaking Java's checked-exception checking. Use clazz.getDeclaredConstructor(paramTypes).newInstance(args) instead, which wraps those exceptions in InvocationTargetException.

open as a page

What is the difference between getDeclaredFields()/getDeclaredMethods() and getFields()/getMethods() on a Java Class object?

level: middleimportance: must knowfreq 72%

basics

~10 s

getDeclared* returns ALL members written in that one class (any access level) but nothing inherited. getFields/getMethods returns only PUBLIC members, including ones inherited from parent classes and interfaces.

open as a page

What does setAccessible(true) do, and why might you call it when using reflection?

level: juniorimportance: should knowfreq 55%

basics

~10 s

setAccessible(true) tells Java to skip the normal access checks (public/private/protected) for a field, method, or constructor, so reflection can read or call a member it otherwise could not, such as a private field.

open as a page

When checking an object's type, when should you use getClass() comparison versus instanceof, and what is the trade-off?

level: middleimportance: should knowfreq 40%

basics

~20 s

instanceof returns true for subclasses too, so use it when subtypes should count as a match. getClass() == OtherClass requires the exact runtime type, ignoring subclasses. equals() methods often use getClass() so a subclass is not treated as equal to its parent.

open as a page

How do getName, getSimpleName, and getCanonicalName differ on a Class object?

level: middleimportance: should knowfreq 55%

basics

~20 s

getName() gives the JVM's internal name (with $ for nested classes and special codes for arrays). getSimpleName() gives just the short class name without any package. getCanonicalName() gives the dotted source-code name you would write to import it, or null when none exists.

open as a page

Show how to use a JDK dynamic proxy to add logging or timing transparently around any interface (a transparent decorator).

level: middleimportance: should knowfreq 35%

basics

~10 s

Wrap the real object in a proxy whose InvocationHandler logs before and after forwarding each call with method.invoke(target, args). Callers use the same interface and never know logging was added.

open as a page

What is a MethodHandle in java.lang.invoke, and how do you obtain one via MethodHandles.lookup()?

level: middleimportance: should knowfreq 45%

basics

~20 s

A MethodHandle is a typed, directly-callable reference to a method, constructor, or field. You first get a Lookup object from MethodHandles.lookup(), then ask it to find the method (e.g. findVirtual) and get back a handle you can invoke.

open as a page

Design a small reflection-based validator that reads a @NotBlank annotation on String fields. What must the annotation declare, and what reflection calls do you use?

level: middleimportance: should knowfreq 40%

basics

~10 s

Define @NotBlank with @Retention(RUNTIME) and @Target(FIELD). At runtime, loop over getDeclaredFields(), check isAnnotationPresent(NotBlank.class), call setAccessible(true), read the value with field.get(object), and fail if the String is null or blank.

open as a page

How do you read and write a field (including a private one) reflectively, and what does setAccessible(true) actually do?

level: middleimportance: should knowfreq 45%

basics

~10 s

Get a Field via getDeclaredField("name"), call field.setAccessible(true) if it's private, then field.get(obj) to read and field.set(obj, value) to write. Pass null as the object for static fields.

open as a page

Given a class hierarchy, how do you reflectively read a private field that is declared on a superclass rather than the most-derived class?

level: middleimportance: should knowfreq 48%

basics

~10 s

getDeclaredField only looks at one class, so start at the subclass and walk up via getSuperclass() until you find the field. Then call setAccessible(true) before reading it.

open as a page

What exactly does Class.forName do regarding class loading and initialization, and how can you control it?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Class.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.

open as a page

How does InvocationHandler.invoke work, and what are the gotchas around Object methods, primitives, and exceptions?

level: seniorimportance: should knowfreq 42%

basics

~20 s

invoke(proxy, method, args) runs for every call on the proxy. You read the method name and arguments, do your logic (often forwarding to a real object), and return the result. Calls to equals, hashCode, and toString also reach invoke.

open as a page

When would you choose a JDK dynamic proxy versus a bytecode library like CGLIB or ByteBuddy?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Use a JDK dynamic proxy when your target is behind an interface — it needs no extra libraries. Use CGLIB or ByteBuddy when you must proxy a concrete class with no interface, because JDK proxies only work with interfaces.

open as a page

What is the difference between MethodHandle.invokeExact() and invoke(), and why does invokeExact often throw WrongMethodTypeException?

level: seniorimportance: should knowfreq 40%

basics

~20 s

invokeExact requires the call's argument and return types to match the handle's signature exactly, with no conversions. invoke is more forgiving: it will box, unbox, widen, or cast as needed. invokeExact throws WrongMethodTypeException when the static types at the call site do not match precisely.

open as a page

How do MethodHandles compare to the classic Reflection API in terms of access checks and performance?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Reflection checks access on each use (unless you call setAccessible), boxes arguments into an Object array, and is hard for the JIT to optimise. A MethodHandle checks access once at lookup time and, especially with invokeExact, is much closer to a direct call, so it usually performs better.

open as a page

How do canAccess and trySetAccessible differ from setAccessible, and when should you use them?

level: seniorimportance: should knowfreq 38%

basics

~10 s

setAccessible(true) returns nothing and throws if denied. trySetAccessible() does the same enabling but returns true/false instead of throwing. canAccess(obj) just asks whether access would currently succeed, without changing anything.

open as a page

Can you mutate a private final field via setAccessible(true), and what are the broader risks of deep reflection?

level: seniorimportance: should knowfreq 32%

basics

~20 s

Not reliably. setAccessible(true) lets you reach a private field, but final fields are special: the JVM may have inlined their value and modern Java largely forbids reflective writes to them, so the change may not take effect or may throw. Treat final fields as immutable.

open as a page

What is the difference between getAnnotations() and getDeclaredAnnotations(), and when does @Inherited matter?

level: seniorimportance: should knowfreq 48%

basics

~10 s

getDeclaredAnnotations() returns only annotations written directly on that element. getAnnotations() also includes inherited ones from a superclass — but only annotations themselves marked @Inherited, and only for class inheritance.

open as a page

Why is reflective invocation slower than a direct call, and how can you reduce that cost in a hot path?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Reflection is slower because each call does extra work the compiler normally does up front: looking up members, checking access, boxing arguments into an Object[], and it inhibits some JIT optimizations. To cut the cost, look up Method/Field/Constructor objects once and cache them, call setAccessible(true), and reuse them — or use MethodHandles/codegen for the hottest paths.

open as a page

How does getMethod / getDeclaredMethod identify which overloaded method to return, and what is returned for inherited or interface default methods?

level: seniorimportance: should knowfreq 40%

basics

~20 s

You pass the method name plus the parameter Class types, so Java picks the exact matching overload. getMethod finds public methods including inherited and interface ones; getDeclaredMethod finds any-access methods declared in that one class only.

open as a page

How do you describe a method's signature with MethodType, and how does it relate to building and adapting MethodHandles?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

MethodType is an immutable object describing a signature: the return type plus the parameter types, with no name. You pass it to find* methods to say which overload you want, and you can derive new MethodTypes (change a parameter, drop one) to adapt a handle's shape.

open as a page

How are array and primitive types represented as Class objects, and how do their names encode dimensions?

level: seniorimportance: nice to knowfreq 25%

basics

~20 s

Primitives and arrays each have their own Class objects. Array getName() uses one leading [ per dimension plus an element code: [I is int[], [[I is int[][], and [Ljava.lang.String; is String[]. Primitives use single letters like I, Z, J, D.

open as a page

How do you read a method or class's type variables and their bounds via reflection, and what do you get for `<T extends Number & Comparable<T>>`?

level: seniorimportance: nice to knowfreq 28%

basics

~20 s

Use getTypeParameters() on a Class or Method to get its declared type variables (like T). Each TypeVariable has getBounds(), which returns its upper bounds — for <T extends Number & Comparable<T>> you get [Number, Comparable<T>].

open as a page

How do mocking frameworks and AOP frameworks use dynamic proxies under the hood, and what are the trade-offs versus MethodHandles or compile-time weaving?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Frameworks generate a proxy (interface-based JDK proxy or a subclass via ByteBuddy/CGLIB) and route every call through an interceptor. Mocks record/replay calls there; AOP runs advice there. Alternatives are MethodHandles for faster dispatch and compile-time weaving for zero runtime proxies.

open as a page

showing 1–30 of 34