Reflection is an API that is used to examine or modify the behaviour of methods, classes, and interfaces at runtime.

Java reflection, which allows us to

  • Inspect classes, methods, fields, and constructors at runtime.
  • Access and modify private fields/methods.
  • Instantiate objects dynamically.
  • Invoke methods or create proxies dynamically.

Reflection is part of the java.lang.reflect package and shipped with the JDK, it is used extensively by different Java frameworks and libraries.

Drawbacks

  • Performance overhead
    • Slower than direct method calls or field access.
  • Security risks
    • Can bypass private access modifiers, leading to potential vulnerabilities.
  • Compile-time safety
    • Errors only show at runtime, not during compilation.

Getting the class object

The entry point for reflection is the Class object (java.lang.Class). Each class or interface loaded into the JVM is represented by an object of the Class class. A Class object contains metadata about a class, such as:

  • The class name.
  • Its fields, methods, and constructors.
  • Annotations, superclasses, and implemented interfaces.

There are three ways of getting the class object.

  • .class
    • The .class literal is a compile-time way of getting the Class object for a specific class or interface. It is used when the class is known at compile time and does not require an instance of the class
  • getClass()
    • The getClass() method is used to obtain the Class object of the runtime class of an object instance. It is used when you have an instance of the class, and you want to dynamically obtain its Class object, it requires an instance of the class to call the method.
  • Class.forName()
    • The Class.forName() method is used to load a class dynamically by its fully qualified name (package + class name) as a string. It is used when the class name is not known at compile time and must be determined at runtime (e.g., from a configuration file or user input). It requires the fully qualified name of the class as a string.
Class<?> cls1 = String.class;                     // Using the .class syntax  
Class<?> cls2 = "Hello".getClass();               // Using the object's getClass() method  
Class<?> cls3 = Class.forName("java.lang.String"); // Using Class.forName()  
  
System.out.println(cls1.getName()); 
System.out.println(cls2.getName());  
System.out.println(cls3.getName());
java.lang.String  
java.lang.String  
java.lang.String  

Inspect class information

You can inspect modifiers, fields, methods, constructors, and superclass of a class.

Class<?> cls = Class.forName("java.util.ArrayList");  
  
// Class name  
System.out.println("Class Name: " + cls.getName());  
  
// Modifiers  
int modifiers = cls.getModifiers();  
System.out.println("Is Public? " + Modifier.isPublic(modifiers));  
  
// Superclass  
System.out.println("Superclass: " + cls.getSuperclass());  
  
// Implemented interfaces  
Class<?>[] interfaces = cls.getInterfaces();  
System.out.println("Interfaces:");  
for (Class<?> i : interfaces) {  
    System.out.println(" - " + i.getName());  
}
Class Name: java.util.ArrayList
Is Public? true
Superclass: class java.util.AbstractList
Interfaces:
 - java.util.List
 - java.util.RandomAccess
 - java.lang.Cloneable
 - java.io.Serializable

Accessing fields

Fields (including private ones) can be accessed and modified using reflection.

class Person {
    private String name;
    
    public Person(String name) {
        this.name = name;
    }
}
 
public class Demo {
    public static void main(String[] args) throws Exception {
        Person person = new Person("John");
        
        // Access the private field
		Field field = Person.class.getDeclaredField("name");
        field.setAccessible(true); // Bypass private access
        
        // Get and modify the value
        System.out.println("Original name: " + field.get(person));
        field.set(person, "Alice");
        System.out.println("Modified name: " + field.get(person));
    }
}
Original name: John
Modified name: Alice

Access to multiple fields

class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
 
public class Demo {
    public static void main(String[] args) throws Exception {
		People p1 = new People(20, "John");
		
		Field[] fields = p1.getClass().getDeclaredFields();
		for (Field field : fields) {
			if (field.getName().equals("age")) {
				System.out.println("Age: " + field.get(p1));
			}
			if (field.getName().equals("name")) {
				System.out.println("Name: " + field.get(p1));
			}
		}
    }
}
Age: 20
Name: John

Accessing Methods

Methods can be invoked dynamically.

class Calculator {
	public int add(int a, int b) { return a + b; }
}
 
public class Demo {
	public static void main(String[] args) {
		Calculator calc = new Calculator();
		
		// Get the method
		Method method = Calculator.class.getMethod("add", int.class, int.class);
		// Invoke the method 
		int result = (int) method.invoke(math, 5, 3);
		System.out.println("Result: " + result);
	}
}
8

Accessing constructors

class Student {
    private String name;
    private age;
    
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
 
public class ConstructorReflectionExample {
    public static void main(String[] args) throws Exception {
        // Get the constructor
        Constructor<Student> constructor = Student.class.getConstructor(String.class, int.class);
        
        // Create a new instance
        Student student = constructor.newInstance("Emma", 19);
    }
}

Back to parent page: Java

Web_and_App_Development Programming_Languages Java Reflection

Referece: