Reflection API in Java
Reflection is the ability of a Java program to examine and use classes at run time, including classes it knew nothing about when it was compiled. With reflection, a program can ask an object "which class are you?", list that class's fields, methods and constructors, read annotations, create objects, and call methods, all by name. The API lives in java.lang.Class and the package java.lang.reflect. Frameworks depend on it: Spring creates your beans, JUnit finds your test methods, and Jackson turns objects into JSON, all through reflection. In this lesson, you will learn about the Class object, how to get one, what it can tell you, and what reflection costs.
The Class Object
For every class, interface, enum, record, array type and primitive type that is loaded, the JVM creates one object of type java.lang.Class that describes it. This object is the entry point to everything else in reflection.
Three Ways to Get a Class Object
| Way | Example | Use it when |
|---|---|---|
The .class literal | Student.class | You know the class at compile time |
getClass() on an object | student.getClass() | You have an object and want its real class |
Class.forName(name) | Class.forName("com.school.Student") | You have only the name as text, for example from a configuration file |
Class.forName takes the fully qualified name and throws the checked ClassNotFoundException if no such class can be found.
Methods of Class
| Method | Description |
|---|---|
getName() | It is used to get the full name, such as java.lang.String. |
getSimpleName() | It returns the short name, such as String. |
getPackageName() | It returns the package name. |
getSuperclass() | It returns the Class of the superclass. |
getInterfaces() | It returns the interfaces the class directly implements. |
getModifiers() | It returns the modifiers as an int; decode with Modifier.toString. |
isInterface(), isEnum(), isRecord(), isArray() | They test what kind of type it is. |
getDeclaredFields(), getDeclaredMethods(), getDeclaredConstructors() | They return the members declared in the class. |
getAnnotation(type), isAnnotationPresent(type) | They read runtime annotations. |
getModule() | It returns the module the class belongs to. |
Example: Asking a Class About Itself
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
public class Meet {
interface Printable { }
static class Person { }
static final class Student extends Person
implements Printable, Comparable<Student> {
public int compareTo(Student o) {
return 0;
}
}
record Point(int x, int y) { }
enum Color { RED }
public static void main(String[] args) throws Exception {
Class<Student> a = Student.class;
Class<?> b = new Student().getClass();
Class<?> c = Class.forName("Meet$Student");
System.out.println(a == b && b == c);
System.out.println(a.getName());
System.out.println(a.getSimpleName());
System.out.println(a.getSuperclass().getSimpleName());
System.out.println(Arrays.toString(a.getInterfaces()));
System.out.println(Modifier.toString(a.getModifiers()));
System.out.println(Point.class.isRecord() + " "
+ Color.class.isEnum() + " " + Printable.class.isInterface()
+ " " + int[].class.isArray());
System.out.println(int.class.getName() + " "
+ int[].class.getName() + " " + String[].class.getName()
+ " " + int[].class.getSimpleName());
System.out.println(new ArrayList<String>().getClass()
== new ArrayList<Integer>().getClass());
System.out.println(ArrayList.class.getModule().getName());
try {
Class.forName("com.school.Missing");
} catch (ClassNotFoundException e) {
System.out.println("ClassNotFoundException: " + e.getMessage());
}
}
}true
Meet$Student
Student
Person
[interface Meet$Printable, interface java.lang.Comparable]
static final
true true true true
int [I [Ljava.lang.String; int[]
true
java.base
ClassNotFoundException: com.school.MissingExplanation:
true: all three ways return the sameClassobject. There is exactly one per loaded class (per class loader).Meet$Student: the JVM's name for a nested class joins the outer and inner names with$. That is the nameClass.forNameneeds.- Interfaces:
Comparableappears without<Student>. Generic type arguments are erased from the class itself. - Array names: the JVM writes
int[]as[IandString[]as[Ljava.lang.String;.getSimpleName()gives the readable form. ArrayList<String>vsArrayList<Integer>: both objects have the same class, which is type erasure seen through reflection.
What Reflection Is Used For
| Use | Example |
|---|---|
| Dependency injection | Spring finds classes with @Component, creates them and fills @Autowired fields |
| Test frameworks | JUnit finds methods with @Test and calls them |
| Serialization and mapping | Jackson and Hibernate read and write fields by name |
| Plugins | Loading a class whose name comes from configuration |
| Tools | IDE debuggers, object inspectors, code generators |
The Costs of Reflection
| Drawback | Why |
|---|---|
| No compile-time checking | A misspelled method name is found only when the code runs |
| Slower | Lookups by name and access checks cost time, though the JIT reduces the gap for repeated calls |
| Breaks encapsulation | It can reach private members, so classes can't rely on their own rules |
| Module restrictions | Since Java 9, code can't reflect into a module's non-exported internals without permission |
| Harder to read and refactor | Renaming a method doesn't update a string that names it |
Common Mistakes
Passing a simple name to Class.forName. It needs the full name with the package, and $ for nested classes.
Using reflection where an interface would do. If you control the classes, give them a common interface and call it normally.
Looking up the same Method or Field again and again. Look it up once and keep it.
Interview Corner
What is reflection in Java?
Reflection is an API (java.lang.Class and java.lang.reflect) that lets a program inspect classes, interfaces, fields, methods, constructors and annotations at run time, and use them: create objects, call methods and read or write fields by name, even for classes unknown at compile time. Frameworks such as Spring, Hibernate and JUnit are built on it. Its drawbacks are the loss of compile-time checking, slower performance, weakened encapsulation and module access restrictions.
How can you get the Class object of a class?
With the class literal (Student.class), by calling getClass() on an instance, or with Class.forName("fully.qualified.Name"), which loads the class by name and throws ClassNotFoundException if it's missing. All three return the same Class object for a given class and class loader.
Practice
- Easy: Print the name, superclass and interfaces of
java.util.ArrayListandjava.lang.Integer. - Harder: Write a method that takes any object and prints its whole superclass chain up to
Object, usinggetSuperclass()in a loop.
Key Takeaways
Was this tutorial helpful?