Java反射機(jī)制基礎(chǔ)詳解
1、什么是Java反射機(jī)制?
在程序運(yùn)行中動(dòng)態(tài)地獲取類的相關(guān)屬性,同時(shí)調(diào)用對(duì)象的方法和獲取屬性,這種機(jī)制被稱之為Java反射機(jī)制
下面給出一個(gè)反射的簡(jiǎn)單例子:
import lombok.Data;
@Data
public class User {
public String username;
private String password;
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
public static void reflectionSimpleExample() throws Exception{
User user = new User();
System.out.println(user.toString());
// 獲取對(duì)應(yīng)類的class
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
Object obj = cls.newInstance();
System.out.println(obj);
// 獲取成員變量,注意要是public的
Field field = cls.getField("username");
System.out.println(field.get(obj));
}
2、反射機(jī)制原理
Java反射是Java實(shí)現(xiàn)動(dòng)態(tài)語(yǔ)言的關(guān)鍵,也就是通過(guò)反射實(shí)現(xiàn)類動(dòng)態(tài)加載
靜態(tài)加載: 在編譯時(shí)加載相關(guān)的類,如果找不到類就會(huì)報(bào)錯(cuò),依賴性比較強(qiáng)動(dòng)態(tài)加載:在運(yùn)行時(shí)加載需要的類,在項(xiàng)目跑起來(lái)之后,調(diào)用才會(huì)報(bào)錯(cuò),降低了依賴性
例子:靜態(tài)加載,如下代碼,如果找不到類的情況,代碼編譯都不通過(guò)
User user = new User();
而動(dòng)態(tài)加載,就是反射的情況,是可以先編譯通過(guò)的,然后在調(diào)用代碼時(shí)候,也就是運(yùn)行時(shí)才會(huì)報(bào)錯(cuò)
Class<?> cls = Class.forName("com.example.core.example.reflection.User");
Object obj = cls.newInstance();
Exception in thread “main” java.lang.ClassNotFoundException: com.example.core.example.reflection.User
java中的反射允許程序在執(zhí)行期借助jdk中Reflection API來(lái)獲取類的內(nèi)部信息,比如成員變量、成員方法、構(gòu)造方法等等,并能操作類的屬性和方法
java中反射的實(shí)現(xiàn)和jvm和類加載機(jī)制有一定的關(guān)系,加載好類之后,在jvm的堆中會(huì)產(chǎn)生一個(gè)class類型的對(duì)象,這個(gè)class類包括了類的完整結(jié)構(gòu)信息,通過(guò)這個(gè)class對(duì)象就可以獲取到類的結(jié)構(gòu)信息,所以形象地稱之為java反射

3、Class類介紹
3.1、Class類基本介紹
然后這個(gè)Class類是什么?看下uml類圖:

Class也是類,因此也繼承Object類
Class不是直接new出來(lái)的,而是經(jīng)過(guò)系統(tǒng)創(chuàng)建的
User user = new User();
打個(gè)斷點(diǎn),debug進(jìn)行看看源碼,可以看到傳統(tǒng)的直接new一個(gè)對(duì)象也是通過(guò)類加載器loadClass拿到的

java反射方式,通用通過(guò)調(diào)試看看:
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
同樣本質(zhì)也是通過(guò)ClassLoad再通過(guò)類的全類名

3.2、Class類對(duì)象的獲取方法 Class.forname()
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
應(yīng)用場(chǎng)景:多用于讀取類全路徑,加載類
具體類.class
已經(jīng)知道具體類的情況,通過(guò)具體類的class屬性獲取
Class u = User.class;
應(yīng)用場(chǎng)景:多用于用于參數(shù)傳遞
對(duì)象.getClass
已經(jīng)創(chuàng)建好對(duì)象的情況,直接通過(guò)對(duì)象實(shí)例獲取class對(duì)象
Class cls = user.getClass();
應(yīng)用場(chǎng)景:通過(guò)創(chuàng)建好的對(duì)象,獲取Class對(duì)象
ClassLoader獲取
ClassLoader cl = user.getClass().getClassLoader();
Class cls = cl.loadClass("com.example.core.example.reflection.domain.User");
基本數(shù)據(jù)類型
Class cls = int.class;
包裝類
基本數(shù)據(jù)類型對(duì)應(yīng)的包裝類可以通過(guò).TYPE得到Class類對(duì)象
Class cls = Integer.TYPE;
3.3 、可以獲取Class對(duì)象的類型
1、外部類,成員內(nèi)部類,靜態(tài)內(nèi)部類,局部?jī)?nèi)部類,匿名內(nèi)部類
2、interface:接口
3、數(shù)組
4、enum:枚舉
5、annotation:注解
6、基本數(shù)據(jù)類型
7、void8、Class… ,等等
import com.example.core.example.reflection.domain.User;
import lombok.ToString;
import java.util.List;
public class GetClassObjectExample {
public static void main(String[] args) {
// 外部類
Class<User> cls1 = User.class;
// 接口
Class<List> cls2 = List.class;
// 數(shù)組
Class<Integer[]> cls3 = Integer[].class;
// 二維數(shù)組
Class<String[][]> cls4 = String[][].class;
// 注解
Class<lombok.ToString> cls5 = ToString.class;
// 枚舉
Class<Thread.State> cls6 = Thread.State.class;
// 基本數(shù)據(jù)類型
Class<Long> cls7 = Long.class;
// void 數(shù)據(jù)類型
Class<Void> cls8 = Void.class;
// Class
Class<Class> cls9 = Class.class;
System.out.println(cls1);
System.out.println(cls2);
System.out.println(cls3);
System.out.println(cls4);
System.out.println(cls5);
System.out.println(cls6);
System.out.println(cls7);
System.out.println(cls8);
System.out.println(cls9);
}
}
4、java反射的作用?
可以通過(guò)外部類的全路徑名創(chuàng)建對(duì)象,并使用這些類可以枚舉出類的全部成員,包括構(gòu)造函數(shù)、屬性、方法利用反射 API 訪問(wèn)類的私有成員
5、反射API主要類
1、java.lang.Class:代表一個(gè)類,表示某個(gè)類在jvm堆中的對(duì)象
2、 java.lang.reflect.Method:代表類的方法
3、 java.lang.reflect.Field:代表類的成員變量
4、 java.lang.reflect.Constructor:代表類額構(gòu)造方法
6、Java反射的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):使用Java反射可以靈活動(dòng)態(tài)地創(chuàng)建和使用對(duì)象,反射是框架的底層支撐缺點(diǎn):使用Java反射,基本就是解釋執(zhí)行的,對(duì)執(zhí)行速度是有影響的
7、反射調(diào)用的優(yōu)化方法
前面介紹了Java反射雖然很靈活,但是缺點(diǎn)就是調(diào)用時(shí)候比較慢,相對(duì)直接new對(duì)象來(lái)說(shuō),情況是怎么樣的?下面通過(guò)例子進(jìn)行試驗(yàn):
import com.example.core.example.reflection.domain.User;
import java.lang.reflect.Method;
public class TestReflectionExample {
private static final Integer TOTAL_COUNT = 1000000;
public static void main(String[] args) throws Exception{
test0();
test1();
test2();
}
public static void test0() {
long start = System.currentTimeMillis();
User user = new User();
for (int i = 0 ; i < TOTAL_COUNT; i++) {
user.hello();
}
System.out.println(String.format("傳統(tǒng)調(diào)用方法執(zhí)行時(shí)間:%d" , System.currentTimeMillis() - start));
}
public static void test1() throws Exception{
long start = System.currentTimeMillis();
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
Object obj = cls.newInstance();
Method hello = cls.getMethod("hello");
for (int i = 0 ; i < TOTAL_COUNT; i++) {
hello.invoke(obj);
}
System.out.println(String.format("反射方法調(diào)用執(zhí)行時(shí)間:%d" , System.currentTimeMillis() - start));
}
public static void test2() throws Exception{
long start = System.currentTimeMillis();
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
Object obj = cls.newInstance();
Method hello = cls.getMethod("hello");
// 關(guān)鍵,取消調(diào)用反射方法時(shí)的安全檢查
hello.setAccessible(true);
for (int i = 0 ; i < TOTAL_COUNT; i++) {
hello.invoke(obj);
}
System.out.println(String.format("優(yōu)化后的反射方法調(diào)用執(zhí)行時(shí)間:%d" , System.currentTimeMillis() - start));
}
}
傳統(tǒng)調(diào)用方法執(zhí)行時(shí)間:19
反射方法調(diào)用執(zhí)行時(shí)間:112
優(yōu)化后的反射方法調(diào)用執(zhí)行時(shí)間:50
8、反射的基本使用例子
java.lang.Class類
| 方法名 | 作用 |
|---|---|
| getName | 獲取全類名 |
| getSimpleName | 獲取簡(jiǎn)單類名 |
| getFields | 獲取所有public修飾的屬性、包括本類以及父類的 |
| getDeclaredFields | 獲取本類中所有的屬性 |
| getMethods | 獲取所有的public修飾的方法,包括本類以及父類的 |
| getDeclaredMethod | 獲取本類中所有方法 |
| getConstructors | 獲取所有public修飾的構(gòu)造器,只有本類 |
| getDeclaredConstructors | 獲取本類中所有構(gòu)造器 |
| getPackage | 以Package形式返回 包信息 |
| getSuperClass | 以Class形式返回父信息 |
| getInterfaces | 以Class[]形式返回父接口信息 |
| getAnnotations | 以Annotation[]形式返回注解信息 |
String allClassName = "com.example.core.example.reflection.domain.User"; // 通過(guò)全類名獲取class對(duì)象 Class<?> cls = Class.forName(allClassName); System.out.println(cls); // 通過(guò)對(duì)象獲取class System.out.println(cls.getClass()); // 獲取全類名 System.out.println(cls.getName()); // 獲取包名 System.out.println(cls.getClass().getPackage().getName()); // 獲取對(duì)象實(shí)例 Object obj = cls.newInstance(); System.out.println(obj);
java.lang.reflect.Field類
| 方法名 | 作用 |
|---|---|
| getModifiers | 以int形式返回修飾符,默認(rèn)修飾符是0,public是1,private是2,protected是4,static是8,final是16 |
| getType | 以Class形式返回類型 |
| getName | 返回屬性名稱 |
// 獲取類屬性
Field field = cls.getField("username");
field.set(obj , "admin");
System.out.println(field.get(obj));
// 獲取所有類屬性,private的成員變量沒(méi)有權(quán)限訪問(wèn)
Field[] fields = cls.getFields();
for (Field field1 : fields) {
System.out.println(field1.get(obj));
}
// 獲取所有類屬性包括private成員變量
Field[] allFields = cls.getDeclaredFields();
for (Field afield : allFields) {
// 開(kāi)放權(quán)限,私有的成員變量也能打印出來(lái)
afield.setAccessible(true);
System.out.println(afield.get(obj));
}
java.lang.reflect.Method類
| 方法名 | 作用 |
|---|---|
| getModifiers | 以int形式返回修飾符,默認(rèn)修飾符是0,public是1,private是2,protected是4,static是8,final是16 |
| getName | 返回方法名 |
| getReturnType | 以class形式返回類型 |
| getParmeterTypes | 以Class[] 返回參數(shù)類型數(shù)組 |
// 獲取class方法,同樣默認(rèn)情況不能獲取private的方法
Method method = cls.getMethod("hello");
System.out.println(method.invoke(obj));
java.lang.reflect.Constructor類
| 方法名 | 作用 |
|---|---|
| getModifiers | 以int形式返回修飾符,默認(rèn)修飾符是0,public是1,private是2,protected是4,static是8,final是16 |
| getName | 返回方法名 |
| getParmeterTypes | 以Class[] 返回參數(shù)類型數(shù)組 |
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
Constructor<?> con = cls.getDeclaredConstructor();
con.setAccessible(true);
Object obj = con.newInstance();
System.out.println(obj);
9、反射開(kāi)放權(quán)限操作
在我們使用Java反射獲取class的private成員變量或者方法時(shí),這種情況是不允許獲取的,不過(guò)可以通過(guò)開(kāi)放權(quán)限的方式來(lái)處理,通過(guò)設(shè)置setAccessible(true);即可
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* <pre>
* 開(kāi)放java反射權(quán)限,允許調(diào)用類的private屬性或者方法
* </pre>
* <p>
* <pre>
* @author mazq
* 修改記錄
* 修改后版本: 修改人: 修改日期: 2021/08/09 19:10 修改內(nèi)容:
* </pre>
*/
public class AccessibleReflectionExample {
public static void main(String[] args) throws Exception{
test();
}
public static void test() throws Exception{
// 創(chuàng)建class實(shí)例對(duì)象
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
Constructor<?> con = cls.getDeclaredConstructor();
con.setAccessible(true);
Object obj = con.newInstance();
// 獲取私有成員變量
Field pwd = cls.getDeclaredField("password");
// 開(kāi)放私有變量的訪問(wèn)權(quán)限
pwd.setAccessible(true);
pwd.set(obj , "123456");
// 私有方法調(diào)用
Method method = cls.getDeclaredMethod("priToString");
method.setAccessible(true);
System.out.println(method.invoke(obj));
}
}
總結(jié)
本篇文章就到這里了,希望能給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
利用java+mysql遞歸實(shí)現(xiàn)拼接樹(shù)形JSON列表的方法示例
這篇文章主要給大家介紹了關(guān)于利用java+mysql遞歸實(shí)現(xiàn)拼接樹(shù)形JSON列表的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來(lái)一起看看吧。2017-08-08
SpringBoot2如何集成Elasticsearch6.x(TransportClient方式)
這篇文章主要介紹了SpringBoot2如何集成Elasticsearch6.x(TransportClient方式)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
使用@SpringBootTest注解進(jìn)行單元測(cè)試
這篇文章主要介紹了使用@SpringBootTest注解進(jìn)行單元測(cè)試,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
springboot接收前端參數(shù)的四種方式圖文詳解
Spring Boot可以通過(guò)多種方式接收前端傳遞的數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于springboot接收前端參數(shù)的四種方式,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-11-11
java:程序包javafx.geometry不存在問(wèn)題及解決
這篇文章主要介紹了java:程序包javafx.geometry不存在問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
SpringBoot通過(guò)自定義注解實(shí)現(xiàn)參數(shù)校驗(yàn)
實(shí)現(xiàn)參數(shù)校驗(yàn)說(shuō)實(shí)話方式還挺多,個(gè)人使用過(guò)直接在Controller代碼里面寫(xiě)、AOP+自定義注解、ConstraintValidator。本文主要和大家講的是ConstraintValidator實(shí)現(xiàn),感興趣的可以了解一下2022-12-12
springboot項(xiàng)目數(shù)據(jù)庫(kù)密碼如何加密
在我們?nèi)粘i_(kāi)發(fā)中,我們可能很隨意把數(shù)據(jù)庫(kù)密碼直接明文暴露在配置文件中,今天就來(lái)聊聊在springboot項(xiàng)目中如何對(duì)數(shù)據(jù)庫(kù)密碼進(jìn)行加密,感興趣的可以了解一下2021-07-07

