Java中反射機制的理解
- java反射的概念
- 在Java中的反射機制是指在執行狀態中,對於任意一個類都能夠知道這個類所有的屬性和方法;並且對於任意一個物件,都能夠呼叫它的任意一個方法;這種動態獲取資訊以及動態呼叫物件方法的功能成為Java語言的反射機制。簡單的說,反射機制就是在程式的執行過程中被允許對程式本身進行操作,比如自我檢查,進行裝載,還可以獲取類本身,類的所有成員變數和方法,類的物件,還可以在執行過程中動態的建立類的例項,通過例項來呼叫類的方法,這就是反射機制一個比較重要的功能了。
- 理解反射機制,首先要理解類的載入過程
- 載入過程圖
-
在Java程式執行的時候,要經歷三個步驟:載入、連線和初始化。首先程式要載入到JVM的方法區中,然後進行連線,最後初始化。這裡就主要介紹一下類的載入。如上圖,首先,JVM會從硬碟中讀取Java原始檔並將其載入到方法區中同時生成類名.class檔案,也就是類物件,這個類物件中包含了我們建立類的例項時所需要的模板資訊,也就是原始碼中的成員變數和方法等。Class本身也是一個類,它的主要功能之一就是生成類載入時的class檔案,為類的初始化及例項化做準備。而我們在程式中通過關鍵字new建立的物件建立的是類的物件,而不是類物件,二者的區別如圖中所示。
- 原理模型
- 常用的反射類以及用法
- java.lang.class
-
(呼叫某個物件的getClass()方法
User user=new User()
Class clazz=user.getClass() -
呼叫某個類的class屬性來獲取該類對應的Class物件
Class clazz=User.class -
呼叫包的地址
Calss clazz=Class.forName("")
-
- java.lang.reflect.Method
-
獲取類的所有方法資訊getDeclaredMethods()
-
獲取類的公有方法資訊getMethods()
-
要注意的是,在暴力獲取私有方法後還要獲取呼叫該方法的許可權:.setAccessible(true)(預設false,需要設定成true)
-
- java.lang.reflect.fields
-
獲取類的所有成員屬性資訊getDeclaredFields()
-
獲取類的公有成員屬性資訊getFields()
-
- java.lang.reflect.constructor
-
獲取類的所有構造方法資訊getDeclaredConstructors()
-
獲取類的公有構造方法資訊getConstructors()
-
- java.lang.class
- 建立例項的兩種方式
-
使用Class物件的newInstance()方法來建立該Class物件對應類的例項,但是這種方法要求該Class物件對應的類有預設的 空構造器。
-
先使用Class物件獲取指定的Constructor物件,再呼叫Constructor物件的newInstance()方法來建立 Class物件對應類的實 例,通過這種方法可以選定構造方法建立例項。
-
- Java反射優缺點
-
Java反射優點:在執行期確定物件、繫結物件、操作物件,最大限度的發揮了Java的靈活性。
-
Java反射缺點:反射相當於一系列解釋操作,通知jvm要做的事情,效能相對較低。反射會跳過型別檢查等,導致安全性問題。例如通過反射跳過泛型的編譯前型別檢查
-
- 應用場景
-
框架中的xml,properties等配置
-
框架中註解的使用
-
動態代理、AOP
-
JDBC資料庫連線
-
- 例子
import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import com.javasm.lang.Person; public class PersonTest3 { /** * 操作類的屬性/成員變數 * 操作方法---->操作Method類 * 操作構造---->操作Constructor類 */ public static void test1() { try { Person person = new Person(); //1.建立Class類的例項 Class clazz = Class.forName("com.lj.bean.Person");//類或者介面的全限定名稱 包名+類名 Method[] methods = clazz.getMethods();//只獲得public方法(包含從父類繼承的) System.out.println(Arrays.toString(methods)); methods = clazz.getDeclaredMethods();//只獲得本類裡面public/protected/預設/private System.out.println(Arrays.toString(methods)); // String 方法名 parameterTypes:引數型別 可變引數 >=0實參 //對name賦值 setName Method method = clazz.getMethod("setName", String.class); //執行方法 Object obj = method.invoke(person, "張三");//方法的引數資料 System.out.println(obj); System.out.println("person:"+person.getName()); System.out.println("------------------------------"); //執行b方法 method = clazz.getDeclaredMethod("b", String.class,Integer.class); method.setAccessible(true); obj = method.invoke(person, "hello world",100); System.out.println(obj); } catch (ClassNotFoundException|NoSuchMethodException|SecurityException e) { e.printStackTrace(); } catch (IllegalAccessException |IllegalArgumentException |InvocationTargetException e) { e.printStackTrace(); } } public static void main(String[] args) { test1(); } }