Java Reflection - Complete Tutorial

Java Reflection is a powerful feature that allows developers to inspect and manipulate the properties of classes, methods, fields, and constructors at runtime. This tutorial covers the essential aspects of Java Reflection, including practical examples to demonstrate its capabilities.

Table of Contents

  1. Introduction to Java Reflection
  2. Getting Class Objects
  3. Inspecting Class Information
  4. Working with Fields
  5. Working with Methods
  6. Working with Constructors
  7. Creating Instances
  8. Accessing Private Members
  9. Working with Arrays
  10. Working with Enums
  11. Reflection Utilities and Best Practices
  12. Common Use Cases and Examples
  13. Java Reflection Interview Questions

1. Introduction to Java Reflection

Java Reflection is a part of the Java API that allows programs to analyze and modify the behavior of applications running in the Java Virtual Machine (JVM). It provides the ability to inspect classes, interfaces, fields, and methods at runtime without knowing their names at compile time.

2. Getting Class Objects

To use reflection, the first step is to obtain the Class object representing the class you want to inspect. There are several ways to get the Class object:

Using .class Syntax

Class<?> clazz = MyClass.class;

Using getClass() Method

MyClass obj = new MyClass();
Class<?> clazz = obj.getClass();

Using Class.forName() Method

Class<?> clazz = Class.forName("com.example.MyClass");

3. Inspecting Class Information

Once you have the Class object, you can inspect various aspects of the class, such as its name, modifiers, superclass, and implemented interfaces.

Example

Class<?> clazz = MyClass.class;

// Get class name
String className = clazz.getName();
System.out.println("Class Name: " + className);

// Get superclass
Class<?> superClass = clazz.getSuperclass();
System.out.println("Superclass: " + superClass.getName());

// Get implemented interfaces
Class<?>[] interfaces = clazz.getInterfaces();
System.out.println("Implemented Interfaces: ");
for (Class<?> iface : interfaces) {
    System.out.println(iface.getName());
}

4. Working with Fields

Reflection allows you to access and manipulate fields of a class, including private fields.

Example: Accessing Fields

import java.lang.reflect.Field;

class MyClass {
    public int publicField;
    private String privateField;

    public MyClass(int publicField, String privateField) {
        this.publicField = publicField;
        this.privateField = privateField;
    }
}

public class FieldExample {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = MyClass.class;
        MyClass obj = new MyClass(10, "Hello");

        // Access public field
        Field publicField = clazz.getField("publicField");
        int publicValue = publicField.getInt(obj);
        System.out.println("Public Field Value: " + publicValue);

        // Access private field
        Field privateField = clazz.getDeclaredField("privateField");
        privateField.setAccessible(true);
        String privateValue = (String) privateField.get(obj);
        System.out.println("Private Field Value: " + privateValue);
    }
}

Output

Public Field Value: 10
Private Field Value: Hello

5. Working with Methods

Reflection provides the ability to inspect and invoke methods of a class.

Example: Invoking Methods

import java.lang.reflect.Method;

class MyClass {
    public void printMessage(String message) {
        System.out.println("Message: " + message);
    }
}

public class MethodExample {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = MyClass.class;
        MyClass obj = new MyClass();

        // Get method
        Method method = clazz.getMethod("printMessage", String.class);

        // Invoke method
        method.invoke(obj, "Hello, World!");
    }
}

Output

Message: Hello, World!

6. Working with Constructors

Reflection allows you to access and invoke constructors of a class.

Example: Invoking Constructors

import java.lang.reflect.Constructor;

class MyClass {
    private int number;
    private String text;

    public MyClass(int number, String text) {
        this.number = number;
        this.text = text;
    }

    @Override
    public String toString() {
        return "MyClass [number=" + number + ", text=" + text + "]";
    }
}

public class ConstructorExample {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = MyClass.class;

        // Get constructor
        Constructor<?> constructor = clazz.getConstructor(int.class, String.class);

        // Create new instance using constructor
        MyClass obj = (MyClass) constructor.newInstance(42, "Reflection Example");

        System.out.println(obj);
    }
}

Output

MyClass [number=42, text=Reflection Example]

7. Creating Instances

Reflection can be used to create new instances of a class.

Example: Creating Instances

Class<?> clazz = MyClass.class;
MyClass obj = (MyClass) clazz.getConstructor(int.class, String.class).newInstance(42, "Reflection Example");

8. Accessing Private Members

Private members can be accessed using reflection by setting the Accessible flag to true.

Example: Accessing Private Members

Field privateField = clazz.getDeclaredField("privateField");
privateField.setAccessible(true);
String privateValue = (String) privateField.get(obj);

9. Working with Arrays

Reflection provides methods to inspect and manipulate arrays.

Example: Working with Arrays

import java.lang.reflect.Array;

public class ArrayReflectionExample {
    public static void main(String[] args) {
        // Create an array of integers
        int[] intArray = (int[]) Array.newInstance(int.class, 5);

        // Set values in the array
        Array.set(intArray, 0, 10);
        Array.set(intArray, 1, 20);

        // Get values from the array
        int value0 = Array.getInt(intArray, 0);
        int value1 = Array.getInt(intArray, 1);

        System.out.println("Value at index 0: " + value0);
        System.out.println("Value at index 1: " + value1);
    }
}

Output

Value at index 0: 10
Value at index 1: 20

10. Working with Enums

Reflection can be used to inspect and manipulate enum types.

Example: Working with Enums

import java.lang.reflect.Method;

enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

public class EnumReflectionExample {
    public static void main(String[] args) {
        Class<?> clazz = Day.class;

        // Get all enum constants
        Object[] enumConstants = clazz.getEnumConstants();
        for (Object constant : enumConstants) {
            System.out.println(constant);
        }

        // Use reflection to invoke valueOf method
        try {
            Method valueOfMethod = clazz.getMethod("valueOf", String.class);
            Day day = (Day) valueOfMethod.invoke(null, "MONDAY");
            System.out.println("Value of MONDAY: " + day);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Output

MONDAY
TUESDAY
WEDNESDAY
THURSDAY
FRIDAY
SATURDAY
SUNDAY
Value of MONDAY: MONDAY

11. Reflection Utilities and Best Practices

Utility Methods

Java provides several utility methods for reflection in the java.lang.reflect package. These include:

  • Modifier class: To inspect class and member modifiers.
  • Array class: To create and manipulate arrays.

Best Practices

  • Performance: Reflection can be slower than direct access. Use it judiciously.
  • Security: Reflection can bypass access control checks. Be cautious when using it in secure environments.
  • Maintainability: Code using reflection can be harder to read and maintain. Ensure clear documentation.

12. Common Use Cases and Examples

Dynamic Proxy

Creating dynamic proxies for implementing interfaces at runtime.

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Hello {
    void sayHello();
}

class HelloWorld implements Hello {
    public void sayHello() {
        System.out.println("Hello, World!");
    }
}

class DynamicInvocationHandler implements InvocationHandler {
    private final Object target;

    public DynamicInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method call");
        Object result = method.invoke(target, args);
        System.out.println("After method call");
        return result;
    }
}

public class DynamicProxyExample {
    public static void main(String[] args) {
        Hello hello = new HelloWorld();
        InvocationHandler handler = new DynamicInvocationHandler(hello);

        Hello proxy = (Hello) Proxy.newProxyInstance(
                hello.getClass().getClassLoader(),
                hello.getClass().getInterfaces(),
                handler
        );

        proxy.sayHello();
    }
}

Output

Before method call


Hello, World!
After method call

13. Java Reflection Interview Questions

Java Reflection Interview Questions

Conclusion

Java Reflection is a powerful tool that enables dynamic inspection and manipulation of classes, methods, fields, and constructors at runtime. While it provides flexibility and is useful in various scenarios like dynamic proxies and frameworks, it should be used cautiously due to potential performance and security implications. Understanding and applying reflection appropriately can significantly enhance the capabilities of Java applications.

Comments