![translation](https://cdn.durumis.com/common/trans.png)
Ez egy AI által fordított bejegyzés.
[Java] Reflection fogalma és használata
- Írás nyelve: Koreai
- •
-
Referencia ország: Minden ország
- •
- Informatika
Válasszon nyelvet
A durumis AI által összefoglalt szöveg
- A Reflection egy API, amely a futásidő alatt lehetővé teszi a osztályok információinak elérését és a osztályok manipulálását.
- A reflection segítségével a osztályok példányait létrehozhatjuk, és elérhetjük a mezőket és a metódusokat a hozzáférési módosítók figyelmen kívül hagyásával. Különösen hasznos a keretrendszerekhez hasonló nagy léptékű fejlesztési fázisokban, ahol dinamikusan kell kezelni a függőségi viszonyokat.
- A kapszulázás megsértését és a teljesítmény csökkenését okozhatja, ezért célszerű csak akkor használni, ha feltétlenül szükséges.
Mi az a reflexió?
A reflexió egy olyan API, amely lehetővé teszi, hogy a heap memóriában betöltött Class típusú objektumok segítségével létrehozzunk egy kívánt osztály példányát, és hozzáférjünk a példány mezőihez és metódusaihoz, függetlenül a hozzáférési módosítókól.
A betöltött osztály azt jelenti, hogy a JVM osztálybetöltője befejezte az osztályfájl betöltését, majd létrehoz egyClass típusú objektumot, amely tartalmazza az osztály információit, és elmenti a heap memóriába. Fontos megjegyezni, hogy ez különbözik a new kulcsszóval létrehozott objektumoktól. Ha nem érti a Class típusú objektumokat, nézze meg a java.lang.class objektumok JDK dokumentációját.
Használati mód
A reflexió használata előtt meg kell szereznünk a heap memóriában betöltött Class típusú objektumot. Ehhez három módszer létezik.
- A class.class segítségével
- Az instance.getClass() segítségével
- A Class.forName("osztálynév") segítségével
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("A jelszó 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("Jeyon", 23, "Dará쓰 Development");
Class extends Member> memberClass2 = member.getClass();
System.out.println(System.identityHashCode(memberClass2));
Class> memberClass3 = Class.forName("{csomagnév}.Member");
System.out.println(System.identityHashCode(memberClass3));
}
}
// Futtatási eredmény
1740000325
1740000325
Láthatjuk, hogy a Class típusú objektumok mindegyike ugyanaz, függetlenül attól, hogy melyik módszert használjuk. A hash-értékük megegyezik, ezért a helyzetnek megfelelően szabadon választhatunk.
Mostantól a megszerzett Class típusú objektum segítségével létrehozhatunk példányokat az adott osztályból, és hozzáférhetünk a példány mezőihez és metódusaihoz, függetlenül a hozzáférési módosítóktól. Először hozzunk létre egy példányt az adott osztályból.
public class Main {
public static void main(String[] args) throws Exception {
// A Member összes konstruktorának kinyomtatása
Member member = new Member();
Class extends Member> memberClass = member.getClass();
Arrays.stream(memberClass.getConstructors()).forEach(System.out::println);
// Példány létrehozása a Member alapértelmezett konstruktorával
Constructor extends Member> constructor = memberClass.getConstructor();
Member member2 = constructor.newInstance();
System.out.println("member2 = " + member2);
// Példány létrehozása a Member másik konstruktorával
Constructor extends Member> fullConstructor =
memberClass.getConstructor(String.class, int.class, String.class);
Member member3 = fullConstructor.newInstance("Jeyon", 23, "Dará쓰 Development");
System.out.println("member3 = " + member3);
}
}
// Futtatási eredmény
public Member()
public Member(java.lang.String,int,java.lang.String)
member2 = Member{name='null', age=0, hobby='null'}
A getConstructor() segítségével lekérhetjük a konstruktort, a newInstance() segítségével pedig dinamikusan létrehozhatunk egy Member példányt.
Végül hozzáférjünk a példány mezőihez és metódusaihoz, függetlenül a hozzáférési módosítóktól.
public class Main {
public static void main(String[] args) throws Exception {
Member member = new Member("Jeyon", 23, "Dará쓰 Development");
Class extends Member> memberClass = member.getClass();
// Mező hozzáférése
Field[] fields = memberClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.get(member));
}
fields[0].set(member, "Jeyon2");
System.out.println(member);
// Metódus hozzáférése
Method speakMethod = memberClass.getDeclaredMethod("speak", String.class);
speakMethod.invoke(member, "Reflexió teszt");
Method secretMethod = memberClass.getDeclaredMethod("secret");
secretMethod.setAccessible(true);
secretMethod.invoke(member);
}
}
// Futtatási eredmény
Jeyon
23
Dará쓰 Development
Member{name='Jeyon2', age=23, hobby='Dará쓰 Development'}
Reflexió teszt
A getDeclaredFileds() segítségével lekérhetjük az osztály összes példányváltozóját, a get() segítségével lekérhetjük a mező értékét, a set() segítségével pedig módosíthatjuk a mező értékét. Fontos megjegyezni, hogy a private hozzáférési módosítóval rendelkező mezőkhöz való hozzáféréskor a setAccessible() metódus paramétereként true-t kell átadnunk.
A getDeclaredMethod() segítségével lekérhetjük a metódusokat is. Ekkor meg kell adnunk a metódus nevét és a paraméterek típusát is. Hasonlóképpen, ha private hozzáférési módosítóval rendelkező metódushoz akarunk hozzáférni, a setAccessible() metódus paramétereként true-t kell beállítanunk. Végül az invoke() metódus segítségével meghívhatjuk a reflexió API-val megszerzett metódusokat.
Előnyök és hátrányok
- Előnyök
- Rugalmasságot biztosít a futtatás idején az osztályok példányainak létrehozásához, valamint a mezőkhöz és metódusokhoz való hozzáféréshez, függetlenül a hozzáférési módosítóktól.
- Hátrányok
- Sérti a kapszulázást.
- A futtatás idején történik a példányok létrehozása, így a fordítási időben nem ellenőrizhetők a típusok.
- A futtatás idején történik a példányok létrehozása, így nehéz megérteni a konkrét működési folyamatot.
- A reflexió használata lassabb, mint a közvetlen mezőkhöz és metódusokhoz való hozzáférés. (Nem minden esetben lassabb.)
Miért használjuk?
A reflexió API lehetővé teszi, hogy futásidőben hozzáférjünk az osztályok adataihoz, és a kívánt módon manipuláljuk az osztályokat. Még a private hozzáférési módosítóval deklarált mezőket és metódusokat is manipulálhatjuk. Ez olyannak tűnik, mint egy olyan technika, amelyet nem szabadna használni, mivel sérti az objektumorientált tervezés egyik alapelvét, a kapszulázást.
Kis méretű konzolos alkalmazásokban a fejlesztő a fordítási időben pontosan tudja, hogy mely objektumokat és függőségeket fogja használni a program. De a keretekhez hasonló nagy léptékű fejlesztések során rengeteg objektum és függőség van, amelyeket nehéz nyomon követni. Ebben az esetben a reflexió segítségével dinamikusan létrehozhatunk osztályokat és építhetünk fel függőségi viszonyokat.
Például a Spring Bean Factory-ban, ha egy osztályhoz hozzáadjuk a @Controller, @Service, @Repository stb. annotációkat, a Bean Factory automatikusan létrehozza és kezeli az adott annotációval ellátott osztályokat. A fejlesztő nem adta meg az osztályokat a Bean Factory-nak, de ez azért lehetséges, mert a reflexiót használják. Futásidőben megkeresi az annotált osztályokat, és ha talál egyet, a reflexió segítségével létrehozza az osztály példányát, befecskendezi a szükséges mezőket, és elmenti a Bean Factory-ba.
Természetesen, ahogy azt korábban is említettük, a kapszulázás megsértése miatt csak akkor szabad használni, ha feltétlenül szükséges.