Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
165: Inspecting Classes at Runtime
I've noticed a recurring theme when developers first encounter the Reflection API: they treat it like a read-only debugger. You might think that reflection is simply a way to "peek" at the metadata of a class you've already written—perhaps to print out a list of method names or check if a class implements a certain interface during a unit test. In other words, the misconception is that reflection is a passive observation tool for your own source code.
Reflection isn't just a mirror; it's a remote control
The truth is far more potent (and dangerous). Reflection isn't just for observing; it's for manipulating. You can instantiate objects of classes you didn't even know existed when you compiled your code, invoke methods by name, and—most controversially—bypass the private modifier entirely. I've used this in the past to fix bugs in third-party libraries where I couldn't change the source code but desperately needed to change a private internal state to make a feature work.
Consider this: if you have a java.lang.String, you know it has a private field called value (in older Java versions) or value (in newer ones). You can't access it in your code normally. But with reflection, you can reach inside that JDK class and pull the value out. It’s not just for your classes; it’s for every class loaded into the JVM.
The Class object as your entry point
Everything starts with the java.lang.Class object. Think of this as the "passport" for a type. You can get it three ways: using the class literal (String.class), calling getClass() on an instance, or using Class.forName("java.lang.String") if you only have the name as a string.
// Imagine we are building a plugin system where we don't know the class names at compile time
String className = "com.myapp.plugins.AdvancedCalculator";
Class> pluginClass = Class.forName(className);
// We can create an instance of this class dynamically
Object pluginInstance = pluginClass.getDeclaredConstructor().newInstance();
I'll be honest: Class.forName is where the real magic happens. It allows your program to be extensible. You can put a class name in a config file, and your app can load that logic without you ever having to recompile the main engine. This is exactly how Spring Framework handles Dependency Injection.
Breaking the visibility seal
Now, let's talk about the "dark arts." By default, if you try to call getDeclaredField("somePrivateField") and then call field.get(instance), Java will throw an IllegalAccessException. The JVM is trying to protect the encapsulation of the object. However, the Field and Method classes have a method called setAccessible(true).
When you call this, you're essentially telling the JVM, "I know what I'm doing, stop checking the access modifiers."
public class SecretVault {
private String secretCode = "12345-ABC";
}
// ... inside your reflection logic ...
SecretVault vault = new SecretVault();
Field field = SecretVault.class.getDeclaredField("secretCode");
field.setAccessible(true); // This is the key move
String value = (String) field.get(vault);
System.out.println("Stolen secret: " + value);
Use this sparingly. If you find yourself using setAccessible(true) in your everyday business logic, you're likely fighting the architecture of your system rather than working with it. But when you're building a generic JSON serializer or a testing framework, it's an indispensable tool.
The performance tax
One last thing I want you to keep in mind: reflection is slow. When you call a method normally, the JVM can optimize it, inline it, and make it lightning fast. When you use reflection, the JVM has to perform expensive lookups in the constant pool and verify access rights every single time. If you're doing this in a tight loop—say, inside a game render loop or a high-frequency trading system—you're going to tank your performance. Cache your Method and Field objects if you must use them repeatedly.
📋 Practical Task
Build a Dynamic Command Dispatcher
Your goal is to create a system that can execute methods of a class based on a string input, without using a giant switch statement. This mimics how many CLI tools and bot frameworks handle commands.
Requirements:
- Create a class called
CommandLibrary. Give it several public methods (e.g.,sayHello(),printDate(),calculateSum(int a, int b)). - Create a class called
CommandDispatcherwith a method:public void execute(String methodName, Object... args). - Inside
execute, use the Reflection API to:- Find the method in
CommandLibrarythat matches themethodNamestring. - Determine the parameter types of that method to ensure you can call
getMethod()` correctly (Hint: you may need to iterate throughgetDeclaredMethods()to find the one with the matching name). - Invoke that method on an instance of
CommandLibraryusing the providedargs.
- Find the method in
- Handle potential exceptions like
NoSuchMethodExceptionorIllegalAccessExceptiongracefully by printing a user-friendly error message.
Test Case: Your dispatcher should be able to take the string "calculateSum" and the integers 10, 20, and successfully trigger the calculateSum method in the library class to output 30.
There are no comments for now.