1. Erzeugen von Objekten per Reflexion
Manchmal müssen wir Objekte erstellen, ohne ihren Typ zur Compile‑Zeit zu kennen. Zum Beispiel kommt der Klassenname aus der Konfiguration, oder wir schreiben ein generisches Framework (Serializer, DI‑Container). Im normalen Code würden wir schreiben:
User user = new User("Ivan", 25);
Was aber, wenn wir nicht wissen, dass die Klasse User heißt, und die Parameter ihres Konstruktors nicht kennen? Hier hilft Reflexion.
Veralteter Ansatz: Class.newInstance()
Früher gab es in Java die Methode Class.newInstance(), die ein Objekt über den öffentlichen, parameterlosen Konstruktor erzeugt:
Class<?> clazz = Class.forName("com.example.User");
Object obj = clazz.newInstance();
WICHTIG: Diese Methode ist seit Java 9 als deprecated markiert und wird nicht empfohlen. Sie erlaubt die Konstruktorwahl nicht, verschleiert Fehlerursachen und erfordert einen public-Konstruktor ohne Parameter.
Moderner Ansatz: über Konstruktoren
Im realen Code gibt es längst nicht immer einen public-Konstruktor ohne Parameter. Der aktuelle Ansatz besteht darin, den benötigten Konstruktor über getConstructor(...) oder getDeclaredConstructor(...) zu holen und dann newInstance(...) darauf aufzurufen.
Beispiel: Objekt mit Parametern erstellen
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
import java.lang.reflect.Constructor;
Class<?> clazz = Class.forName("User");
// Konstruktor mit Parametern String, int holen
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
// Objekt erzeugen, Parameter übergeben
Object user = constructor.newInstance("Ivan", 25);
System.out.println(user); // toString, falls definiert
Was passiert hier?
- Wir holen den benötigten Konstruktor anhand der Signatur.
- Wir rufen newInstance(...) darauf auf und übergeben die Argumente.
- Wir erhalten ein Objekt vom Typ Object (kann zu User gecastet werden, wenn der Typ bekannt ist).
Wenn der Konstruktor privat ist?
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true); // Magie! Jetzt lässt sich der private Konstruktor aufrufen.
Object user = constructor.newInstance("Ivan", 25);
ACHTUNG: Missbrauchen Sie das nicht – die Kapselung wird verletzt. In modularen Anwendungen (Java 9+) gelten außerdem Zugriffsbeschränkungen.
Vergleichstabelle der Objekt-Erzeugungsmethoden
| Methode | Geschwindigkeit | Typsicherheit | Wann einsetzen |
|---|---|---|---|
|
Am schnellsten | Vollständig | Immer, wenn möglich |
|
Langsam | Geht verloren | Frameworks, Plugins |
|
Mittel | Teilweise | Performante Bibliotheken |
| Factories | Schnell | Vollständig | Flexibles Erzeugen von Objekten |
2. Methodenaufrufe per Reflexion
Sie können jede Methode eines Objekts aufrufen, selbst wenn Sie ihren Namen zur Compile‑Zeit nicht kennen oder wenn sie private ist.
Methode ermitteln
import java.lang.reflect.Method;
Class<?> clazz = user.getClass();
// Öffentliche Methode anhand von Name und Parametertypen holen
Method method = clazz.getMethod("getName"); // Ohne Parameter
Object result = method.invoke(user); // Methodenaufruf ohne Parameter
System.out.println(result); // Gibt den Namen des Benutzers aus
Aufruf einer Methode mit Parametern
Method setName = clazz.getMethod("setName", String.class);
setName.invoke(user, "Petr"); // Neuen Namen setzen
Aufruf einer privaten Methode
Method secret = clazz.getDeclaredMethod("secretMethod", int.class);
secret.setAccessible(true); // Zugriffsschutz aufheben
Object secretResult = secret.invoke(user, 123);
Wichtig: Alle Parameter werden als Objekt‑Array (Varargs) übergeben. Gibt die Methode etwas zurück, kommt das Ergebnis als Object.
3. Zugriff auf Felder per Reflexion
Manchmal müssen Sie den Wert eines Feldes lesen oder ändern, selbst wenn es private ist (z. B. bei Serialisierung oder Tests).
Ein Feld ermitteln
import java.lang.reflect.Field;
Class<?> clazz = user.getClass();
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true); // Falls das Feld nicht public ist
// Wert lesen
Object age = ageField.get(user);
System.out.println("Alter: " + age);
// Wert ändern
ageField.set(user, 42);
System.out.println("Neues Alter: " + ageField.get(user));
Mit statischen Feldern arbeiten
Wenn das Feld statisch ist, übergeben Sie bei get/set statt des Objekts null:
Field staticField = clazz.getDeclaredField("counter");
staticField.setAccessible(true);
staticField.set(null, 100); // Für static‑Felder ist kein Objekt nötig
4. Einschränkungen und Ausnahmen
Bei der Arbeit mit Reflexion treten viele checked Exceptions auf. Die häufigsten sind:
- ClassNotFoundException – Klasse mit diesem Namen nicht gefunden.
- NoSuchMethodException – Kein Konstruktor oder keine Methode mit dieser Signatur.
- NoSuchFieldException – Feld nicht gefunden.
- IllegalAccessException – Kein Zugriff auf Member (z. B. private Methode ohne setAccessible(true)).
- InstantiationException – Klasse ist abstrakt oder ein Interface; Objekt kann nicht erstellt werden.
- InvocationTargetException – Fehler innerhalb des aufgerufenen Konstruktors oder der Methode.
Beispiel für die Behandlung:
try {
// ... Ihr Reflexions‑Code ...
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
Wenn Details unwichtig sind, ist es bequemer, die gemeinsame Oberklasse ReflectiveOperationException (Java 7+) zu fangen.
5. Praxis: Mini‑Programm „Dynamischer Instanziator“
Beispielklasse für Experimente
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private void sayHello() {
System.out.println("Hallo, ich heiße " + name + ", ich bin " + age + " Jahre alt.");
}
}
Dynamische Erstellung und Manipulation
import java.lang.reflect.*;
public class ReflectionDemo {
public static void main(String[] args) {
try {
// 1. Class‑Objekt per Name laden
Class<?> clazz = Class.forName("Person");
// 2. Konstruktor holen und Objekt erzeugen
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object person = constructor.newInstance("Alice", 30);
// 3. Private Methode sayHello aufrufen
Method sayHello = clazz.getDeclaredMethod("sayHello");
sayHello.setAccessible(true);
sayHello.invoke(person); // Hallo, ich heiße Alice, ich bin 30 Jahre alt.
// 4. Privates Feld name ändern
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(person, "Bob");
// 5. sayHello erneut aufrufen
sayHello.invoke(person); // Hallo, ich heiße Bob, ich bin 30 Jahre alt.
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
}
}
Beachten Sie:
- Alles erfolgt, ohne den Typ Person im Code direkt zu kennen.
- Man kann private Felder ändern und private Methoden aufrufen (sofern dies durch die JVM‑Sicherheitsrichtlinie und die Modulkonfiguration erlaubt ist).
6. Wie hängt das mit realen Anwendungen zusammen?
Reflexion bildet die Grundlage vieler populärer Bibliotheken und Frameworks:
- JUnit: Finden von Methoden mit der Annotation @Test, Erzeugen von Testinstanzen und Methodenaufrufe.
- Spring: DI, Bean‑Erzeugung, Autowiring.
- Jackson, Gson: Serialisierung/Deserialisierung von Objektfeldern.
- Hibernate: Zugriff auf Entity‑Felder, Proxys und Lazy Loading.
7. Visuelles Schema: Wie ein Objekt per Reflexion erstellt wird
flowchart TB
A["Klassenname (String)"] --> B["Class.forName"]
B --> C["Class<?>"]
C --> D["getConstructor(...)"]
D --> E["Constructor<?>"]
E --> F["newInstance(...)"]
F --> G["Object"]
8. Typische Fehler beim Arbeiten mit Reflexion
Fehler Nr. 1: Falscher Konstruktor. Sie fragen einen Konstruktor mit falscher Signatur an, z. B. getConstructor(String.class), obwohl es nur einen Konstruktor mit zwei Parametern gibt – das führt zu NoSuchMethodException. Prüfen Sie immer die Signaturen!
Fehler Nr. 2: Kein Zugriff. Der Aufruf eines privaten Konstruktors/einer privaten Methode ohne setAccessible(true) führt zu IllegalAccessException. Und bedenken Sie: In Java 9+ kann das Modulsystem zusätzliche Beschränkungen auferlegen.
Fehler Nr. 3: Typ-Probleme. Alle Parameter und Rückgabewerte über Reflexion sind Object. Falsches Casting – ClassCastException.
Fehler Nr. 4: Unbehandelte Ausnahmen. Reflexion wirft viele checked Exceptions. Behandeln Sie sie z. B. über die gemeinsame ReflectiveOperationException, sonst kompiliert der Code nicht.
Fehler Nr. 5: Verletzung der Kapselung. Gedankenloses setAccessible(true) ist wie „Zutritt zum fremden Kühlschrank“. Verwenden Sie es nur bei echter Notwendigkeit und beachten Sie Sicherheitsanforderungen.
GO TO FULL VERSION