天天看點

一文帶你了解Java反射機制

一文帶你了解Java反射機制

想要擷取更多文章可以通路我的部落格 - 代碼無止境。

上周上班的時候解決一個需求,需要将一批資料導出到Excel。本來公司的中間件組已經封裝好了使用POI生成Excel的工具方法,但是無奈産品的需求裡面有個合并單元格的要求,工具類中找了半天也沒發現适用的方法,就隻能自己撸起袖子幹了。導出Excel的工具方法會少不了使用反射,但是反射這東西對于我這種寫業務代碼的人來說接觸比較少,是以就惡補了一下,寫下這篇文章記錄一下。

什麼是反射

萬物究其根,研究一樣新東西,首先我們需要了解它是什麼,幹什麼用的。在運作狀态中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法和屬性;這種動态擷取的資訊以及動态調用對象的方法的功能稱為Java語言的反射機制。那麼我們又能利用反射做什麼呢?

在運作時分析類。

在運作時檢視對象,我們還可以利用反射編寫一個toString方法供所有類使用。

利用Method對象,在運作時任意調用一個對象的方法。

那麼本篇文章将圍繞者上面三個點來了解一下Java的反射機制。在開始之前,我們先來介紹一下一個類,這個類是我們在使用反射的過程中必不可少會使用到的一個類。

Class類

在運作時,Java運作時系統會為每一個對象都維護一個辨別這個對象類型的資訊,而儲存這些資訊的類型就是Class類。我們可以通過對象的getClass()方法來擷取該對象對于的Class對象,就像下面這樣。

User user = new User();

Class c = user.getClass();

這個世界上的任何東西都有它存在的意義,那麼我們可以用Class對象來幹什麼呢?我們最常使用Class來判斷一個對象是不是屬于某個類型,就像下面這樣:

if (user.getClass() == User.class) {

System.out.println("user is User");           

}

當然我們也經常會使用Class類的getName()方法來擷取某個類的名稱。有寫時候,我們還會利用它的newInstance()方法來擷取某個類型的執行個體(當這類沒有提供共有的構造方法時)。

利用反射分析類

分析一個類,無外乎就是檢視這個類中的屬性、方法以及其構造方法了。在Java的反射包中提供了三個類Field、Method以及Constructor來分别描述屬性、方法和構造器。

下面我們就分别來看下,我們是如何通過反射機制來擷取一個類的這些資訊的。

1.擷取屬性

Class cl = user.getClass();

Field[] fields1 = cl.getFields();

Field[] fields2 = cl.getDeclaredFields();

可以看到我們可以利用getFields()和getDeclaredFields()兩個方法來擷取類中的屬性清單,那麼這兩個方法有什麼差別呢?差別就是前者隻會傳回類的共有成員資訊,而後者這會傳回類中所有的成員資訊包括公有的、私有的、受保護的,但是不包括父類的成員資訊。

2.擷取構造器

Constructor[] constructors1 = cl.getConstructors();

Constructor[] constructors2 = cl.getDeclaredConstructors();

3.擷取方法

Method[] methods1 = cl.getMethods();

Method[] methods2 = cl.getDeclaredMethods();

可以看到我們可以通過一個類的Class對象很輕松的擷取他的屬性、構造器以及方法資訊。但是在Field、Constructor以及Method中又分别提供了哪些api呢?下面我們就一起來看下。

1.getName()方法,用來擷取對應的名稱。同時存在于Field、Constructor以及Method類中。

2.getModifiers()方法來擷取前面的修飾符(public等),但是getModifiers()傳回的是一個int值,我們可以通過Modifier.toString(int i)将其轉換成對應的字元串。也同樣同時存在于Field、Constructor以及Method類中。

3.getParameterTypes()方法,用來擷取方法的參數類型數組。存在于Constructor以及Method類中

4.getReturnType()方法,用來擷取方法的傳回類型。隻存在于Method類中

有了這些api,我們就擁有了在運作時分析一個類的能力,我們可以通過一個簡單的小例子來實踐一下,我們可以編寫一個方法來輸出一個類的完整資訊,具體的實作會在文末給出,大家可以先自己嘗試一下。

利用反射檢視對象

有些時候呢,我們可能也需要反射去擷取對象中屬性的值,比如說在導出Excel的時候,我們隻知道列所對應屬性的字段名稱,然後我們需要通過反射擷取它的值,然後把它寫到Excel中。那麼這節内容就一起來看下如何利用Java的反射機制來分析對象。

User user = new User(1,"itweknow");

Field userName = cl.getDeclaredField("userName");

Object value = userName.get(user);

就像上面的代碼一樣,我們可以使用Field類中提供的get(Object obj)方法來擷取屬性的值,對于基礎類型還提供了特定的get方法,比如getDouble()。但是如果上面的userName是個私有屬性的話,get()方法肯定會抛出IllegalAccessException的異常。這是時候我們需要使用setAccessible()方法覆寫安全管理器的通路控制。

userName.setAccessible(true);

setAccessible()方法在Field、Method、Constructor類中都有提供。與get()方法呼應,Field還提供了set()方法用來給屬性設定值。

利用反射調用任何方法

在Method類中提供了invoke()方法來調用,目前Method對象所包裝的方法。invoke()方法的定義如下:

Object invoke(Object obj, Object... args)

第一個參數是調用這個方法的對象,第二個參數是該方法的參數,是一個數組的形式。下面我們就來看下如何利用反射來調用User類中的sayHello()方法吧。

Method sayHelloMethod = cl.getDeclaredMethod("sayHello", String.class);

sayHelloMethod.setAccessible(true);

sayHelloMethod.invoke(user, "Reflect");

看上面的代碼我們通過getDeclaredMethod()方法來擷取了一個名為sayHello的私有方法(PS:如果是公有方法的話直接使用getMethod()方法就可以了),同樣對于私有方法,我們需要修改它的通路控制才能順利調用。

API整理

上面的章節中提到了不少Java反射機制中提供的Api,下面是我整理的一些常用的反射Api,大家可以參考一下。

1.Class類

Api 描述

forName() 傳回指定類名的Class對象

newInstance() 傳回一個這個類的新執行個體

getFields() 傳回這個類所有的公有屬性

getDeclaredField() 傳回這個類所有的屬性(包含公有、私有、受保護)

getMethods() 傳回這個類下所有的共有方法

getDeclaredMethods() 傳回這個類所有的方法(包含公有、私有、受保護)

getConstructors() 傳回這個類所有公有的構造器

getDeclaredConstructors() 傳回這個類所有的構造器(包含公有、私有、受保護)

getField() & getDeclaredField() 傳回這個類中指定名稱的屬性

getMethod() & getDeclaredMethod() 傳回指定名稱和參數的方法

cl.getConstructor() & cl.getDeclaredConstructor() 擷取指定參數的構造器

2.Field類

getModifiers() 傳回一個用于描述屬性的修飾符的整型數值。使用 Modifier類中的toString()方法将其轉為字元串。

getName() 返冋一個用于描述屬性名的字元串。

3.Method類

getModifiers() 傳回一個用于描述方法的修飾符的整型數值。使用 Modifier類中的toString()方法将其轉為字元串。

getName() 返冋一個用于描述方法名的字元串。

getParameterTypes() 傳回一個用于描述參數類型的Class對象數組。

getReturnType() 傳回一個用于描述返H類型的Class對象。

invoke() 調用這個對象所描述的方法, 傳遞給定參數,并傳回方法的傳回值。

4.Constructor類

getModifiers() 傳回一個用于描述構造器的修飾符的整型數值。使用 Modifier類中的toString()方法将其轉為字元串。

getName() 返冋一個用于描述構造器名的字元串。

5.AccessibleObject類

setAccessible(boolean flag) 為反射對象設定可通路标志。flag 為 true 表明屏蔽 Java 語言的通路檢查,使得對象的私有屬性也可以被査詢和設定。

isAccessible() 傳回反射對象的可通路标志的值。

結束語

這篇文章主要和大家一起了解了一下Java的反射機制,以及在反射包下Field、Method、Constructor三個類所提供的api。在利用反射分析類小節中,我提到了使用反射列印類的完整資訊,具體的實作代碼點選這裡擷取。希望這篇文章能夠對大家有所幫助。最後,如果你喜歡這篇文章的話歡迎在Github源碼項目點個Star。

原文位址

https://www.cnblogs.com/endless-code/p/11261558.html