一篇文章帶你搞定JAVA反射
1、反射的概念
1、概念
反射,指在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法,對(duì)任意一個(gè)對(duì)象,都能調(diào)用它的任意一個(gè)方法。這種動(dòng)態(tài)獲取信息,以及動(dòng)態(tài)調(diào)用對(duì)象方法的功能,叫做java語(yǔ)言的反射機(jī)制。反射很強(qiáng)大,有優(yōu)點(diǎn)也有缺點(diǎn)。
優(yōu)點(diǎn):靈活性高。因?yàn)榉瓷鋵儆趧?dòng)態(tài)編譯,即只有到運(yùn)行時(shí)才動(dòng)態(tài)創(chuàng)建 &獲取對(duì)象實(shí)例。
缺點(diǎn):執(zhí)行效率低。
2、獲取字節(jié)碼文件對(duì)象的方式
2.1 元數(shù)據(jù)的概念
元數(shù)據(jù)(metadata):元數(shù)據(jù)是指用來(lái)描述類的數(shù)據(jù),就是class的代碼數(shù)據(jù)。所有的class文件加載到虛擬機(jī)之后都會(huì)被構(gòu)建成class對(duì)象,class對(duì)象描述了一個(gè)類都有哪些東西,大家都知道的實(shí)現(xiàn)的接口,繼承的抽象類,成員變量,類變量,成員方法,類方法,靜態(tài)方法等,這個(gè)class對(duì)象就是元數(shù)據(jù)。
- Class類:代表一個(gè)類。
- Field類:代表類的成員變量(成員變量也稱為類的屬性)。
- Method類:代表類的方法。
- Constructor類:代表類的構(gòu)造方法。

2.2 獲取class對(duì)象的方式
- 通過(guò)對(duì)象獲得,因?yàn)槿魏螌?duì)象都必須和class對(duì)象關(guān)聯(lián)
- 通過(guò)類對(duì)象直接獲得
- 通過(guò)類加載器獲得,因?yàn)轭惣虞d器讀取class文件會(huì)返回class對(duì)象
即將用來(lái)反射的對(duì)象(隨便定義的一個(gè)對(duì)象,只是為了演示)
package org.pdool.reflect;
/**
* @author 香菜
*/
public class Npc {
// 靜態(tài)field
public static int NPC_TYPE_1 = 1;
// 私有成員變量
private int npcType;
// 共有成員變量
public String name;
// 無(wú)參構(gòu)造函數(shù)
public Npc() {
}
// 有參構(gòu)造函數(shù)
public Npc(int npcType, String name) {
this.npcType = npcType;
this.name = name;
}
public int getNpcType() {
return npcType;
}
public void setNpcType(int npcType) {
this.npcType = npcType;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 靜態(tài)方法
public static void sayHello(String word){
System.out.println("hello " + word);
}
}
獲取反射class的三種方式
package org.pdool.reflect;
/**
* @author 香菜
*/
public class ClazzTest {
public static void main(String[] args) {
//第一種方式獲取Class對(duì)象
Npc npc1 = new Npc();//這一new 產(chǎn)生一個(gè)Npc對(duì)象,一個(gè)Class對(duì)象。
Class npcClazz1 = npc1.getClass();//獲取Class對(duì)象
System.out.println(npcClazz1.getName());
//第二種方式獲取Class對(duì)象
Class npcClazz2 = Npc.class;
System.out.println(npcClazz1 == npcClazz2);//判斷第一種方式獲取的Class對(duì)象和第二種方式獲取的是否是同一個(gè)
//第三種方式獲取Class對(duì)象
try {
Class npcClazz3 = Class.forName("org.pdool.reflect.Npc");//注意此字符串必須是真實(shí)路徑,就是帶包名的類路徑,包名.類名
System.out.println(npcClazz3 == npcClazz2);//判斷三種方式是否獲取的是同一個(gè)Class對(duì)象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
1、訪問(wèn)權(quán)限
反射機(jī)制的默認(rèn)行為受限于Java的訪問(wèn)控制,可通過(guò) setAccessible 繞過(guò)控制。
// 設(shè)置對(duì)象數(shù)組可訪問(wèn)標(biāo)志 static void setAccessible(AccessibleObject[] array, boolean flag)
2、獲取方法

2.1 訪問(wèn)靜態(tài)方法
public static void main(String[] args) throws NoSuchMethodException,InvocationTargetException, IllegalAccessException {
Npc npc = new Npc(1, "妖神·凰女");
Class npcClazz = Npc.class;
// 第一個(gè)參數(shù)是方法名,第二個(gè)參數(shù)是函數(shù)的參數(shù)class對(duì)象,因?yàn)榇嬖谥剌d的可能性,用參數(shù)類型區(qū)分
Method sayHello = npcClazz.getMethod("sayHello", String.class);
sayHello.invoke(npc, "world");
}
2.2 訪問(wèn)類方法
Npc npc = new Npc(1, "妖神·凰女");
System.out.println(npc.getName());
Class npcClazz = Npc.class;
// 第一個(gè)參數(shù)是方法名,第二個(gè)參數(shù)是函數(shù)的參數(shù)class對(duì)象,因?yàn)榇嬖谥剌d的可能性,用參數(shù)類型區(qū)分
Method sayHello = npcClazz.getMethod("setName", String.class);
sayHello.invoke(npc, "world");
System.out.println(npc.getName());
3、獲取字段,讀取字段的值

Npc npc = new Npc(1, "妖神·凰女");
Class npcClazz = Npc.class;
// 獲取字段,并設(shè)置可訪問(wèn)
Field field = npcClazz.getField("name");
field.setAccessible(true);
System.out.println( field.get(npc));
4、獲取實(shí)現(xiàn)的接口

5、獲取構(gòu)造函數(shù),創(chuàng)建實(shí)例

Class npcClazz = Npc.class;
Constructor declaredConstructor = npcClazz.getDeclaredConstructor(int.class,String.class);
Npc npc = (Npc) declaredConstructor.newInstance(1, "妖神");
System.out.println(npc.getName());
6、獲取繼承的父類
Class npcClazz = Npc.class; Class superclass = npcClazz.getSuperclass(); System.out.println(superclass.getName());
7、獲取注解
Class npcClazz = Npc.class;
Annotation[] annotations = npcClazz.getAnnotations();
// 運(yùn)行時(shí)注解
for (Annotation annotation : annotations) {
System.out.println(annotation.getClass().getName());
}
4、反射實(shí)例
獲取到元數(shù)據(jù)不是最終的目的,我們最終的目的是想在運(yùn)行時(shí)去調(diào)用,訪問(wèn)類。說(shuō)了太多,還是舉個(gè)例子,大家都知道Spring的IOC,怎么實(shí)現(xiàn)的吶?
過(guò)程:
1、Spring 在項(xiàng)目啟動(dòng)的時(shí)間通過(guò)讀取xml中配置的bean的路徑,
2、然后通過(guò)Class.forName 讀取class 到類加載器,
3、然后通過(guò)反射創(chuàng)建所有的bean實(shí)例并保存到容器中,啟動(dòng)容器之后,
4、在項(xiàng)目中可以直接獲取bean對(duì)象。
我們來(lái)大概實(shí)現(xiàn)這一過(guò)程,因?yàn)閤ml的讀取比較麻煩,直接用property來(lái)代替了。大家體會(huì)一下思想就可以了。
package org.pdool.reflect;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* @author 香菜
*/
public class ClazzTest {
public static void main(String[] args){
try {
Map<String,Object> container = new HashMap<>();
//1.讀取配置
InputStream in = ClazzTest.class.getResourceAsStream("/beans.properties");
Properties property = new Properties();
property.load(in);
//2.反射創(chuàng)建對(duì)象
Set<Object> keySet = property.keySet();
for (Object key : keySet) {
// 2.1 獲取類的全路徑
String classStr = (String) property.get(key);
// 2.2 加載class 到虛擬機(jī)
Class<?> beanClazz = Class.forName(classStr);
// 2.3 獲取缺省的構(gòu)造函數(shù)
Constructor<?> declaredConstructor = beanClazz.getDeclaredConstructor();
// 2.4 創(chuàng)建實(shí)例
Object o = declaredConstructor.newInstance();
container.put((String) key,o);
}
// 3.獲取實(shí)例
Npc npc = (Npc) container.get("npc");
System.out.println(npc == null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
5、總結(jié)
在使用Java反射機(jī)制時(shí),主要步驟包括:
獲取 目標(biāo)類型的Class對(duì)象
通過(guò) Class 對(duì)象分別獲取Constructor類對(duì)象、Method類對(duì)象 或者 Field 類對(duì)象
通過(guò) Constructor類對(duì)象、Method類對(duì)象 & Field類對(duì)象分別獲取類的構(gòu)造函數(shù)、方法&屬性的具體信息,并進(jìn)行后續(xù)操作。
本篇文章就到這里了,希望能給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
通過(guò)JDBC連接oracle數(shù)據(jù)庫(kù)的十大技巧
通過(guò)JDBC連接oracle數(shù)據(jù)庫(kù)的十大技巧...2006-12-12
jpa實(shí)現(xiàn)多對(duì)多的屬性時(shí)查詢的兩種方法
這篇文章主要介紹了jpa實(shí)現(xiàn)多對(duì)多的屬性時(shí)查詢的兩種方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
簡(jiǎn)單了解Spring Cloud搭建Config過(guò)程實(shí)例
這篇文章主要介紹了簡(jiǎn)單了解Spring Cloud搭建Config過(guò)程實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
解決在Idea 2020.2下使用 Lombok的注解不生效的問(wèn)題(插件安裝了,依賴也寫(xiě)了,自動(dòng)注解也設(shè)置了)
這篇文章主要介紹了在Idea 2020.2下使用 Lombok的注解不生效的問(wèn)題(插件安裝了,依賴也寫(xiě)了,自動(dòng)注解也設(shè)置了),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
SpringCloud?Gateway之請(qǐng)求應(yīng)答日志打印方式
這篇文章主要介紹了SpringCloud?Gateway之請(qǐng)求應(yīng)答日志打印方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Mybatis使用注解實(shí)現(xiàn)復(fù)雜動(dòng)態(tài)SQL的方法詳解
當(dāng)使用 MyBatis 注解方式執(zhí)行復(fù)雜 SQL 時(shí),你可以使用 @Select、@Update、@Insert、@Delete 注解直接在接口方法上編寫(xiě) SQL,本文給大家介紹了Mybatis如何使用注解實(shí)現(xiàn)復(fù)雜動(dòng)態(tài)SQL,文中有相關(guān)的代碼示例供大家參考,需要的朋友可以參考下2023-12-12
SpringBoot集成WebSocket【基于純H5】進(jìn)行點(diǎn)對(duì)點(diǎn)[一對(duì)一]和廣播[一對(duì)多]實(shí)時(shí)推送
這篇文章主要介紹了SpringBoot集成WebSocket【基于純H5】進(jìn)行點(diǎn)對(duì)點(diǎn)[一對(duì)一]和廣播[一對(duì)多]實(shí)時(shí)推送,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08
springboot 如何重定向redirect 并隱藏參數(shù)
這篇文章主要介紹了springboot 如何重定向redirect 并隱藏參數(shù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09

