一文搞懂Java中的反射機制
一. 反射的概念
Java的反射機制是在運行狀態(tài)中,對于任何一個類,都可以知道這個類的所有屬性和方法,對于任何一個對象,都可以調(diào)用它所有的方法和屬性,修改部分類型信息,這種動態(tài)獲取信息以及動態(tài)調(diào)用對象方法的功能稱為Java的反射機制
二. 為什么需要反射
在日常的第三方開發(fā)中,經(jīng)常遇到某個類的方法或?qū)傩允撬接械模@時候就可以利用反射機制來獲取所需要的私有方法或?qū)傩?/p>
我們在進行Java程序開發(fā)時,為了開發(fā)效率,一般會選擇IDE開發(fā)環(huán)境,IDE開發(fā)環(huán)境有一個強大的功能就是自動提示功能,IDE是如何知道對象中有哪些屬性和方法呢?
反射最重要的用途就是開發(fā)各種通用框架,比如在spring中,我們將所有的類Bean交給spring容器管理,無論是XML配置Bean還是注解配置,當我們從容器中獲取Bean來依賴注入時,容器會讀取配置,而配置中給的就是類的信息,spring根據(jù)這些信息,需要創(chuàng)建那些Bean,spring就動態(tài)的創(chuàng)建這些類
三. 反射的基石
反射的基石是字節(jié)碼文件對象
Java的源文件是不能直接進行運行的,需要先進行編譯為.class的字節(jié)碼文件,然后使用雙親委派模型被類加載器加載到虛擬機中形成字節(jié)碼文件對象,才可以在JVM中運行
何時才能觸發(fā)類的加載呢?只要需要用類就會觸發(fā)類的加載,比如:
- new一個對象的時候
- 訪問一個靜態(tài)成員的時候
- 訪問一個靜態(tài)方法的時候
- 創(chuàng)建一個子類對象的時候
- java命令執(zhí)行一個字節(jié)碼文件的時候
- 通過反射機制創(chuàng)建一個字節(jié)碼文件對象的時候
在Java中,一切皆對象,當字節(jié)碼文件加載到JVM中,會形成一個Class類對象,即該類在jvm中變成了一個對象
字節(jié)碼文件對象包含了三部分內(nèi)容:
構(gòu)造方法---Constructor對象
成員方法---Method對象
成員變量---Filed對象
四. 反射的實現(xiàn)
反射的第一步就是先獲取Class類對象,也就是字節(jié)碼文件對象,然后通過Class對象的核心方法達到反射的目的
1. 獲取字節(jié)碼文件對象
獲取Class對象有三種方式:
- 使用Class.forName("類的全路徑名"),可能會拋出ClassNotFoundException異常
- 使用類名.class,需要在編譯期間就明確要操作的類
- 使用對象.getClass()方法,需要先將對象創(chuàng)建出類
先創(chuàng)建一個Student類,將它的屬性,方法都設(shè)置為私有的
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
private String getName() {
return name;
}
private void setName(String name) {
this.name = name;
}
private int getAge() {
return age;
}
private void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
這時候,Student類的全路徑名為:反射枚舉lambda.Student

下面是獲取字節(jié)碼對象三種方式的代碼展示:
public class TestReflect {
public static void main(String[] args) {
//獲取字節(jié)碼文件對象
//1.使用Class.forName("類的全路徑")
try {
Class<?> stuClass1 = Class.forName("反射枚舉lambda.Student");
System.out.println(stuClass1);
//2.使用類.class
Class<?> stuClass2 = Student.class;
System.out.println(stuClass2);
System.out.println(stuClass1==stuClass2); //true,字節(jié)碼文件只有一份,故是同一個對象
//3.使用對象.getClass()
//該方法需要先創(chuàng)建對象,故先將Student類的構(gòu)造方法改為公有的再進行下述操作
Student student = new Student("張三",26);
Class<?> stuClass3 = student.getClass();
System.out.println(stuClass3);
System.out.println(stuClass2==stuClass3); //true,字節(jié)碼文件只有一份,故是同一個對象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
打印結(jié)果:字節(jié)碼文件只有一份,所以不同方式獲得的是同一個對象

2. 反射的使用
2.1 反射構(gòu)造方法創(chuàng)建實例
與反射相關(guān)的包都在import java.lang.reflect包下面
| 方法 | 說明 |
| Constructor[] getConstructors() | 獲取類中所有公有的構(gòu)造器對象 |
| Constructor<T> getConstructors(Class...<T> paramTypes) | 獲取參數(shù)匹配的共有的構(gòu)造器對象 |
| Constructor[] getDeclaredConstructors() | 獲取類中所有的構(gòu)造器對象,包括私有的 |
| Constructor<T> getDeclaredConstructors(Class...<T> paramTypes) | 獲取類中參數(shù)匹配的構(gòu)造器對象,包括私有的 |
具體步驟:
- 獲取字節(jié)碼文件對象
- 使用字節(jié)碼對象獲取構(gòu)造方法
- 設(shè)置構(gòu)造方法權(quán)限
- 使用構(gòu)造方法創(chuàng)建實例對象
代碼示例:
public static void main(String[] args) {
try {
//1.獲取字節(jié)碼對象
Class<?> stuClass = Class.forName("反射枚舉lambda.Student");
//2.獲取構(gòu)造方法
Constructor<?> stuConstructor = stuClass.getDeclaredConstructor(String.class,int.class); //參數(shù)也是class類型
//3.修改方法的訪問權(quán)限
stuConstructor.setAccessible(true);
//4.調(diào)用該方法
Object object = stuConstructor.newInstance("李四",23); //newInstance()創(chuàng)建類的實例,為Object類型
Student s = (Student) object;
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}
}
打印結(jié)果:

2.2 反射屬性
| 方法 | 說明 |
| getFields() | 獲取所有公有的屬性對象 |
| getField(String name) | 獲取某個公有的屬性對象 |
| getDeclaredFields() | 獲取所有的屬性對象,包括私有屬性 |
| getDeclaredField(String name) | 獲取某個屬性對象,包括私有屬性 |
具體步驟:
- 獲取字節(jié)碼對象
- 使用字節(jié)碼對象獲取屬性
- 設(shè)置屬性權(quán)限
- 調(diào)用方法設(shè)置屬性值
代碼示例:
//反射屬性
Field sutAge = stuClass.getDeclaredField("age"); //參數(shù)為屬性
sutAge.setAccessible(true);
sutAge.setInt(s,18); //設(shè)置屬性值為int,第一個參數(shù)為哪個對象,第二個參數(shù)為設(shè)置值
System.out.println(s);
打印結(jié)果:將對象s的age設(shè)置為18

2.3 反射方法
| 方法 | 說明 |
| getMethods() | 獲取該類所有的公有的方法 |
| getMethod(String name,Class...<?> parameterTypes) | 獲取該類某個公有的方法 |
| getDeclaredMethods() | 獲取該類所有方法,包括私有 |
| getDeclaredMethod(String name,Class...<?> parameterTypes) | 獲取該類某個方法,包括私有 |
具體步驟:
- 獲取字節(jié)碼對象
- 使用字節(jié)碼對象獲取方法
- 設(shè)置方法權(quán)限
- 使用方法.invoke調(diào)用,第一個參數(shù)為哪個對象,后面參數(shù)為方法參數(shù)的具體值
代碼示例:
//反射方法
Method setNameMethod = stuClass.getDeclaredMethod("setName", String.class); //第一個參數(shù)為方法名,后面參數(shù)為方法參數(shù)
setNameMethod.setAccessible(true);
setNameMethod.invoke(s,"王五");
System.out.println(s);
打印結(jié)果:將對象s的姓名改為王五

反射的優(yōu)缺點
優(yōu)點:
對于任意一個類,可以獲取該類的所有屬性和方法,對于一個對象,能調(diào)用它任意一個方法
增加程序的靈活性和擴展性,降低耦合性,提高自適應(yīng)能力
反射已經(jīng)應(yīng)用在很多框架中,如:Spring,Struts,Hibernate
缺點:
破壞了類的封裝性
使用反射導(dǎo)致程序效率低
反射代碼比較復(fù)雜,因而會帶來維護問題
以上就是一文搞懂Java中的反射機制的詳細內(nèi)容,更多關(guān)于Java反射機制的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java JDK動態(tài)代理實現(xiàn)原理實例解析
這篇文章主要介紹了Java JDK動態(tài)代理實現(xiàn)原理實例解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友可以參考下2020-06-06
登陸驗證碼kaptcha結(jié)合spring boot的用法詳解
在一個web應(yīng)用中驗證碼是一個常見的元素。不管是防止機器人還是爬蟲都有一定的作用,下面這篇文章主要給大家介紹了登陸驗證碼kaptcha結(jié)合spring boot用法的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-06-06
spring boot activiti工作流的搭建與簡單使用
這篇文章主要給大家介紹了關(guān)于spring boot activiti工作流的搭建與簡單使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧2018-08-08
java設(shè)計模式之策略模式在促銷活動場景中的使用案例
這篇文章主要為大家介紹了java設(shè)計模式之策略模式在促銷活動場景中案例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-05-05
Java Iterator迭代器_動力節(jié)點Java學(xué)院整理
迭代器是一種模式,它可以使得對于序列類型的數(shù)據(jù)結(jié)構(gòu)的遍歷行為與被遍歷的對象分離,接下來通過本文給大家分享Java Iterator迭代器_動力節(jié)點Java學(xué)院整理,需要的朋友參考下吧2017-05-05
Springboot?多級緩存設(shè)計與實現(xiàn)方案
多級緩存是提升高并發(fā)系統(tǒng)性能的關(guān)鍵策略之一,它不僅能夠減少系統(tǒng)的響應(yīng)時間,提高用戶體驗,還能有效降低后端系統(tǒng)的負載,防止系統(tǒng)過載,這篇文章主要介紹了Springboot?多級緩存設(shè)計與實現(xiàn),需要的朋友可以參考下2024-02-02

