CodeGym /Kurse /JAVA 25 SELF /Objekte über Reflexion erstellen

Objekte über Reflexion erstellen

JAVA 25 SELF
Level 62 , Lektion 3
Verfügbar

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
new User()
Am schnellsten Vollständig Immer, wenn möglich
Constructor.newInstance()
Langsam Geht verloren Frameworks, Plugins
MethodHandle
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.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION