???反射是啥呀相信許多學java的同學非常困惑在學的時候,總是感覺懂了卻又沒懂或者直接忽略過去了,那么本文就帶大家探討一下什么是反射在java中以及它的機制和運用。
??什么是反射:
首先我們知道一些知識:
維基百科的解釋
在計算機學中,反射式編程(英語:reflective programming)或反射(英語:reflection),是指計算機程序在運行時(runtime)可以訪問、檢測和修改它本身狀態或行為的一種能力.用比喻來說,反射就是程序在運行的時候能夠“觀察”并且修改自己的行為。
要注意術語“反射”和“內省”(type introspection)的關系。內省(或稱“自省”)機制僅指程序在運行時對自身信息(稱為元數據)的檢測;反射機制不僅包括要能在運行時對程序自身信息進行檢測,還要求程序能進一步根據這些信息改變程序狀態或結構。
這個解釋用在java就是通過jvm創建class示例進行操作反過來又能對對象進行訪問和修改。反射(Reflection)是 Java 的一種特性,它可以讓程序在運行時獲取自身的信息,并且動態地操作類或對象的屬性、方法和構造器等。通過反射功能,可以讓我們在不知道具體類名的情況下,依然能夠實例化對象,調用方法以及設置屬性。
首先知道一個加載過程
Java語言的編譯過程主要包括以下幾個步驟:
詞法分析:
詞法分析器(Lexer)讀取源代碼文件,將源代碼中的字符序列轉換為記號(Token)序列。這些記號是編譯器進一步處理的基本單元。
語法分析:
語法分析器(Parser)根據Java語言的語法規則,將詞法分析器輸出的記號序列組織成語法樹(Abstract Syntax Tree, AST)。語法樹表示了源代碼中的程序結構。
語義分析:
語義分析器檢查語法樹是否滿足語義規則,比如變量是否已經聲明、類型是否匹配、表達式是否有意義等。這個階段還會進行一些類型推斷和轉換。
字節碼生成:
編譯器將經過語義分析后的語法樹轉換成字節碼(Bytecode)。字節碼是一種平臺無關的中間代碼,它可以在任何安裝了Java虛擬機(JVM)的平臺上運行。
具體來說,Java編譯過程如下:
源代碼到字節碼:
使用javac命令或Java編譯器API將.java源文件編譯成.class字節碼文件。這一步包含了上述的詞法分析、語法分析、語義分析和字節碼生成。
字節碼驗證:
當.class文件被加載到JVM時,字節碼驗證器會檢查字節碼文件,確保其遵循Java語言規范,沒有安全問題,比如確保不會發生數組越界、棧溢出等。
類加載:
類加載器(ClassLoader)負責將.class文件加載到JVM中,并為字節碼分配內存,將其轉換為運行時數據結構。
運行時優化:
JVM可能會對字節碼進行即時編譯(Just-In-Time, JIT)優化,將字節碼轉換成本地機器碼以提高執行效率。
執行:
最終,在JVM上執行這些字節碼,程序開始運行。
這個過程確保了Java語言的“一次編寫,到處運行”的特性,因為編譯生成的字節碼可以在任何有JVM的平臺上執行。
然后我們正式開始:
反射之中包含了一個「反」字,所以想要解釋反射就必須先從「正」開始解釋。
一般情況下,我們使用某個類時必定知道它是什么類,是用來做什么的。于是我們直接對這個類進行實例化,之后使用這個類對象進行操作。
Apple apple = new Apple(); //直接初始化,「正射」
apple.setPrice(4);
上面這樣子進行類對象的初始化,我們可以理解為「正」。
而反射則是一開始并不知道我要初始化的類對象是什么,自然也無法使用 new 關鍵字來創建對象了。
這時候,我們使用 JDK 提供的反射 API 進行反射調用:
Class clz = Class.forName("com.muggle.reflect.Apple");
Method method = clz.getMethod("setPrice", int.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, 4);
上面兩段代碼的執行結果,其實是完全一樣的。但是其思路完全不一樣,第一段代碼在未運行時就已經確定了要運行的類(Apple),而第二段代碼則是在運行時通過字符串值才得知要運行的類(com.chenshuyi.reflect.Apple)。
所以說什么是反射?
反射就是在運行時才知道要操作的類是什么,并且可以在運行時獲取類的完整構造,并調用對應的方法。
(1)Java反射機制的核心是在程序運行時動態加載類并獲取類的詳細信息,從而操作類或對象的屬性和方法。本質是JVM得到class對象之后,再通過class對象進行反編譯,從而獲取對象的各種信息。
(2)Java屬于先編譯再運行的語言,程序中對象的類型在編譯期就確定下來了,而當程序在運行時可能需要動態加載某些類,這些類因為之前用不到,所以沒有被加載到JVM。通過反射,可以在運行時動態地創建對象并調用其屬性,不需要提前在編譯期知道運行的對象是誰。
??獲取Class對象的三種方式
package fanshe;
/*** 獲取Class對象的三種方式* 1 Object ——> getClass();* 2 任何數據類型(包括基本數據類型)都有一個“靜態”的class屬性* 3 通過Class類的靜態方法:forName(String className)(常用)**/
public class Fanshe {public static void main(String[] args) {//第一種方式獲取Class對象 Student stu1 = new Student();//這一new 產生一個Student對象,一個Class對象。Class stuClass = stu1.getClass();//獲取Class對象System.out.println(stuClass.getName());//第二種方式獲取Class對象Class stuClass2 = Student.class;System.out.println(stuClass == stuClass2);//判斷第一種方式獲取的Class對象和第二種方式獲取的是否是同一個//第三種方式獲取Class對象try {Class stuClass3 = Class.forName("fanshe.Student");//注意此字符串必須是真實路徑,就是帶包名的類路徑,包名.類名System.out.println(stuClass3 == stuClass2);//判斷三種方式是否獲取的是同一個Class對象} catch (ClassNotFoundException e) {e.printStackTrace();}}
}
??反射的運用
框架:半成品軟件。可以在框架的基礎上進行軟件開發,簡化編碼
反射:將類的各個組成部分封裝為其他對象,這就是反射機制
? 好處:
? 可以在程序運行過程中,操作這些對象。
? 可以解耦,提高程序的可擴展性。
像咱們平時大部分時候都是在寫業務代碼,很少會接觸到直接使用反射機制的場景。但是,這并不代表反射沒有用。相反,正是因為反射,你才能這么輕松地使用各種框架。像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射機制。這些框架中也大量使用了動態代理,而動態代理的實現也依賴反射。比如下面是通過 JDK 實現動態代理的示例代碼,其中就使用了反射類 Method 來調用指定的方法。
public class DebugInvocationHandler implements InvocationHandler {/*** 代理類中的真實對象*/private final Object target;public DebugInvocationHandler(Object target) {this.target = target;}public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {System.out.println("before method " + method.getName());Object result = method.invoke(target, args);System.out.println("after method " + method.getName());return result;}
}
另外,像 Java 中的一大利器 注解 的實現也用到了反射。為什么你使用 Spring 的時候 ,一個@Component注解就聲明了一個類為 Spring Bean 呢?為什么你通過一個 @Value注解就讀取到配置文件中的值呢?究竟是怎么起作用的呢?這些都是因為你可以基于反射分析類,然后獲取到類/屬性/方法/方法的參數上的注解。你獲取到注解之后,就可以做進一步的處理。談談反射機制的優缺點優點:可以讓咱們的代碼更加靈活、為各種框架提供開箱即用的功能提供了便利缺點:讓我們在運行時有了分析操作類的能力,這同樣也增加了安全問題。比如可以無視泛型參數的安全檢查(泛型參數的安全檢查發生在編譯時)。
在使用 JDBC 連接數據庫時使用 Class.forName()通過反射加載數據庫的驅動程序,如果是mysql則傳入mysql的驅動類,而如果是oracle則傳入的參數就變成另一個了。
Spring 框架的 IOC(動態加載管理 Bean),Spring通過配置文件配置各種各樣的bean,你需要用到哪些bean就配哪些,spring容器就會根據你的需求去動態加載,你的程序就能健壯地運行。
還有Spring AOP(動態代理)功能都和反射有關系。
除此之外還有很多框架:mybatis、dubbo、rocketmq等等都會用到反射機制。
參考
反射的作用
大白話說反射
javaguide
java非常重要的基礎概念之一
反射