您現在的位置是:首頁 > 攝影首頁攝影

小白進階之反射學習(超詳細)

由 紙鶴視界 發表于 攝影2021-05-18
簡介建立入口函式main()——不用**單元測試方法:是java測試的最小單位,使用靈活,推薦使用 * 語法要求: @Test + void + 沒有引數 + public * 注意使用時需要導包:Add JUnit 4 library to

入射點是什麼

1 什麼是反射?

Reflection(反射) 是 Java 程式開發語言的特徵之一,它允許執行中的 Java 程式對自身進行檢查,或者說“自審”,也有稱作“自省”。

反射非常強大,它甚至能直接操作程式的私有屬性。我們前面學習都有一個概念,被private封裝的資源只能類內部訪問,外部是不行的,但這個規定被反射赤裸裸的打破了。

反射就像一面鏡子,它可以在執行時獲取一個類的所有資訊,可以獲取到任何定義的資訊(包括成員變數,成員方法,構造器等),並且可以操縱類的欄位、方法、構造器等部分。

2 為什麼需要反射?

如果想建立物件,我們直接new User(); 不是很方便嘛,為什麼要去透過反射建立物件呢?

那我要先問你個問題了,你為什麼要去餐館吃飯呢?

例如:我們要吃個牛排大餐,如果我們自己建立,就什麼都得管理。

好處是,每一步做什麼我都很清晰,壞處是什麼都得自己實現,那不是累死了。牛接生你管,吃什麼你管,屠宰你管,運輸你管,冷藏你管,烹飪你管,上桌你管。就拿做菜來說,你能有特級廚師做的好?

那怎麼辦呢?有句話說的好,專業的事情交給專業的人做,飼養交給農場主,屠宰交給劊子手,烹飪交給特級廚師。那我們幹嘛呢?

我們翹起二郎腿直接拿過來吃就好了。

再者,飯店把東西做好,不能扔到地上,我們去撿著吃吧,那不是都成原始人了。那怎麼辦呢?很簡單,把做好的東西放在一個容器中吧,如把牛排放在盤子裡。

我們在後面的學習中,會學習框架,有一個框架Spring就是一個非常專業且功能強大的產品,它可以幫我們建立物件,管理物件。以後我無需手動new物件,直接從Spring提供的容器中的Beans獲取即可。Beans底層其實就是一個Map,最終透過getBean(“user”)來獲取。而這其中最核心的實現就是利用反射技術。

總結一句,類不是你建立的,是你同事或者直接是第三方公司,此時你要或得這個類的底層功能呼叫,就需要反射技術實現。有點抽象,彆著急,我們做個案例,你就立馬清晰。

3 反射需要用到的API

3。1 獲取位元組碼物件

Class。forName(“類的全路徑”);類名。class物件。getClass();

3。2 常用方法

獲取包名 類名

clazz。getPackage()。getName()//包名

clazz。getSimpleName()//類名

clazz。getName()//完整類名

獲取成員變數定義資訊

getFields()//獲取所有公開的成員變數,包括繼承變數

getDeclaredFields()//獲取本類定義的成員變數,包括私有,但不包括繼承的變數

getField(變數名)

getDeclaredField(變數名)

獲取構造方法定義資訊

getConstructor(引數型別列表)//獲取公開的構造方法

getConstructors()//獲取所有的公開的構造方法

getDeclaredConstructors()//獲取所有的構造方法,包括私有

getDeclaredConstructor(int。class,String。class)

獲取方法定義資訊

getMethods()//獲取所有可見的方法,包括繼承的方法

getMethod(方法名,引數型別列表)

getDeclaredMethods()//獲取本類定義的的方法,包括私有,不包括繼承的方法

getDeclaredMethod(方法名,int。class,String。class)

反射新建例項

clazz。newInstance();//執行無參構造建立物件

clazz。newInstance(666,”海綿寶寶”);//執行含參構造建立物件

clazz。getConstructor(int。class,String。class)//獲取構造方法

反射呼叫成員變數

clazz。getDeclaredField(變數名);//獲取變數

clazz。setAccessible(true);//使私有成員允許訪問

f。set(例項,值);//為指定例項的變數賦值,靜態變數,第一引數給null

f。get(例項);//訪問指定例項變數的值,靜態變數,第一引數給null

反射呼叫成員方法

Method m = Clazz。getDeclaredMethod(方法名,引數型別列表);

m。setAccessible(true);//使私有方法允許被呼叫

m。invoke(例項,引數資料);//讓指定例項來執行該方法

4 反射的應用

4。1 建立 : 測試物料類

建立包: cn。tedu。 reflection

建立類: Student。java

package cn。tedu。reflection;/**本類用於測試反射,先準備的物料類*/publicclassStudent{ //1。定義成員變數 String name;int age;//2。定義構造方法//快捷方式:右鍵——>Source——>倒數第三個——>OKpublicStudent(){ }//注意要先手動新增無參構造,防止被覆蓋publicStudent(String name,int age){ super();this。name = name;this。age = age;}//3。定義成員方法publicvoideat(int n){ System。out。println(“餓了吃點火鍋吧”+n);}//4。提供重寫的toString()//目的:為了方便檢視物件的屬性值@Overridepublic String toString(){ return“Student [name=”+ name +“,age=”+ age +“]”;}}4。2 練習 : 獲取類物件

建立包: cn。tedu。 reflection

建立類: Test1Reflect。java

package cn。tedu。reflection;import java。lang。reflect。Constructor;import java。util。Arrays;import org。junit。Test;/**本類用來測試反射*/publicclassTest1Reflect{ //1。建立入口函式main()——不用/**單元測試方法:是java測試的最小單位,使用靈活,推薦使用 * 語法要求: @Test + void + 沒有引數 + public * 注意使用時需要導包:Add JUnit 4 library to the build path:import org。junit。Test; * 單元測試方法執行方式:選中方法名——>右鍵執行(Run As——>JUnit Test)——>出現小綠條說明執行成功 *///2。透過單元測試來測試如何獲取類物件@TestpublicvoidgetClazz()throws Exception { /**右鍵要獲取位元組碼物件的類名,選擇Copy Quailfied Name複製類的全路徑名*/ Class<?> student1 = Class。forName(“cn。tedu。reflection。Student”);//此處的引數是類的全路徑名[包名+類名] Class<?> student2 = Student。class; Class<?> student3 =newStudent()。getClass();//先建立匿名物件,匿名物件沒有名字,然後物件的位元組碼物件 System。out。println(student1);//反射得到的位元組碼Class物件 System。out。println(student2。getName());//獲取類的全路徑名[包名+類名] System。out。println(student3。getSimpleName());//只獲取類名 System。out。println(student3。getPackage()。getName());//獲取包名}}4。3 練習 : 類獲取構造方法

package cn。tedu。reflection;import java。lang。reflect。Constructor;import java。util。Arrays;import org。junit。Test;/**本類用來測試反射*/publicclassTest1Reflect{ //3。透過單元測試來測試如何獲取構造方法@TestpublicvoidgetConstruct(){ //1。獲取位元組碼Class物件 Class<?> clazz = Student。class;//2。獲取構造方法們,並放入cs陣列中 Constructor<?>[] cs = clazz。getConstructors();//3。獲取每個構造//使用增強for迴圈完成//for(1 2 : 3){迴圈體}其中3是要遍歷的資料,1是遍歷後每個元素的資料型別 2是遍歷後每個資料的變數名for(Constructor c : cs){ //拿到每個構造方法以後可以獲取構造方法的相關資訊 System。out。println(c。getName());//獲取構造方法的名稱 Class[] cp = c。getParameterTypes();//獲取構造方法的引數型別,可能有多個 System。out。println(Arrays。toString(cp));}}}4。4 練習 : 獲取成員方法

package cn。tedu。reflection;import java。lang。reflect。Method;import java。util。Arrays;import org。junit。Test;/**本類用來測試反射*/publicclassTest1Reflect{ //4。透過單元測試來測試獲取成員方法@TestpublicvoidgetFunction()throws Exception { //1。獲取Class位元組碼物件 Class<?> clazz = Class。forName(“cn。tedu。reflection。Student”);//2。獲取所有成員方法 Method[] ms = clazz。getMethods();//3。遍歷陣列,獲取每個方法的資訊for(Method m : ms){ System。out。println(m。getName());//獲取方法名 Class<?>[] pt = m。getParameterTypes();//獲取方法引數型別 System。out。println(Arrays。toString(pt));}}}4。5 練習 : 獲取成員變數

package cn。tedu。reflection;import java。lang。reflect。Field;import org。junit。Test;/**本類用來測試反射*/publicclassTest1Reflect{ //5。透過單元測試來測試獲取成員變數@TestpublicvoidgetFields(){ //1。獲取Class位元組碼物件/** Class<?>中的“?”是泛型約束的萬用字元,類似於“*” */ Class<?> clazz = Student。class;//2。獲取所有的成員變數,公共的!!!/**!!!注意目前成員變數的修飾符必須是public才能獲取到,採用預設修飾符就反射不到*/ Field[] fs = clazz。getFields();//3。遍歷陣列,獲取每個成員變數的資訊for(Field f: fs){ System。out。println(f。getName());//獲取變數名 System。out。println(f。getType()。getName());//獲取變數型別}}}4。6 練習 : 建立物件

package cn。tedu。reflection;import java。lang。reflect。Constructor;import org。junit。Test;/**本類用來測試反射*/publicclassTest1Reflect{ //6。透過單元測試來測試反射建立物件/** * 方式一:透過位元組碼物件直接呼叫newInstance(),觸發無參構造來建立物件 * 方式二:先獲取指定的建構函式,再透過建構函式物件呼叫newInstance(),觸發對應的建構函式來建立物件 * */@Testpublicvoid getObject ()throws Exception { //1。獲取Class位元組碼物件 Class<?> clazz = Student。class;//2。建立物件 Object obj = clazz。newInstance();//觸發無參構造 System。out。println(obj);//Student [name=null,age=0]//3。可以指定去呼叫哪個構造方法,注意根據引數來指定,而且傳入的是引數的位元組碼物件 Constructor<?> c = clazz。getConstructor(String。class,int。class);//4。觸發指定的構造方法 Object obj2 = c。newInstance(“海綿寶寶”,3); System。out。println(obj2);//Student [name=海綿寶寶,age=3]//5。檢視物件具體的屬性值,或者呼叫方法,需要把Object強轉成指定的子類型別/**為什麼要把Object強轉成子類型別?因為想要使用子類的特有功能,父類無法使用子類的特有功能 * obj是Object型別,Object中沒有Student的屬性與功能 * 這個操作叫做向下轉型——想使用子類特有功能時需要做此操作 * */ Student s =(Student)obj2; System。out。println(s。name); System。out。println(s。age); s。eat(666);}}4。7 熟悉API

自己建立類練習,獲取類中的所有資源,熟悉反射中涉及的API

5 暴力反射

指可以將程式中的私有的屬性或者方法透過反射技術,暴力的獲取到資源。需要使用的常見方法如下:

小白進階之反射學習(超詳細)

5。1 建立 : 測試物料類

建立包: cn。tedu。 reflection

建立類: Person。java

package cn。tedu。reflection;/**本類用來測試暴力反射*/publicclassPerson{ //1。提供私有屬性private String name;privateint age;//2。提供私有方法privatevoidsave(int m,String n){ System。out。println(“save()。。”+m+n);}privatevoidupdate(){ System。out。println(“update()。。”);}}5。2 練習 : 建立測試類

建立包: cn。tedu。 reflection

建立類: TestReflect2。java

package cn。tedu。reflection;import java。lang。reflect。Field;import java。lang。reflect。Method;import org。junit。Test;/**本類用於測試暴力反射*/publicclassTestReflect2{ //1。透過單元測試來測試暴力反射獲取和設定私有屬性@TestpublicvoidgetField()throws Exception { //1。獲取Class位元組碼物件 Class<?> clazz = Person。class;//2。獲取私有屬性 Field field = clazz。getDeclaredField(“name”);//3。根據獲取到的屬性物件獲取屬性型別 System。out。println(field。getType()。getName());//4。設定屬性的值//set(m,n)—— m:要給哪個物件設定值,n:是要設定的具體值//4。1沒有物件就透過反射的方式建立物件 Object obj = clazz。newInstance();//4。2暴力反射!!!設定私有可見 field。setAccessible(true);//4。3給指定物件設定值 field。set(obj,“派大星”);//5。獲取私有屬性的值 System。out。println(field。get(obj));}//2。透過單元測試來測試暴力反射獲取和設定私有方法@TestpublicvoidgetFunction()throws Exception{ //1。獲取Class位元組碼物件 Class<?> clazz = Person。class;//2。透過暴力反射獲取私有方法//getDeclaredMethod(m,n,x,y。。。。) m:要執行的方法名 n x y :是方法的引數型別 Method method = clazz。getDeclaredMethod(“save”,int。class,String。class);//3。透過暴力反射執行私有方法//invoke(m,n,x,y。。。) m:要執行的哪個物件的方法 n x y :是方法的引數型別//3。1沒有物件就透過反射的方式建立物件 Object obj = clazz。newInstance();//3。2 想要執行私有方法,首先設定私有可見 method。setAccessible(true);//3。3 invoke表示透過反射執行方法 method。invoke(obj,100,“蟹老闆”);}}

恭喜你,又學會了一個新知識,反射的API比較多,要多多練習哦

下一節 內部類 點這裡哦

,https://blog。csdn。net/weixin_43884234/article/details/115056812