作為Android開發你必須知道的Java反射機制

那個人發表於2017-10-12

一.什麼是反射機制?

JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意方法和屬性;這種動態獲取資訊以及動態呼叫物件方法的功能稱為java語言的反射機制.


二.我們需要知道哪些東西?

反射中我們最常接觸到的類大致有一下幾個:

  • Class:
    Class物件是一個特殊的物件,是用來建立其它物件的物件(這裡的其他物件就是指:java類的例項.其實Class物件就是java類編譯後生成的.class檔案,它包含了與類有關的資訊.
  • Field:
    Field欄位提供有關和動態訪問的資訊,類或介面的單個欄位。反射的欄位可能
    是類(靜態)欄位或例項欄位.
  • Method:
    Method方法提供了關於單個方法的資訊和訪問在類或介面上。反射的方法可能是類方法或者一個例項方法(包括一個抽象的方法).
  • Constructor:
    Constructor提供了關於某類的構造方法的所需資訊.

三.我們要掌握哪些方法呢?

  • 獲取Class物件的幾個方法:
    (1) 類名.class;
    (2) Class.forName(類路徑);
    (3) 物件.getClass();
    三個方法的區別是方法一不執行靜態塊和動態構造塊,方法二執行靜態塊、不執行動態構造塊,方法三需要建立物件,靜態塊和動態構造塊均會執行.
  • 獲取類屬性的幾個方法:
    (1) persionClass.getField(方法的名稱);
    (2) persionClass.getFields();
    (3) persionClass.getDeclaredField(方法的名稱);
    (4) persionClass.getDeclaredFields();複製程式碼
    裡面getField和getFields只能獲取到public修飾的欄位,getDeclaredField和getDeclaredFields可以獲取到所有的欄位.其中以s結尾的是獲取所有的欄位返回的是一個Field 的陣列.
  • 獲取類方法的幾個方法:
    (1) persionClass.getMethod(方法的名稱);
    (2) persionClass.getMethods();
    (3) persionClass.getDeclaredMethod(方法的名稱);
    (4) persionClass.getDeclaredMethods();複製程式碼
    getMethod和getMethods只能獲取到public修飾的方法名稱,getDeclaredMethod和getDeclaredMethods可以獲取到所有的方法.
  • 獲取建構函式的幾個方法:
    (1) persionClass.getConstructor(可變引數集合);
    (2) persionClass.getConstructors();
    (3) persionClass.getDeclaredConstructor(方法的名稱);
    (4) persionClass.getDeclaredConstructors();複製程式碼
    getConstructor和getConstructors只能獲取到public修飾的方法名稱,getDeclaredConstructor和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基礎的同學學起來還是比較容易的.

    歡迎大家的觀看並提出寶貴的意見

相關文章