一篇文章帶你搞定JAVA反射
1、反射的概念
1、概念
反射,指在運行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法,對任意一個對象,都能調(diào)用它的任意一個方法。這種動態(tài)獲取信息,以及動態(tài)調(diào)用對象方法的功能,叫做java語言的反射機制。反射很強大,有優(yōu)點也有缺點。
優(yōu)點:靈活性高。因為反射屬于動態(tài)編譯,即只有到運行時才動態(tài)創(chuàng)建 &獲取對象實例。
缺點:執(zhí)行效率低。
2、獲取字節(jié)碼文件對象的方式
2.1 元數(shù)據(jù)的概念
元數(shù)據(jù)(metadata):元數(shù)據(jù)是指用來描述類的數(shù)據(jù),就是class的代碼數(shù)據(jù)。所有的class文件加載到虛擬機之后都會被構(gòu)建成class對象,class對象描述了一個類都有哪些東西,大家都知道的實現(xiàn)的接口,繼承的抽象類,成員變量,類變量,成員方法,類方法,靜態(tài)方法等,這個class對象就是元數(shù)據(jù)。
- Class類:代表一個類。
- Field類:代表類的成員變量(成員變量也稱為類的屬性)。
- Method類:代表類的方法。
- Constructor類:代表類的構(gòu)造方法。
2.2 獲取class對象的方式
- 通過對象獲得,因為任何對象都必須和class對象關(guān)聯(lián)
- 通過類對象直接獲得
- 通過類加載器獲得,因為類加載器讀取class文件會返回class對象
即將用來反射的對象(隨便定義的一個對象,只是為了演示)
package org.pdool.reflect; /** * @author 香菜 */ public class Npc { // 靜態(tài)field public static int NPC_TYPE_1 = 1; // 私有成員變量 private int npcType; // 共有成員變量 public String name; // 無參構(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對象 Npc npc1 = new Npc();//這一new 產(chǎn)生一個Npc對象,一個Class對象。 Class npcClazz1 = npc1.getClass();//獲取Class對象 System.out.println(npcClazz1.getName()); //第二種方式獲取Class對象 Class npcClazz2 = Npc.class; System.out.println(npcClazz1 == npcClazz2);//判斷第一種方式獲取的Class對象和第二種方式獲取的是否是同一個 //第三種方式獲取Class對象 try { Class npcClazz3 = Class.forName("org.pdool.reflect.Npc");//注意此字符串必須是真實路徑,就是帶包名的類路徑,包名.類名 System.out.println(npcClazz3 == npcClazz2);//判斷三種方式是否獲取的是同一個Class對象 } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
1、訪問權(quán)限
反射機制的默認行為受限于Java的訪問控制,可通過 setAccessible 繞過控制。
// 設置對象數(shù)組可訪問標志 static void setAccessible(AccessibleObject[] array, boolean flag)
2、獲取方法
2.1 訪問靜態(tài)方法
public static void main(String[] args) throws NoSuchMethodException,InvocationTargetException, IllegalAccessException { Npc npc = new Npc(1, "妖神·凰女"); Class npcClazz = Npc.class; // 第一個參數(shù)是方法名,第二個參數(shù)是函數(shù)的參數(shù)class對象,因為存在重載的可能性,用參數(shù)類型區(qū)分 Method sayHello = npcClazz.getMethod("sayHello", String.class); sayHello.invoke(npc, "world"); }
2.2 訪問類方法
Npc npc = new Npc(1, "妖神·凰女"); System.out.println(npc.getName()); Class npcClazz = Npc.class; // 第一個參數(shù)是方法名,第二個參數(shù)是函數(shù)的參數(shù)class對象,因為存在重載的可能性,用參數(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; // 獲取字段,并設置可訪問 Field field = npcClazz.getField("name"); field.setAccessible(true); System.out.println( field.get(npc));
4、獲取實現(xiàn)的接口
5、獲取構(gòu)造函數(shù),創(chuàng)建實例
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(); // 運行時注解 for (Annotation annotation : annotations) { System.out.println(annotation.getClass().getName()); }
4、反射實例
獲取到元數(shù)據(jù)不是最終的目的,我們最終的目的是想在運行時去調(diào)用,訪問類。說了太多,還是舉個例子,大家都知道Spring的IOC,怎么實現(xiàn)的吶?
過程:
1、Spring 在項目啟動的時間通過讀取xml中配置的bean的路徑,
2、然后通過Class.forName 讀取class 到類加載器,
3、然后通過反射創(chuàng)建所有的bean實例并保存到容器中,啟動容器之后,
4、在項目中可以直接獲取bean對象。
我們來大概實現(xiàn)這一過程,因為xml的讀取比較麻煩,直接用property來代替了。大家體會一下思想就可以了。
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)建對象 Set<Object> keySet = property.keySet(); for (Object key : keySet) { // 2.1 獲取類的全路徑 String classStr = (String) property.get(key); // 2.2 加載class 到虛擬機 Class<?> beanClazz = Class.forName(classStr); // 2.3 獲取缺省的構(gòu)造函數(shù) Constructor<?> declaredConstructor = beanClazz.getDeclaredConstructor(); // 2.4 創(chuàng)建實例 Object o = declaredConstructor.newInstance(); container.put((String) key,o); } // 3.獲取實例 Npc npc = (Npc) container.get("npc"); System.out.println(npc == null); } catch (Exception e) { e.printStackTrace(); } } }
5、總結(jié)
在使用Java反射機制時,主要步驟包括:
獲取 目標類型的Class對象
通過 Class 對象分別獲取Constructor類對象、Method類對象 或者 Field 類對象
通過 Constructor類對象、Method類對象 & Field類對象分別獲取類的構(gòu)造函數(shù)、方法&屬性的具體信息,并進行后續(xù)操作。
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
SpringBoot項目加入沖突動態(tài)監(jiān)測算法的實現(xiàn)
沖突動態(tài)監(jiān)測算法是一種網(wǎng)絡通信中的沖突檢測方法,適用于無線網(wǎng)絡或其他共享傳輸介質(zhì)的環(huán)境,本文主要介紹了SpringBoot項目加入沖突動態(tài)監(jiān)測算法的實現(xiàn),感興趣的可以了解一下2023-09-09java中ImageReader和BufferedImage獲取圖片尺寸實例
這篇文章主要介紹了java中ImageReader和BufferedImage獲取圖片尺寸實例,具有一定借鑒價值,需要的朋友可以參考下2018-01-01SpringBoot處理form-data表單接收對象數(shù)組的方法
form-data則是一種更加靈活的編碼方式,它可以處理二進制數(shù)據(jù)(如圖片、文件等)以及文本數(shù)據(jù),這篇文章主要介紹了SpringBoot處理form-data表單接收對象數(shù)組,需要的朋友可以參考下2023-11-11SpringBoot3和ShardingSphere5框架實現(xiàn)數(shù)據(jù)分庫分表
這篇文章主要介紹了SpringBoot3和ShardingSphere5框架實現(xiàn)數(shù)據(jù)分庫分表的相關(guān)資料,需要的朋友可以參考下2023-08-08