一文搞懂Java中的反射機(jī)制
一. 反射的概念
Java的反射機(jī)制是在運(yùn)行狀態(tài)中,對(duì)于任何一個(gè)類(lèi),都可以知道這個(gè)類(lèi)的所有屬性和方法,對(duì)于任何一個(gè)對(duì)象,都可以調(diào)用它所有的方法和屬性,修改部分類(lèi)型信息,這種動(dòng)態(tài)獲取信息以及動(dòng)態(tài)調(diào)用對(duì)象方法的功能稱(chēng)為Java的反射機(jī)制
二. 為什么需要反射
在日常的第三方開(kāi)發(fā)中,經(jīng)常遇到某個(gè)類(lèi)的方法或?qū)傩允撬接械?,這時(shí)候就可以利用反射機(jī)制來(lái)獲取所需要的私有方法或?qū)傩?/p>
我們?cè)谶M(jìn)行Java程序開(kāi)發(fā)時(shí),為了開(kāi)發(fā)效率,一般會(huì)選擇IDE開(kāi)發(fā)環(huán)境,IDE開(kāi)發(fā)環(huán)境有一個(gè)強(qiáng)大的功能就是自動(dòng)提示功能,IDE是如何知道對(duì)象中有哪些屬性和方法呢?
反射最重要的用途就是開(kāi)發(fā)各種通用框架,比如在spring中,我們將所有的類(lèi)Bean交給spring容器管理,無(wú)論是XML配置Bean還是注解配置,當(dāng)我們從容器中獲取Bean來(lái)依賴(lài)注入時(shí),容器會(huì)讀取配置,而配置中給的就是類(lèi)的信息,spring根據(jù)這些信息,需要?jiǎng)?chuàng)建那些Bean,spring就動(dòng)態(tài)的創(chuàng)建這些類(lèi)
三. 反射的基石
反射的基石是字節(jié)碼文件對(duì)象
Java的源文件是不能直接進(jìn)行運(yùn)行的,需要先進(jìn)行編譯為.class的字節(jié)碼文件,然后使用雙親委派模型被類(lèi)加載器加載到虛擬機(jī)中形成字節(jié)碼文件對(duì)象,才可以在JVM中運(yùn)行
何時(shí)才能觸發(fā)類(lèi)的加載呢?只要需要用類(lèi)就會(huì)觸發(fā)類(lèi)的加載,比如:
- new一個(gè)對(duì)象的時(shí)候
- 訪問(wèn)一個(gè)靜態(tài)成員的時(shí)候
- 訪問(wèn)一個(gè)靜態(tài)方法的時(shí)候
- 創(chuàng)建一個(gè)子類(lèi)對(duì)象的時(shí)候
- java命令執(zhí)行一個(gè)字節(jié)碼文件的時(shí)候
- 通過(guò)反射機(jī)制創(chuàng)建一個(gè)字節(jié)碼文件對(duì)象的時(shí)候
在Java中,一切皆對(duì)象,當(dāng)字節(jié)碼文件加載到JVM中,會(huì)形成一個(gè)Class類(lèi)對(duì)象,即該類(lèi)在jvm中變成了一個(gè)對(duì)象
字節(jié)碼文件對(duì)象包含了三部分內(nèi)容:
構(gòu)造方法---Constructor對(duì)象
成員方法---Method對(duì)象
成員變量---Filed對(duì)象
四. 反射的實(shí)現(xiàn)
反射的第一步就是先獲取Class類(lèi)對(duì)象,也就是字節(jié)碼文件對(duì)象,然后通過(guò)Class對(duì)象的核心方法達(dá)到反射的目的
1. 獲取字節(jié)碼文件對(duì)象
獲取Class對(duì)象有三種方式:
- 使用Class.forName("類(lèi)的全路徑名"),可能會(huì)拋出ClassNotFoundException異常
- 使用類(lèi)名.class,需要在編譯期間就明確要操作的類(lèi)
- 使用對(duì)象.getClass()方法,需要先將對(duì)象創(chuàng)建出類(lèi)
先創(chuàng)建一個(gè)Student類(lèi),將它的屬性,方法都設(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 + '}'; } }
這時(shí)候,Student類(lèi)的全路徑名為:反射枚舉lambda.Student
下面是獲取字節(jié)碼對(duì)象三種方式的代碼展示:
public class TestReflect { public static void main(String[] args) { //獲取字節(jié)碼文件對(duì)象 //1.使用Class.forName("類(lèi)的全路徑") try { Class<?> stuClass1 = Class.forName("反射枚舉lambda.Student"); System.out.println(stuClass1); //2.使用類(lèi).class Class<?> stuClass2 = Student.class; System.out.println(stuClass2); System.out.println(stuClass1==stuClass2); //true,字節(jié)碼文件只有一份,故是同一個(gè)對(duì)象 //3.使用對(duì)象.getClass() //該方法需要先創(chuàng)建對(duì)象,故先將Student類(lèi)的構(gòu)造方法改為公有的再進(jìn)行下述操作 Student student = new Student("張三",26); Class<?> stuClass3 = student.getClass(); System.out.println(stuClass3); System.out.println(stuClass2==stuClass3); //true,字節(jié)碼文件只有一份,故是同一個(gè)對(duì)象 } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
打印結(jié)果:字節(jié)碼文件只有一份,所以不同方式獲得的是同一個(gè)對(duì)象
2. 反射的使用
2.1 反射構(gòu)造方法創(chuàng)建實(shí)例
與反射相關(guān)的包都在import java.lang.reflect包下面
方法 | 說(shuō)明 |
Constructor[] getConstructors() | 獲取類(lèi)中所有公有的構(gòu)造器對(duì)象 |
Constructor<T> getConstructors(Class...<T> paramTypes) | 獲取參數(shù)匹配的共有的構(gòu)造器對(duì)象 |
Constructor[] getDeclaredConstructors() | 獲取類(lèi)中所有的構(gòu)造器對(duì)象,包括私有的 |
Constructor<T> getDeclaredConstructors(Class...<T> paramTypes) | 獲取類(lèi)中參數(shù)匹配的構(gòu)造器對(duì)象,包括私有的 |
具體步驟:
- 獲取字節(jié)碼文件對(duì)象
- 使用字節(jié)碼對(duì)象獲取構(gòu)造方法
- 設(shè)置構(gòu)造方法權(quán)限
- 使用構(gòu)造方法創(chuàng)建實(shí)例對(duì)象
代碼示例:
public static void main(String[] args) { try { //1.獲取字節(jié)碼對(duì)象 Class<?> stuClass = Class.forName("反射枚舉lambda.Student"); //2.獲取構(gòu)造方法 Constructor<?> stuConstructor = stuClass.getDeclaredConstructor(String.class,int.class); //參數(shù)也是class類(lèi)型 //3.修改方法的訪問(wèn)權(quán)限 stuConstructor.setAccessible(true); //4.調(diào)用該方法 Object object = stuConstructor.newInstance("李四",23); //newInstance()創(chuàng)建類(lèi)的實(shí)例,為Object類(lèi)型 Student s = (Student) object; System.out.println(s); } catch (Exception e) { e.printStackTrace(); } }
打印結(jié)果:
2.2 反射屬性
方法 | 說(shuō)明 |
getFields() | 獲取所有公有的屬性對(duì)象 |
getField(String name) | 獲取某個(gè)公有的屬性對(duì)象 |
getDeclaredFields() | 獲取所有的屬性對(duì)象,包括私有屬性 |
getDeclaredField(String name) | 獲取某個(gè)屬性對(duì)象,包括私有屬性 |
具體步驟:
- 獲取字節(jié)碼對(duì)象
- 使用字節(jié)碼對(duì)象獲取屬性
- 設(shè)置屬性權(quán)限
- 調(diào)用方法設(shè)置屬性值
代碼示例:
//反射屬性 Field sutAge = stuClass.getDeclaredField("age"); //參數(shù)為屬性 sutAge.setAccessible(true); sutAge.setInt(s,18); //設(shè)置屬性值為int,第一個(gè)參數(shù)為哪個(gè)對(duì)象,第二個(gè)參數(shù)為設(shè)置值 System.out.println(s);
打印結(jié)果:將對(duì)象s的age設(shè)置為18
2.3 反射方法
方法 | 說(shuō)明 |
getMethods() | 獲取該類(lèi)所有的公有的方法 |
getMethod(String name,Class...<?> parameterTypes) | 獲取該類(lèi)某個(gè)公有的方法 |
getDeclaredMethods() | 獲取該類(lèi)所有方法,包括私有 |
getDeclaredMethod(String name,Class...<?> parameterTypes) | 獲取該類(lèi)某個(gè)方法,包括私有 |
具體步驟:
- 獲取字節(jié)碼對(duì)象
- 使用字節(jié)碼對(duì)象獲取方法
- 設(shè)置方法權(quán)限
- 使用方法.invoke調(diào)用,第一個(gè)參數(shù)為哪個(gè)對(duì)象,后面參數(shù)為方法參數(shù)的具體值
代碼示例:
//反射方法 Method setNameMethod = stuClass.getDeclaredMethod("setName", String.class); //第一個(gè)參數(shù)為方法名,后面參數(shù)為方法參數(shù) setNameMethod.setAccessible(true); setNameMethod.invoke(s,"王五"); System.out.println(s);
打印結(jié)果:將對(duì)象s的姓名改為王五
反射的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
對(duì)于任意一個(gè)類(lèi),可以獲取該類(lèi)的所有屬性和方法,對(duì)于一個(gè)對(duì)象,能調(diào)用它任意一個(gè)方法
增加程序的靈活性和擴(kuò)展性,降低耦合性,提高自適應(yīng)能力
反射已經(jīng)應(yīng)用在很多框架中,如:Spring,Struts,Hibernate
缺點(diǎn):
破壞了類(lèi)的封裝性
使用反射導(dǎo)致程序效率低
反射代碼比較復(fù)雜,因而會(huì)帶來(lái)維護(hù)問(wèn)題
以上就是一文搞懂Java中的反射機(jī)制的詳細(xì)內(nèi)容,更多關(guān)于Java反射機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java JDK動(dòng)態(tài)代理實(shí)現(xiàn)原理實(shí)例解析
這篇文章主要介紹了Java JDK動(dòng)態(tài)代理實(shí)現(xiàn)原理實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06SpringBoot之?dāng)r截器與過(guò)濾器解讀
這篇文章主要介紹了SpringBoot之?dāng)r截器與過(guò)濾器解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07Java?EventBus手把手帶你實(shí)現(xiàn)
EventBus是Guava的事件處理機(jī)制,是設(shè)計(jì)模式中觀察者模式(生產(chǎn)/消費(fèi)者編程模型)的優(yōu)雅實(shí)現(xiàn)。本文就來(lái)和大家聊聊EventBus的使用,需要的可以參考一下2023-01-01登陸驗(yàn)證碼kaptcha結(jié)合spring boot的用法詳解
在一個(gè)web應(yīng)用中驗(yàn)證碼是一個(gè)常見(jiàn)的元素。不管是防止機(jī)器人還是爬蟲(chóng)都有一定的作用,下面這篇文章主要給大家介紹了登陸驗(yàn)證碼kaptcha結(jié)合spring boot用法的相關(guān)資料,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-06-06spring boot activiti工作流的搭建與簡(jiǎn)單使用
這篇文章主要給大家介紹了關(guān)于spring boot activiti工作流的搭建與簡(jiǎn)單使用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08java設(shè)計(jì)模式之策略模式在促銷(xiāo)活動(dòng)場(chǎng)景中的使用案例
這篇文章主要為大家介紹了java設(shè)計(jì)模式之策略模式在促銷(xiāo)活動(dòng)場(chǎng)景中案例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05Java程序啟動(dòng)時(shí)初始化數(shù)據(jù)的四種方式
本文主要介紹了Java程序啟動(dòng)時(shí)初始化數(shù)據(jù)的四種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-02-02Java Iterator迭代器_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
迭代器是一種模式,它可以使得對(duì)于序列類(lèi)型的數(shù)據(jù)結(jié)構(gòu)的遍歷行為與被遍歷的對(duì)象分離,接下來(lái)通過(guò)本文給大家分享Java Iterator迭代器_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理,需要的朋友參考下吧2017-05-05Springboot?多級(jí)緩存設(shè)計(jì)與實(shí)現(xiàn)方案
多級(jí)緩存是提升高并發(fā)系統(tǒng)性能的關(guān)鍵策略之一,它不僅能夠減少系統(tǒng)的響應(yīng)時(shí)間,提高用戶體驗(yàn),還能有效降低后端系統(tǒng)的負(fù)載,防止系統(tǒng)過(guò)載,這篇文章主要介紹了Springboot?多級(jí)緩存設(shè)計(jì)與實(shí)現(xiàn),需要的朋友可以參考下2024-02-02