🪞 Java Reflection: The Magic Mirror of Your Code
Imagine you have a magic mirror that can look inside any toy box and tell you exactly what’s in there—even if the box is locked! That’s what Reflection does for Java programs.
🎭 What is Reflection?
Think of your Java program like a house. Normally, you can only enter through the front door (public methods). But Reflection gives you X-ray vision! You can see through walls, open locked rooms, and even rearrange furniture without the owner knowing.
In simple words: Reflection lets your program examine and change itself while it’s running.
// Normal way: You need to know everything beforehand
Person p = new Person();
p.sayHello();
// Reflection way: You discover things at runtime!
Class<?> clazz = Class.forName("Person");
Object obj = clazz.newInstance();
Why does this matter?
- 🛠️ Frameworks like Spring use it to create objects automatically
- 🔍 Debuggers use it to inspect your variables
- 📝 Testing tools use it to access private methods
📦 The Class Object: Your Blueprint Reader
Every object in Java comes from a blueprint called a Class. The Class object is like having a copy of that blueprint you can read anytime.
Three Ways to Get a Class Object
graph TD A["Get Class Object"] --> B[".class syntax"] A --> C[".getClass method"] A --> D["Class.forName"] B --> E["String.class"] C --> F["\"hello\".getClass#40;#41;"] D --> G["Class.forName#40;\"String\"#41;"]
Example: Getting the Class Object
// Way 1: Using .class (you know the type)
Class<String> c1 = String.class;
// Way 2: Using .getClass() (you have an object)
String name = "Alice";
Class<?> c2 = name.getClass();
// Way 3: Using Class.forName() (you have the name)
Class<?> c3 = Class.forName("java.lang.String");
What Can You Learn from a Class Object?
| What | Method | Example |
|---|---|---|
| Class name | getName() |
“java.lang.String” |
| Simple name | getSimpleName() |
“String” |
| Package | getPackage() |
java.lang |
| Is it an interface? | isInterface() |
false |
| Parent class | getSuperclass() |
Object.class |
🔎 Inspecting Class Members: Opening the Toy Box
Now that you have the Class object, let’s peek inside! A class has three types of members:
graph TD A["Class Members"] --> B["🏠 Fields<br>Variables that store data"] A --> C["⚙️ Methods<br>Actions the class can do"] A --> D["🏗️ Constructors<br>How to build new objects"]
Finding Fields (Variables)
Class<?> clazz = Person.class;
// Get ALL fields (including private ones)
Field[] allFields = clazz.getDeclaredFields();
// Get only PUBLIC fields (including inherited)
Field[] publicFields = clazz.getFields();
// Get ONE specific field by name
Field nameField = clazz.getDeclaredField("name");
Finding Methods
// Get ALL methods (including private ones)
Method[] allMethods = clazz.getDeclaredMethods();
// Get only PUBLIC methods (including inherited)
Method[] publicMethods = clazz.getMethods();
// Get ONE specific method by name and parameters
Method sayHi = clazz.getDeclaredMethod(
"sayHello",
String.class // parameter type
);
Finding Constructors
// Get ALL constructors
Constructor<?>[] allCons = clazz.getDeclaredConstructors();
// Get ONE specific constructor
Constructor<?> con = clazz.getDeclaredConstructor(
String.class, // first param type
int.class // second param type
);
🔓 Accessing via Reflection: Reading Secret Diaries
Here’s where the magic happens! You can read and write to private fields.
Reading a Private Field
class Secret {
private String password = "abc123";
}
// Normal Java: IMPOSSIBLE to access!
// Reflection: Hold my coffee ☕
Secret obj = new Secret();
Class<?> clazz = obj.getClass();
Field pwdField = clazz.getDeclaredField("password");
pwdField.setAccessible(true); // 🔑 The magic key!
String pwd = (String) pwdField.get(obj);
System.out.println(pwd); // Prints: abc123
Writing to a Private Field
pwdField.set(obj, "newPassword456");
// Now the password is changed!
⚠️ Important:
setAccessible(true)bypasses Java’s security. Use it wisely!
📞 Invoking via Reflection: Remote Control for Methods
You can call any method—even private ones—using reflection.
Calling a Method with No Arguments
class Greeter {
private void secretGreet() {
System.out.println("Shh! Secret hello!");
}
}
Greeter obj = new Greeter();
Class<?> clazz = obj.getClass();
Method method = clazz.getDeclaredMethod("secretGreet");
method.setAccessible(true);
method.invoke(obj); // Prints: Shh! Secret hello!
Calling a Method with Arguments
class Calculator {
private int add(int a, int b) {
return a + b;
}
}
Calculator calc = new Calculator();
Method addMethod = Calculator.class
.getDeclaredMethod("add", int.class, int.class);
addMethod.setAccessible(true);
int result = (int) addMethod.invoke(calc, 5, 3);
System.out.println(result); // Prints: 8
graph TD A["invoke Method"] --> B["Get the Method object"] B --> C["Set accessible if private"] C --> D["Call invoke with object + args"] D --> E["Get the return value"]
🏭 Creating via Reflection: Building Objects from Thin Air
You can create objects without using the new keyword!
Using Default Constructor
Class<?> clazz = Class.forName("com.example.Person");
// Old way (deprecated)
Object obj1 = clazz.newInstance();
// New way (preferred)
Constructor<?> con = clazz.getDeclaredConstructor();
Object obj2 = con.newInstance();
Using Constructor with Parameters
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
// Get the constructor that takes String and int
Constructor<?> con = Person.class
.getDeclaredConstructor(String.class, int.class);
// Create a new Person
Person p = (Person) con.newInstance("Alice", 25);
Creating Arrays via Reflection
// Create a String array of size 5
Object array = Array.newInstance(String.class, 5);
// Set values
Array.set(array, 0, "Hello");
Array.set(array, 1, "World");
// Get values
String first = (String) Array.get(array, 0);
🏷️ Reflection with Annotations: Reading the Labels
Annotations are like sticky notes on your code. Reflection can read them!
Defining a Custom Annotation
@Retention(RetentionPolicy.RUNTIME) // Keep at runtime!
@Target(ElementType.METHOD) // For methods only
public @interface Important {
String reason() default "No reason";
}
Using and Reading Annotations
class Task {
@Important(reason = "Critical for business")
public void doWork() {
// work happens here
}
}
// Reading the annotation
Method method = Task.class.getMethod("doWork");
if (method.isAnnotationPresent(Important.class)) {
Important ann = method.getAnnotation(Important.class);
System.out.println("Reason: " + ann.reason());
// Prints: Reason: Critical for business
}
Getting All Annotations
// Get all annotations on a method
Annotation[] annotations = method.getAnnotations();
for (Annotation a : annotations) {
System.out.println(a.annotationType().getName());
}
graph TD A["Annotation Reflection"] --> B["isAnnotationPresent?"] B -->|Yes| C["getAnnotation"] B -->|No| D["Skip"] C --> E["Read annotation values"] E --> F["Use in your logic"]
🎯 Quick Summary
| Task | Key Method |
|---|---|
| Get class info | Class.forName(), .getClass(), .class |
| Find fields | getDeclaredFields(), getDeclaredField() |
| Find methods | getDeclaredMethods(), getDeclaredMethod() |
| Find constructors | getDeclaredConstructors() |
| Access private | setAccessible(true) |
| Read field value | field.get(object) |
| Write field value | field.set(object, value) |
| Call method | method.invoke(object, args...) |
| Create object | constructor.newInstance(args...) |
| Check annotation | isAnnotationPresent(), getAnnotation() |
🌟 When to Use Reflection
✅ Good uses:
- Building frameworks (Spring, Hibernate)
- Testing private methods
- Plugin systems that load classes dynamically
❌ Avoid when:
- Normal coding (it’s slower!)
- You can achieve the same with regular Java
- Security is a top concern
Remember: Reflection is powerful but comes with responsibility. It bypasses Java’s safety checks, so use it only when you truly need it!
Now you have X-ray vision for Java code! Go forth and reflect wisely. 🪞✨
