Java核心知識體系1:泛型機制詳解
Java核心知識體系2:註解機制詳解
Java核心知識體系3:異常機制詳解
Java核心知識體系4:AOP原理和切面應用
無論是那種語言體系,反射都是必不可少的一個技術特徵。從Java體系來說,很多常用的技術框架或多或少都使用到了反射技術,比如Spring、MyBatis、RocketMQ、FastJson 等等。反射技術強大而必要,在大多數框架中起到舉足輕重的作用。所以,反射也是Java必不可少的核心技術之一。
接下來我們來看看反射的一些技術要點:
Java反射(Reflection)是Java語言的一個核心特性,它允許執行中的Java程式碼對自身進行自我檢查,甚至修改自身的元件。具體來說,反射機制提供了在執行狀態中,對於任意一個類,都能夠了解這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性。這種動態獲取的資訊以及動態呼叫物件的方法在Java中就叫做反射。
一句話總結:反射就是在執行時才具體知曉要操作的類是什麼結構,並在執行時獲取類的完整構造,並呼叫對應的方法、屬性等。
Java的反射主要包括以下三個部分:
Java Reflection功能非常強大,並且非常有用,比如:
具體的應用場景:
在Java中,Class類與java.lang.reflect類庫配合對反射技術進行了完整的支援。在反射的Package中,我們經常使用功能類如下:
下面將對這幾個類進行詳細介紹。
一般情況下我們通過反射建立類物件主要有兩種方式:
通過 Class 物件的 newInstance() 方法
通過 Constructor 物件的 newInstance() 方法
通過 Class 物件的 newInstance() 方法實現
Class clz = Class.forName("com.ad.reflection.TestRefle");
TestRefle tr= (TestRefle)clz.newInstance();
Class clz = Class.forName("com.ad.reflection.TestRefle");
Constructor constructor = clz.getConstructor();
TestRefle tr= (TestRefle)constructor.newInstance();
這邊需要注意,通過 Constructor 物件建立類物件可以選擇特定構造方法,而通過 Class 物件則只能使用預設的無引數構造方法。
下面的程式碼演示的是通過 Constructor 呼叫有參構造方法進行了類物件初始化:
Class clz = Class.forName("com.ad.reflection.TestRefle");
Constructor constructor = clz.getConstructor(String.class);
TestRefle tr= (TestRefle)constructor.newInstance("提供一個String引數");
接下來我們繼續,通過具體的API獲取詳細的類資訊:類資訊、方法資訊、屬性資訊等。
// 獲取Class物件的三種方式
根據類名: Class mailClass = MailInfo.class;
根據物件: Class mailClass = new MailInfo().getClass();
根據全限定類名: Class mailClass = Class.forName("com.ad.MailInfo");
// 根據物件獲取資訊和範例物件
獲取全限定類名: mailClass.getName();
獲取類名: mailClass.getSimpleName();
範例化: userClass.getDeclaredConstructor().newInstance();
更加詳細Class類獲取參考如下:
方法 | 用途 |
---|---|
forName() | (1)獲取Class物件的一個參照,但參照的類還沒有載入(該類的第一個物件沒有生成)就載入了這個類。 (2)為了產生Class參照,forName()立即就進行了初始化。 |
Object-getClass() | 獲取Class物件的一個參照,返回表示該物件的實際型別的Class參照。 |
getName() | 取全限定的類名(包括包名),即類的完整名字。 getSimpleName() 獲取類名(不包括包名) |
getCanonicalName() | 獲取全限定的類名(包括包名) |
isInterface() | 判斷Class物件是否是表示一個介面 |
getInterfaces() | 返回Class物件陣列,表示Class物件所參照的類所實現的所有介面。 |
getSupercalss() | 返回Class物件,表示Class物件所參照的類所繼承的直接基礎類別。應用該方法可在執行時發現一個物件完整的繼承結構。 |
newInstance() | 返回一個Oject物件,是實現「虛擬構造器」的一種途徑。使用該方法建立的類,必須帶有無參的構造器。 |
Field[] fields = _class.getDeclaredFields();
更加詳細成員變數獲取參考如下:
方法 | 用途 |
---|---|
getField(String name) | 獲得某個公有的屬性物件 |
getFields() | 獲取所有的公有的屬性物件 |
getDeclaredField(String name) | 獲得某個屬性物件(public和非public) |
getDeclaredFields() | 獲得所有屬性物件(public和非public) |
Method[] methods = _class.getDeclaredMethods();
更加詳細方法獲取參考如下:
方法 | 用途 |
---|---|
getMethod(String name, Class...<?> paramerterTypes) | 獲得某個公有的方法物件 |
getMethods() | 獲取所有的公有的方法物件 |
getDeclaredMethod(String name, Class...<?> paramerterTypes) | 獲得對應類下某個方法(public和非public) |
getDeclaredMethods() | 獲得對應類下所有方法(public和非public) |
Constructor[] constructors = _class.getDeclaredConstructors();
更加詳細建構函式獲取參考如下:
方法 | 用途 |
---|---|
getConstructor(Class...<?> paramerterTypes) | 獲得該類中與引數型別匹配的公有構造方法 |
getConstructors() | 獲取該類的所有公有構造方法 |
getDeclaredConstructor(Class...<?> paramerterTypes) | 獲得該類中與引數型別匹配的構造方法 |
getDeclaredConstructors() | 獲取該類的所有構造方法 |
這樣通過反射就可以做在執行時獲取類的完整構造,並獲得類資訊了。
類名 | 用途 |
---|---|
Class類 | 代表類的實體,在執行的Java應用程式中表示類和介面 |
Field類 | 代表類的成員變數(即類的屬性) |
Method類 | 代表類的方法 |
Constructor類 | 代表類別建構函式 |
通過上面的幾個範例我們基本瞭解了反射的使用,但這僅僅是使用,我們還需深入理解反射背後的底層實現原理。
1、編寫完Java專案之後,java檔案都會被編譯成一個.class檔案
2、這些class檔案在程式執行時會被ClassLoader載入到JVM中,當一個類被載入以後,JVM就會在記憶體中自動產生一個Class物件。
3、通過Class物件獲取 Field(屬性)、Method(方法)、Construcor(建構函式)
我們平時通過new的形式建立物件,本質上就是通過Class來建立個新物件
通過上面的流程我們可以看出反射的優勢:
我們的程式在執行時,可能不一定會用到所有我們編寫和構建的類,這樣避免啟動時間太長並且浪費大量無用的機器資源。
取而代之的是動態的載入一些類,這些類可能之前用不到所以不用載入到jvm,而是在執行時根據需要才載入。
完整的呼叫流程,圖片來自網上,比較模糊,後續再補一個
以下案例來自百度文心一言大模型自動生成,已偵錯通過。
import java.lang.reflect.Method;
public class ReflectionExample {
public static void main(String[] args) {
try {
// 獲取目標類的Class物件
Class<?> targetClass = Class.forName("java.util.ArrayList");
// 獲取目標類的所有公共方法
Method[] methods = targetClass.getMethods();
// 遍歷所有方法並列印方法名
for (Method method : methods) {
System.out.println(method.getName());
}
// 獲取特定方法,比如新增元素的add方法
Method addMethod = targetClass.getMethod("add", Object.class);
// 建立目標類的範例物件
Object targetObject = targetClass.newInstance();
// 呼叫add方法新增元素
addMethod.invoke(targetObject, "Hello, World!");
// 獲取目標類的所有屬性(欄位)並列印屬性名
Field[] fields = targetClass.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
這個案例展示瞭如何使用反射來獲取目標類的Class物件,獲取並列印目標類的所有公共方法,獲取特定方法,建立目標類的範例物件,呼叫目標類的方法,以及獲取並列印目標類的所有屬性(欄位)。
無論是那種語言體系(C#、Java等等),反射都是必不可少的一個技術特徵。而從Java體系來說,很多常用的技術框架或多或少都使用到了反射技術,比如Spring、MyBatis、RocketMQ、FastJson 等等。
學習好Java 反射技術能幫助你更好的理解底層呼叫的原理,也有助於設計更加 輕巧、高內聚、低耦合 的業務框架。