1. Was ist Reflexion
Reflexion in Java ist ein Mechanismus, der es einem Programm erlaubt, seine eigene Struktur zur Laufzeit zu untersuchen und sogar zu ändern. Es ist, als könnten Sie in sich selbst hineinschauen und feststellen: „Welche Felder und Methoden habe ich? Welchen Konstruktor habe ich? Welche Klasse bin ich überhaupt?“ – und sogar eine private Methode aufrufen.
Formale Definition:
Reflexion ist eine API, die es erlaubt, zur Laufzeit Informationen über Klassen, Interfaces, Felder, Methoden und Konstruktoren zu erhalten und mit ihnen zu arbeiten.
Wozu braucht man Reflexion?
- Frameworks: Spring, Hibernate, JUnit verwenden Reflexion für „Magie“: automatisches Erzeugen von Objekten, Dependency Injection, Aufruf von Methoden nach Namen und per Annotationen.
- Serialisierung: Objekte in JSON/XML umwandeln und zurück – man muss Felder erkennen und auf sie zugreifen können.
- Testen: Methoden mit Annotationen (z. B. @Test) automatisch finden und aufrufen.
- Dynamisches Laden von Klassen: Laden einer Klasse zur Laufzeit per Namen (Plugins, Treiber).
- IDEs und Code-Analyse: Autovervollständigung, Inspektionen, Refactoring.
Beispiel aus dem Alltag
public class Person {
private String name;
private int age;
public void sayHello() {
System.out.println("Hallo, ich heiße " + name);
}
}
Mit Reflexion können wir zur Laufzeit herausfinden, dass die Klasse Person die Felder name und age sowie die Methode sayHello hat. Darüber hinaus kann man den Wert eines privaten Feldes ändern oder eine Methode per Name aufrufen.
Ein wenig Humor
Man kann sagen, dass Reflexion wie in „Matrix“ ist: Sie erkennen, dass Ihr ganzer Code Daten sind, die man im laufenden Betrieb untersuchen und ändern kann. Vergessen Sie nur nicht die rote Sicherheits‑Pille!
2. Die Klasse Class: das Herz der Reflexion
In Java ist zur Laufzeit jedes Objekt und jeder Typ mit einem besonderen Objekt vom Typ Class verbunden. Dieses Objekt ist die „Metainformation“ über den Typ. Die Klasse java.lang.Class<T> ist der zentrale Einstiegspunkt in die Welt der Reflexion.
Wie erhält man ein Objekt Class?
Es gibt mehrere Wege:
- Über .class
Der direkteste und sicherste Weg, wenn die Klasse zur Kompilierzeit bekannt ist:Class<Person> personClass = Person.class; - Über ein Objekt: getClass()
Wenn Sie bereits eine Instanz haben:
Hier ist clazz das Objekt Class, das die tatsächliche Klasse des Objekts p beschreibt.Person p = new Person(); Class<?> clazz = p.getClass(); - Über einen String: Class.forName()
Wenn der Klassenname nur zur Laufzeit bekannt ist:
Geeignet zum Laden von Plugins, Treibern und sonstiger Dynamik.Class<?> clazz = Class.forName("com.example.Person");
Beispiel: Ermitteln des Klassennamens
Person p = new Person();
Class<?> clazz = p.getClass();
System.out.println(clazz.getName()); // Gibt aus: Person oder com.example.Person
Tabelle: Wichtige Möglichkeiten, ein Objekt Class zu erhalten
| Wie erhalten | Wann verwenden | Beispiel |
|---|---|---|
|
Wenn die Klasse zur Kompilierzeit bekannt ist | |
|
Wenn ein Objekt vorhanden ist | |
|
Wenn der Klassenname zur Laufzeit bekannt ist | |
Schema: Objekt und sein Class
+-------------------+
| Person p |
+-------------------+
|
v
+-------------------+
| p.getClass() |
+-------------------+
|
v
+-------------------+
| Class<Person> |
+-------------------+
Typprüfung über Class
Manchmal muss man feststellen, ob ein Objekt zu einer bestimmten Klasse gehört:
if (p.getClass() == Person.class) {
System.out.println("Das ist definitiv eine Person!");
}
Oder unter Berücksichtigung der Vererbung – der normale Operator instanceof:
if (p instanceof Person) {
// funktioniert wie üblich
}
3. Wann Reflexion nötig ist – und wann nicht
Mit großer Macht kommt große Verantwortung. Reflexion ist ein mächtiges Werkzeug, aber man sollte sie nur einsetzen, wenn es wirklich notwendig ist.
Wo Reflexion notwendig ist
- Frameworks und Bibliotheken: Spring, Hibernate, JUnit, Jackson – Automatisierung, „Magie“ und weniger „Handarbeit“ im Code.
- Serialisierung: Man muss die Felder eines Objekts kennen, um es in JSON/XML zu verwandeln.
- Plugins und Erweiterungen: Klassen werden dynamisch geladen, ihre Struktur ist vorher unbekannt.
Warum man Reflexion nicht überstrapazieren sollte
- Verlust der Typsicherheit: Fehler treten erst zur Laufzeit auf.
- Wartbarkeit: Der Code wird weniger offensichtlich.
- Leistung: Langsamer als direkte Aufrufe.
- Sicherheit: Man kann die Kapselung umgehen und private Daten offenlegen.
Beispiel: Wenn Reflexion nicht nötig ist
p.sayHello();
Beispiel: Wenn Reflexion ein Muss ist
Sie schreiben ein Mini‑Testframework: Der Nutzer markiert Methoden mit der Annotation @Test, und das Programm soll sie automatisch finden und aufrufen – ohne Reflexion geht das nicht.
4. Demonstration: Einstieg in Class
Beispiel: Ausgabe des Klassennamens und seiner Superklasse
public class ReflectionDemo {
public static void main(String[] args) {
String s = "Hello, reflection!";
Class<?> clazz = s.getClass();
System.out.println("Klassenname: " + clazz.getName());
System.out.println("Einfacher Klassenname: " + clazz.getSimpleName());
System.out.println("Paket: " + clazz.getPackageName());
System.out.println("Oberklasse: " + clazz.getSuperclass().getName());
}
}
Ergebnis:
Klassenname: java.lang.String
Einfacher Klassenname: String
Paket: java.lang
Oberklasse: java.lang.Object
Beispiel: Class für primitive Typen erhalten
Class<Integer> intClass = int.class;
System.out.println(intClass.getName()); // int
Class<?> doubleClass = double.class;
System.out.println(doubleClass.getName()); // double
Beispiel: Class für ein Array erhalten
int[] arr = new int[10];
Class<?> arrClass = arr.getClass();
System.out.println(arrClass.getName()); // [I (spezifisches Format für Arrays)
Beispiel: Prüfung der Zugehörigkeit zu einem Typ
if (arrClass.isArray()) {
System.out.println("Das ist ein Array!");
}
5. Praxis: Mini‑Programm „Was für eine Klasse?“
Schreiben wir ein Programm, das den vollqualifizierten Klassennamen entgegennimmt (zum Beispiel "java.util.ArrayList") und grundlegende Informationen darüber ausgibt.
import java.util.Scanner;
public class ClassInfoPrinter {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.print("Geben Sie den vollständigen Klassennamen ein: ");
String className = scanner.nextLine();
Class<?> clazz = Class.forName(className);
System.out.println("Klassenname: " + clazz.getName());
System.out.println("Paket: " + clazz.getPackageName());
System.out.println("Oberklasse: " + clazz.getSuperclass().getName());
Class<?>[] interfaces = clazz.getInterfaces();
System.out.print("Implementiert Schnittstellen: ");
for (Class<?> i : interfaces) {
System.out.print(i.getName() + " ");
}
System.out.println();
}
}
Beispielausgabe:
Geben Sie den vollständigen Klassennamen ein: java.util.ArrayList
Klassenname: java.util.ArrayList
Paket: java.util
Oberklasse: java.util.AbstractList
Implementiert Schnittstellen: java.util.List java.util.RandomAccess java.lang.Cloneable java.io.Serializable
Interessante Fakten zu Class und Reflexion
- Jeder geladene Typ in der JVM hat einen gemeinsamen Repräsentanten – ein Objekt Class. Alle Instanzen eines Typs teilen sich dasselbe Class<T>.
- Man kann die Art des Typs bestimmen: isInterface(), isEnum(), isArray(), isPrimitive() usw.
- Gibt man in Class.forName einen falschen Namen an, erhält man die Ausnahme ClassNotFoundException.
- Mit Reflexion kann man Objekte erzeugen, ohne die Klasse zur Kompilierzeit zu kennen – darüber sprechen wir in der nächsten Vorlesung.
6. Typische Fehler beim ersten Kontakt mit Reflexion
Fehler Nr. 1: Zu erwarten, dass Reflexion schnell ist.
Tatsächlich ist Reflexion langsamer als normale Methodenaufrufe und Feldzugriffe. Verwenden Sie sie nicht für jede Kleinigkeit.
Fehler Nr. 2: Versuch, ein Class für eine nicht vorhandene Klasse zu bekommen.
Wenn Sie sich im Namen vertun, erhalten Sie ClassNotFoundException. Behandeln Sie diese Ausnahme.
Fehler Nr. 3: Verwechslung zwischen Class‑Objekt und Instanz.
Das Objekt Class beschreibt die Struktur eines Typs und ist nicht dessen Instanz.
Fehler Nr. 4: Reflexion ohne Notwendigkeit einsetzen.
Wenn es mit normalem Code geht – machen Sie es besser so. Reflexion ist für Dynamik, Frameworks, Plugins und ähnliche Aufgaben gedacht.
Fehler Nr. 5: Unvorsichtiger Umgang mit privaten Feldern und Methoden.
Reflexion erlaubt es, die Kapselung zu umgehen, was zu Fehlern und Sicherheitsproblemen führen kann – insbesondere in großen Projekten.
GO TO FULL VERSION