![translation](https://cdn.durumis.com/common/trans.png)
Dies ist ein von KI übersetzter Beitrag.
Sprache auswählen
Von durumis AI zusammengefasster Text
- Reflection ist eine API, die es ermöglicht, zur Laufzeit auf Klasseninformationen zuzugreifen und Klassen nach Bedarf zu manipulieren.
- Mit Reflection können Instanzen von Klassen erstellt und auf Felder und Methoden zugegriffen werden, unabhängig von den Zugriffskontrollmodifizierern. Dies ist besonders nützlich in großen Entwicklungsphasen, wie z. B. in Frameworks, um Abhängigkeiten dynamisch zu verwalten.
- Es kann jedoch die Kapselung beeinträchtigen und zu Leistungseinbußen führen, daher sollte es nur verwendet werden, wenn dies unbedingt erforderlich ist.
Was ist Reflection?
Reflection ist eine API, die es ermöglicht, Instanzen einer gewünschten Klasse über das Objekt vom Typ Class zu erstellen, das im Heap-Bereich geladen wurde, und auf die Felder und Methoden der Instanz zuzugreifen, unabhängig von den Zugriffskontrollmodifizierern.
Dabei bezieht sich der Begriff "geladene Klasse" auf den Vorgang, in dem der Klassenlader der JVM das Laden der Klassendatei abgeschlossen hat und einObjekt vom Typ Classerstellt, das die Informationen dieser Klasse enthält, und sie im Heap-Bereich des Speichers speichert. Dies unterscheidet sich von den Objekten, die mit dem Schlüsselwort "new" erstellt werden. Wenn Sie das Objekt vom Typ Class nicht verstehen, lesen Sie die JDK-Dokumentation zu java.lang.class.
Verwendung
Bevor Sie Reflection verwenden können, müssen Sie ein Objekt vom Typ Class abrufen, das im Heap-Bereich geladen wurde. Es gibt drei Möglichkeiten, dies zu tun.
- Abrufen mit Klasse.class
- Abrufen mit Instanz.getClass()
- Abrufen mit Class.forName("Klassenname")
public class Member {
private String name;
protected int age;
public String hobby;
public Member() {
}
public Member(String name, int age, String hobby) {
this.name = name;
this.age = age;
this.hobby = hobby;
}
public void speak(String message) {
System.out.println(message);
}
private void secret() {
System.out.println("Das Passwort ist 1234.");
}
@Override
public String toString() {
return "Member{" +
"name='" + name + '\'' +
", age=" + age +
", hobby='" + hobby + '\'' +
'}';
}
}
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
Class memberClass = Member.class;
System.out.println(System.identityHashCode(memberClass));
Member member = new Member("Jayon", 23, "Darath Entwicklung");
Class extends Member> memberClass2 = member.getClass();
System.out.println(System.identityHashCode(memberClass2));
Class> memberClass3 = Class.forName("{Paketname}.Member");
System.out.println(System.identityHashCode(memberClass3));
}
}
// Ausgabe
1740000325
1740000325
Sie können sehen, dass die drei Methoden das gleiche Objekt vom Typ Class zurückgeben. Unabhängig davon, welche Methode Sie verwenden, ist der Hash-Wert der gleiche, daher können Sie ihn je nach Situation entsprechend verwenden.
Jetzt können Sie mit dem abgerufenen Objekt vom Typ Class eine Instanz dieser Klasse erstellen und auf die Felder und Methoden der Instanz zugreifen, unabhängig vom Zugriffskontrollmodifizierer. Lassen Sie uns zunächst eine Instanz dieser Klasse erstellen.
public class Main {
public static void main(String[] args) throws Exception {
// Alle Konstruktoren von Member ausgeben
Member member = new Member();
Class extends Member> memberClass = member.getClass();
Arrays.stream(memberClass.getConstructors()).forEach(System.out::println);
// Instanz mit dem Standardkonstruktor von Member erstellen
Constructor extends Member> constructor = memberClass.getConstructor();
Member member2 = constructor.newInstance();
System.out.println("member2 = " + member2);
// Instanz mit einem anderen Konstruktor von Member erstellen
Constructor extends Member> fullConstructor =
memberClass.getConstructor(String.class, int.class, String.class);
Member member3 = fullConstructor.newInstance("Jayon", 23, "Darath Entwicklung");
System.out.println("member3 = " + member3);
}
}
// Ausgabe
public Member()
public Member(java.lang.String,int,java.lang.String)
member2 = Member{name='null', age=0, hobby='null'}
Mit getConstructor() können Sie den Konstruktor abrufen und mit newInstance() eine Member-Instanz dynamisch erstellen.
Schließlich können wir auf die Felder und Methoden der Instanz zugreifen, unabhängig vom Zugriffskontrollmodifizierer.
public class Main {
public static void main(String[] args) throws Exception {
Member member = new Member("Jayon", 23, "Darath Entwicklung");
Class extends Member> memberClass = member.getClass();
// Feldzugriff
Field[] fields = memberClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.get(member));
}
fields[0].set(member, "Jayon2");
System.out.println(member);
// Methodenzugriff
Method speakMethod = memberClass.getDeclaredMethod("speak", String.class);
speakMethod.invoke(member, "Reflection Test");
Method secretMethod = memberClass.getDeclaredMethod("secret");
secretMethod.setAccessible(true);
secretMethod.invoke(member);
}
}
// Ausgabe
Jayon
23
Darath Entwicklung
Member{name='Jayon2', age=23, hobby='Darath Entwicklung'}
Reflection Test
Mit getDeclaredFileds() können Sie alle Instanzvariablen der Klasse abrufen, mit get() den Wert des Felds zurückgeben und mit set() den Wert des Felds ändern. Beachten Sie, dass Sie, wenn Sie auf ein Feld mit dem Zugriffskontrollmodifizierer "private" zugreifen möchten, setAccessible() mit dem Wert true aufrufen müssen.
Sie können auch Methoden mit getDeclaredMethod() abrufen. Dabei müssen Sie den Namen der Methode und den Typ der Parameter als Argumente übergeben. Auch hier müssen Sie setAccessible() mit dem Wert true aufrufen, wenn Sie auf eine Methode mit dem Zugriffskontrollmodifizierer "private" zugreifen möchten. Schließlich können Sie die Methode mit invoke() aufrufen, die Sie mit der Reflection-API abgerufen haben.
Vor- und Nachteile
- Vorteile
- Reflection bietet die Flexibilität, Instanzen von Klassen zur Laufzeit zu erstellen und auf Felder und Methoden zuzugreifen, unabhängig von Zugriffskontrollmodifizierern, um die notwendigen Aufgaben auszuführen.
- Nachteile
- Verletzt die Kapselung.
- Da Instanzen zur Laufzeit erstellt werden, kann der Typ zur Kompilierzeit nicht geprüft werden.
- Da Instanzen zur Laufzeit erstellt werden, ist es schwierig, den konkreten Ablauf der Ausführung zu ermitteln.
- Der Zugriff auf Felder und Methoden mit Reflection ist im Allgemeinen langsamer als der direkte Zugriff. (Nicht in allen Fällen ist die Leistung langsamer.)
Gründe für die Verwendung
Mit der Reflection-API können Sie zur Laufzeit auf Klasseninformationen zugreifen und die Klassen nach Ihren Wünschen manipulieren. Sogar Felder und Methoden, die mit dem Zugriffskontrollmodifizierer "private" deklariert wurden, können manipuliert werden. Da dies die Kapselung verletzt, erscheint es als eine Technik, die man vermeiden sollte.
In einer kleinen Konsolenanwendung kann der Entwickler zur Kompilierzeit alle Objekte und Abhängigkeiten erkennen, die im Programm verwendet werden. In einer großen Entwicklung, wie z. B. bei einem Framework, ist es jedoch schwierig, die vielen Objekte und Abhängigkeiten zu erkennen. In diesem Fall kann Reflection verwendet werden, um Klassen dynamisch zu erstellen und Abhängigkeiten zu verknüpfen.
Nehmen Sie zum Beispiel die Bean Factory in Spring. Wenn Sie einfach die Annotationen @Controller, @Service oder @Repository anwenden, erstellt und verwaltet die Bean Factory automatisch die Klassen mit diesen Annotationen. Der Entwickler hat der Bean Factory diese Klassen nie mitgeteilt. Der Grund dafür ist die Verwendung von Reflection. Wenn zur Laufzeit eine Klasse gefunden wird, die diese Annotationen enthält, wird die Instanz dieser Klasse mit Reflection erstellt, die notwendigen Felder injiziert und in der Bean Factory gespeichert.
Natürlich sollte es aufgrund der Verletzung der Kapselung nur in wirklich notwendigen Fällen verwendet werden.