Reflection API in Java

Naveen AnamaneniUpdated · 6 min read

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.

The Class object is the doorway to fields, methods and constructors

Three Ways to Get a Class Object

WayExampleUse it when
The .class literalStudent.classYou know the class at compile time
getClass() on an objectstudent.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

MethodDescription
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

Meet.java▶ Try it
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());
        }
    }
}
Output
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.Missing

Explanation:

  • true: all three ways return the same Class object. 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 name Class.forName needs.
  • Interfaces: Comparable appears without <Student>. Generic type arguments are erased from the class itself.
  • Array names: the JVM writes int[] as [I and String[] as [Ljava.lang.String;. getSimpleName() gives the readable form.
  • ArrayList<String> vs ArrayList<Integer>: both objects have the same class, which is type erasure seen through reflection.

What Reflection Is Used For

UseExample
Dependency injectionSpring finds classes with @Component, creates them and fills @Autowired fields
Test frameworksJUnit finds methods with @Test and calls them
Serialization and mappingJackson and Hibernate read and write fields by name
PluginsLoading a class whose name comes from configuration
ToolsIDE debuggers, object inspectors, code generators

The Costs of Reflection

DrawbackWhy
No compile-time checkingA misspelled method name is found only when the code runs
SlowerLookups by name and access checks cost time, though the JIT reduces the gap for repeated calls
Breaks encapsulationIt can reach private members, so classes can't rely on their own rules
Module restrictionsSince Java 9, code can't reflect into a module's non-exported internals without permission
Harder to read and refactorRenaming 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

  1. Easy: Print the name, superclass and interfaces of java.util.ArrayList and java.lang.Integer.
  2. Harder: Write a method that takes any object and prints its whole superclass chain up to Object, using getSuperclass() in a loop.

Quick check: Reflection

4 questions
1What does reflection allow?
2Which call loads a class from its name as text?
3How many Class objects exist for one loaded class (per class loader)?
4Which is a drawback of reflection?

Key Takeaways

Was this tutorial helpful?

Lesson 466 of 482