你瞭解Java反射嗎?
剛開始接觸反射這個概念,感覺反射這個機制很複雜很難懂,所以在這篇文章中對Java的反射機制以個人的理解總結歸納。 |
什麼是反射?在官方文件中是這樣說的:
Reflection is commonly used by programs which require the ability to examine ormodify the runtime behavior of applications running in the Java virtual machine.
This is a relatively advanced feature and should be used only by developers whohave a strong grasp of the fundamentals of the language.
With that caveat in mind, reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible
翻譯一下:
反射技術通常被用來檢測和改變應用程式在 Java 虛擬機器中的行為表現。它是一個相對而言比較高階的技術,通常它應用的前提是開發者本身對於 Java 語言特性有很強的理解的基礎上。值得說明的是,反射是一種強有力的技術特性,因此可以使得應用程式執行一些常規手段無法企及的目的。
個人理解:反射是一種很牛x的技術,使用反射的條件是程式猿是一個大猿,對java的特性非常理解。反射的牛逼之處在於他可以完成一些非常規操作。
舉個例子來說明一下:
稍微想了一下,覺得用煮飯這個栗子來說明吧,不知道準不準確(^,^)。平時我們在家裡一般是用電飯煲來煮飯的,煮飯的步驟一般是:淘米——>擦乾鍋底——>把鍋放到電飯煲裡——>合上蓋子,通電,電飯煲工作——>飯煮熟了,可以吃了,但現在有個需求,我要在煮飯的過程中加個雞蛋,這時怎麼解決呢?是不是開啟正在通電煮飯的電飯煲,然後把雞蛋放進去呢?(來自吃貨的需求^_^)其實反射就相當於剛才加雞蛋的過程。所以反射很牛逼,他不按常規套路出牌,在程式執行的過程中搞一些小動作,以達到“吃貨”的目的。補充一下:“淘米——>擦乾鍋底——>把鍋放到電飯煲裡——>”這個過程可以看做編碼編譯過程,“——>合上蓋子,通電,電飯煲工作”可以看做是程式執行過程,“——>飯煮熟了,可以吃了”可以看做程式執行結束。
理解反射機制時,首先熟悉一下幾個類:
1)Class類
Class類例項表示正在執行的Java應用程式中的類和介面。Class是普通類、介面、列舉類、陣列等的抽象,即它們的型別就是Class,它們是Class的例項。
既然Class代表著類和介面,那麼我們可以透過他的例項(位元組碼檔案)來獲取對應類或介面的資訊,如:註解、修飾符、型別、類的名稱、屬性、方法、構造方法、直接父類和子類等,還有可以建立它的例項,但只能呼叫無參構造方法來建立。
什麼看不懂?舉個例子,我們都知道,生物可以分為動物、植物、微生物和病毒等,而動物又有人、喵星人、小狗等,植物、微生物和病毒也一樣。同樣,我們可以類比一下,生物就是Class,動物是普通類,植物是介面,微生物是列舉類、病毒是陣列(列舉和陣列是特殊的類),而人、喵星人、小狗是我們熟悉的物件,如圖
這下可整明白了吧,普通類、介面、列舉、陣列其實都可以當做Class的物件。
2)Field類
Field表示類的屬性,屬性含有修飾符、型別、屬性名稱和值。所以可以透過Field的例項獲取屬性的修飾符、型別、屬性名稱,並且可以修改屬性的值。
3)Method類
Method表示類的成員方法,方法包括註解、修飾符、返回型別、方法名,引數等。所以可以透過Method的例項獲取方法的的資訊,如,註解、修飾符、返回型別、方法名並且可以呼叫所表示的方法。
4)Constructor類
Constructor表示構造方法,可以透過Constructor的例項獲取構造方法的資訊,如,修飾符等,並且可以透過它來建立它所在類的的例項。
5)Modifier類
Modifier表示修飾符,可透過它來獲取修飾符的資訊,例如何種修飾符等
6)Annotation
Annotation代表註解
以上類都位於java.lang中
瞭解了什麼是反射後,是不是也想體驗一下反射這種騷操作?
想秀操作,首先要獲取Class物件吧,因為Class物件是代表著各種類,有了它之後才可以得到類的各種資訊。獲取方法如下:
1)透過object.getClass()
public static void main(String[] args) { Car car = new Car(); Class clazz = car.getClass(); }
注意:此方法不適用於int、float等型別
2)透過(型別名).class、包裝類.Type
public static void main(String[] args) { Class clazz = Car.class; Class cls1 = int.class; Class cls2 = String.class; Class cls3=Iteger.Type }
3)透過Class.forClass(String 類的全限定名)
1 try { 2 Class clz = Class.forName("com.frank.test.Car"); 3 } catch (ClassNotFoundException e) { 4 e.printStackTrace(); 5 }
採用哪種方法來獲取,看實際情況而定。
有了Class物件後,就可以獲取類的成員(方法+屬性)、註解和類的修飾符等。上面也說了,java中方法用Method類表示、屬性用Field類表示、註解用Annotation類來表示、修飾符用Modifier類表示。Class類中有對應的方法來獲取他們。如下:
2.3.1 獲取屬性Field的物件
//獲取所有的屬性,但不包括從父類繼承下來的屬性 public Field[] getDeclaredFields() throws SecurityException //獲取自身的所有的 public 屬性,包括從父類繼承下來的。 public Field[] getFields() throws SecurityException //獲取在本類中宣告的指定的屬性,引數為屬性的名稱 public Field getDeclaredField(String name) //獲取指定的公有屬性,包括父類的,引數為屬性的名稱 public Field getField(String name)
2.3.2 獲取方法Method物件
//獲取本類宣告指定的的方法,第一個引數是方法的名稱,後面的引數是方法引數型別的類, //如獲取setName(String name)方法,getDeclareMethod(“setName”,String.Class) public Method getDeclaredMethod(String name, Class< ?>... parameterTypes) //獲取公有的方法,包括父類的 public Method getMethod(String name, Class< ?>... parameterTypes) //獲取本類中宣告的所有方法 public Method[] getDeclaredMethods() //獲取所有的公有方法,包括父類的 public Method[] getMethods()
2.3.3 獲取構造器Constructor物件
//獲取本類中指定的構造方法 public ConstructorgetDeclaredConstructor(Class< ?>... parameterTypes) //獲取指定的公有構造方法 public ConstructorgetConstructor(Class< ?>... parameterTypes) //獲取本類中所有的構造方法 public Constructor< ?>[] getDeclaredConstructors() throws SecurityException //獲取本類中所有的公有構造方法 public Constructor< ?>[] getConstructors()
構造方法的獲取與普通方法的獲取大致是一樣的。
------------------------------------------------------------------
以上的方法都是在Class類中,別傻傻不知道(別問我怎麼知道的>_>),然後透過Class物件呼叫就可以了。
這裡只是列舉了常用類資訊的的獲取方法,其他資訊的獲取方法,看API文件吧,如註解、類的Class的物件(額好像有點繞。。。)等.
上面只是獲取了類的成員所代表類的物件,我們還要使用他們或者獲取成員的資訊(名稱、修飾符等)。因為有了代表成員的物件,使用物件呼叫例項方法就可以了。
2.4.1 Field類
Field類的方法大概可以分為兩種,一種是獲取屬性的資訊,另外一種是設定屬性的值。
第一種:
//返回由此 Field物件表示的欄位的名稱 String getName() //返回一個 類物件標識了此表示的欄位的宣告型別 Field物件。 Class< ?> getType() //返回由該 Field物件表示的欄位的Java語言修飾符,作為整數。把整數作為Modifier的構造方法的引數,就可以獲取該整數代表的修飾符類的物件了 int getModifiers() ---------------------------------------------------------------- //獲取型別為 int的靜態或例項欄位的值,或透過擴充套件轉換轉換為型別 int的另一個原始型別的值。 int getInt(Object obj) //獲取型別為 long的靜態或例項欄位的值,或透過擴大轉換獲得可轉換為型別 long的另一個基本型別的值。 long getLong(Object obj) ......此處省略一堆get**(Object obj)的方法,屬性是什麼基本型別,就get什麼就行了 14屬性是引用型別,那麼就呼叫以下方法 //返回該所表示的欄位的 Field ,指定的物件上。 16 Object get(Object obj)
第二種:
//設定作為一個欄位的值 double指定的物件上。 void setDouble(Object obj, double d) //設定作為一個欄位的值 float指定的物件上。 void setFloat(Object obj, float f) //設定作為一個欄位的值 int指定的物件上。 void setInt(Object obj, int i) ........此處省略一堆set**()方法,屬性是什麼基本型別就set什麼就行了 屬性是引用型別,那麼就呼叫以下方法 //將指定物件引數上的此 Field物件表示的欄位設定為指定的新值。 void set(Object obj, Object value)
注意啦:如果沒有訪問許可權的話,預設是不能設定屬性值的,那��麼辦呢?是不是就秀不了操作了?然而,前面也說了,反射很牛逼,可以來一些非常規操作,
這時我們呼叫Class物件的setAccessible(true)方法就可以了!
是不是覺得反射可以很強?
2.4.2 Method類
Method類的方法主要是獲取方法的資訊
部分方法:
1 int getModifiers() //返回由該物件表示的可執行檔案的Java語言modifiers 。 2 String getName() //返回由此 方法物件表示的方法的名稱,作為 String 。 3 Annotation[][] getParameterAnnotations() //返回一個 Annotation s的陣列陣列,表示由該物件表示的Executable的形式引數的宣告順序的 Executable 。 4 int getParameterCount() //返回由此物件表示的可執行檔案的形式引數(無論是顯式宣告還是隱式宣告)的數量。 5 Class< ?>[] getParameterTypes() //返回一個 類物件的陣列, 類以宣告順序表示由該物件表示的可執行檔案的形式引數型別。
2.4.3 Constructor類
Constructor類的方法主要是獲取構方法的資訊和建立物件
獲取方法資訊:
1 int getModifiers() //返回由該物件表示的可執行檔案的Java語言modifiers 。 2 String getName() //以字串形式返回此建構函式的名稱。 3 Annotation[][] getParameterAnnotations() //返回的陣列的陣列 Annotation表示的形參進行註釋s時,宣告順序的的 Executable該物件表示。 4 int getParameterCount() //返回由此物件表示的可執行檔案的形式引數(無論是顯式宣告還是隱式宣告)的數量。 5 Class< ?>[] getParameterTypes() //返回一個 類物件的陣列, 類以宣告順序表示由該物件表示的可執行檔案的形式引數型別。
建立物件的方法先不說,放到後面去。
2.5.1 建立普通類的物件
建立普通類的物件可以分為兩種方法
第一種:呼叫Class物件的方法
4 //首先獲取Class物件 5 Class clazz=Class.forClass("test.Student"); 6 //建立物件 7 Student stu=(Student)clazz.newInstance(); 注:此方法只能建立無參建構函式的類的物件
第二種:透過Constructor的newInstance()方法
//首先建立Class物件 Class clazz=Class.forClass("test.Student"); //獲取想呼叫的建構函式 Constructor constructor=clazz.getConstructor(String.class, int.class); //呼叫Constructor的newInstance()方法 Student stu=(Student)constructor.newInstance("大王",20);
2.5.2 建立陣列
陣列本質上是一個 Class,而在 Class 中存在一個方法用來識別它是否為一個陣列。
反射建立陣列是透過 Array.newInstance(T.class,維數) 這個方法。
第一個引數指定的是陣列內的元素型別,後面的是可變引數,表示的是相應維度的陣列長度限制。
比如,我要建立一個 int[2][3] 的陣列。
1 Int[][] a=Array.newInstance(Integer.TYPE, 2, 3);
2.5.3 呼叫方法
用了上面的方法,就有Class物件,有方法Method物件,有例項,現在已經萬事俱備,只欠東風了。
那我們怎麼呼叫方法呢?在Method類有這麼一個方法Object invoke(Object obj, Object... args),object為例項物件,args為呼叫方法的引數
來個栗子:
Class< ?> c = Class.forName("com.kal01.reflect05.Person");//獲取Class物件 Person p1 = (Person) c.newInstance();//獲取例項 Method m3 = c.getDeclaredMethod("test");//獲取方法 m3.setAccessible(true);//當沒有訪問許可權時,設定一下就可以 m3.invoke(p1);//呼叫方法 m3.setAccessible(false);//修改了訪問許可權,記得修改回來
看到這裡是不是有個疑問,反射呼叫類的方法好像除了複雜之外,跟我們平時呼叫沒什麼區別。何必弄那麼花裡胡哨?
所以在這裡簡單說一下靜態載入與動態載入。
回想一下之前煮飯的那個栗子,靜態載入和動態載入的這個例子有點相似。
靜態載入:我們在程式中使用類時,靜態載入是要求要使用的類必須要求在編譯的時候存在,否則編譯器報錯,無法執行程式。編碼時忘記導包時,經常會出現這種錯誤。
動態載入:利用反射來載入類(即獲得Class物件),不要求我們在編譯期存在要是用的那個類,在程式執行時,才去尋找類(可以從jar包,網路等尋找),然後把類載入到方法區中,如果沒有找到這個類會丟擲ClassNotFoundException異常。
有圖有真相:
這下看出來反射的牛逼之處了吧,利用反射使用類時,並不需要這個類在編譯期存在,這就增加了程式的靈活性,可以完成我們的騷操作!
最後總結一下:
使用反射時,記住一句話:老哥,穩住,別翻車!
原文地址:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31559985/viewspace-2657489/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 一文帶你瞭解Java反射機制Java反射
- Java SPI機制,你瞭解過嗎?Java
- Java反射—方法的反射、深入瞭解泛型Java反射泛型
- 一篇瞭解Java反射Java反射
- Java 泛型,你瞭解型別擦除嗎?Java泛型型別
- 你真的瞭解Java記憶體模型JMM嗎?Java記憶體模型
- ViewStub你真的瞭解嗎View
- 你真的瞭解mongoose嗎?Go
- 你真的瞭解 WebSocket 嗎?Web
- Java泛型用了這麼多年,你真的瞭解嗎?Java泛型
- JavaScript 你真的瞭解this指向嗎JavaScript
- 你真的瞭解前端路由嗎?前端路由
- 面試官:你瞭解Webpack嗎?面試Web
- 你真的瞭解RPC嗎?RPC
- 你真的瞭解URLEncode嗎?
- 你瞭解物聯網嗎
- 你真的瞭解“密碼”嗎?密碼
- 你真的瞭解nosql世界嗎?SQL
- 你真的瞭解python嗎?這篇文章帶你快速瞭解!Python
- 作為Java初學者,你瞭解Java的應用範圍嗎?Java
- 你必須瞭解的反射——反射來實現實體驗證反射
- Java中反射的概述及瞭解ClassLoaderJava反射
- 你有認真瞭解過自己的“Java物件”嗎? 渣男Java物件
- Java執行緒安全面試題,你真的瞭解嗎?Java執行緒面試題
- Java併發(7)- 你真的瞭解 ReentrantReadWriteLock 嗎?Java
- 你真的瞭解深度學習嗎?深度學習
- 你真的瞭解 Cookie 和 Session 嗎?CookieSession
- 你真的瞭解 Cookie 和 Session 嗎CookieSession
- 你真的瞭解npm-scripts嗎?NPM
- 你真的瞭解 Session 和 Cookie 嗎?SessionCookie
- 你真的瞭解js運算子嗎JS
- 面試官: 你瞭解前端路由嗎?面試前端路由
- 你真的瞭解HTTP快取嗎HTTP快取
- 你瞭解實時計算嗎?
- 你真的瞭解一段Java程式的生命史嗎Java
- Java 中的深複製和淺複製你瞭解嗎?Java
- 你真的瞭解 React 生命週期嗎React
- 阿里P7:你瞭解路由嗎?阿里路由