一.什麼是反射機制?
JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意方法和屬性;這種動態獲取資訊以及動態呼叫物件方法的功能稱為java語言的反射機制.
二.我們需要知道哪些東西?
反射中我們最常接觸到的類大致有一下幾個:
- Class:
Class物件是一個特殊的物件,是用來建立其它物件的物件(這裡的其他物件就是指:java類的例項.其實Class物件就是java類編譯後生成的.class檔案,它包含了與類有關的資訊. - Field:
Field欄位提供有關和動態訪問的資訊,類或介面的單個欄位。反射的欄位可能
是類(靜態)欄位或例項欄位. - Method:
Method方法提供了關於單個方法的資訊和訪問在類或介面上。反射的方法可能是類方法或者一個例項方法(包括一個抽象的方法). - Constructor:
Constructor提供了關於某類的構造方法的所需資訊.
三.我們要掌握哪些方法呢?
- 獲取Class物件的幾個方法:
(1) 類名.class;
(2) Class.forName(類路徑);
(3) 物件.getClass();
三個方法的區別是方法一不執行靜態塊和動態構造塊,方法二執行靜態塊、不執行動態構造塊,方法三需要建立物件,靜態塊和動態構造塊均會執行. - 獲取類屬性的幾個方法:
裡面getField和getFields只能獲取到public修飾的欄位,getDeclaredField和getDeclaredFields可以獲取到所有的欄位.其中以s結尾的是獲取所有的欄位返回的是一個Field 的陣列.(1) persionClass.getField(方法的名稱); (2) persionClass.getFields(); (3) persionClass.getDeclaredField(方法的名稱); (4) persionClass.getDeclaredFields();複製程式碼
- 獲取類方法的幾個方法:
getMethod和getMethods只能獲取到public修飾的方法名稱,getDeclaredMethod和getDeclaredMethods可以獲取到所有的方法.(1) persionClass.getMethod(方法的名稱); (2) persionClass.getMethods(); (3) persionClass.getDeclaredMethod(方法的名稱); (4) persionClass.getDeclaredMethods();複製程式碼
- 獲取建構函式的幾個方法:
getConstructor和getConstructors只能獲取到public修飾的方法名稱,getDeclaredConstructor和getDeclaredConstructors可以獲取到所有的方法.(1) persionClass.getConstructor(可變引數集合); (2) persionClass.getConstructors(); (3) persionClass.getDeclaredConstructor(方法的名稱); (4) persionClass.getDeclaredConstructors();複製程式碼
建立物件的方法:
(1) Constructor.newInstance(可變引數); eg:Persion p = (Persion)constructor.newInstance("1");複製程式碼
- 設定屬性值:
(1) field.set(Objkect obj,Object value); (2) field.setInt(Objkect obj,int value); ... (n) file.setLong(Objkect obj,long value);複製程式碼
獲取屬性物件:
(1) field.get(Object); eg:Person p = (Persion)filed.get(物件);複製程式碼
這裡要注意瞭如果欄位的修飾符是Static的話裡面可以傳入任何值包括null;
呼叫方法:
這裡必須注意的是當操作的物件用private修飾的時候需要用method.setAccessible(true)來設定可以訪問到.然後呼叫method.invoke(Object obj,引數),這個Object的物件必須是該類的物件.不是所謂的類物件.
四.Android能用到的地方
- 修改TabLayout的下劃線的長度.
對於TabLayout的使用這裡就不必多說了,系統只提供了修改下劃線的高度和顏色的方法,並沒有修改長度的方法.這裡就要用到反射區獲取TabLayout內部控制長度的方法.這裡只能通過設定每個Tab的Margin來控制下劃線的寬度,有可能出現Tab的文字被擠壓的情況,只能將就使用了.程式碼如下:Class<? extends TabLayout> tabClass = tabLayout.getClass(); try { Field mTabStrip = tabClass.getDeclaredField("mTabStrip"); mTabStrip.setAccessible(true); LinearLayout linearLayout = (LinearLayout) mTabStrip.get(tabLayout); for (int i = 0; i < linearLayout.getChildCount(); i++) { View child = linearLayout.getChildAt(i); child.setPadding(0,0,0,0); LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) child.getLayoutParams(); // layoutParams.width = 300; layoutParams.leftMargin = 150; layoutParams.rightMargin = 150; child.setLayoutParams(layoutParams); child.invalidate(); } } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); }複製程式碼
控制Toast的顯示時間.
Toast內部類TN的設定顯示時間的程式碼:mParams.hideTimeoutMilliseconds = mDuration == Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT; ... /** * schedule handleHide into the right thread */ @Override public void hide() { if (localLOGV) Log.v(TAG, "HIDE: " + this); mHandler.obtainMessage(HIDE).sendToTarget(); }複製程式碼
這裡系統自帶的Toast只給了我們兩個時間的選擇SHORT_DURATION_TIMEOUT和LONG_DURATION_TIMEOUT其他的我們沒法改變.還好系統提供了hide方法不過在外面我們是訪問不到,這裡我們也可以用到反射,大部分的操作都是Toast的內部類TN來完成的.首先獲取到Toast的class物件,Toast內部含有內部類的欄位(final TN mTN),這樣我們可以獲取到內部類的物件,然後再通過內部類的Class物件來獲取內部類裡面的hide()方法.程式碼如下:
try{ Class<Toast> toastClass = Toast.class; Field mTN = toastClass.getDeclaredField("mTN"); //獲取修飾符型別 toastClass.getModifiers(); mTN.setAccessible(true); Object o = mTN.get(toast); Class<?> aClass = o.getClass(); Method hide = aClass.getDeclaredMethod("hide"); hide.setAccessible(true); hide.invoke(o); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); }複製程式碼
五.結束語
反射對我們來說還是挺重要的,我們不需要多麼的精通只需要在使用的時候不會那麼陌生.對於有java基礎的同學學起來還是比較容易的.
歡迎大家的觀看並提出寶貴的意見