Java 反射簡單使用以及常見問題

什麼是反射?
一句話來說,反射機制指的是程序在運行時能夠獲取自身的信息。
正式點說 :JAVA反射機制是在運行狀態中,對於任意一個類,都能夠獲取這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取類信息以及動態調用對象內容就稱爲java語言的反射機制

反射原理
Java語言在編譯之後會生成一個class文件(Java的源代碼會被編譯成.class文件字節碼),反射就是通過字節碼文件找到其類中的方法和屬性等。

反射作用

  • 在運行時判斷任意一個對象所屬的類;

  • 在運行時構造任意一個類的對象;

  • 在運行時判斷任意一個類所具有的成員變量和方法;

  • 在運行時調用任意一個對象的方法;

反射常用的類

類名 作用
Class 代表運行的 Java 應用程序中的類和接口
Field 獲取類的成員變量
Method 獲取類,參數類型,方法註解,參數註解,方法返回值等信息
Constructor 代表類的構造方法

示例1
一般我們對類進行實例化的時候(假設有個User類)

User A=new User();
A.doSomething();

而利用反射創建對象方法

         //1.Class<?> clazz=user.getClass();
        //2.Class clazz=User.class;
      	// 獲取類的 Class 對象實例
        Class clazz=Class.forName("bean.User");
        Constructor constructor=clazz.getConstructor();
        Object object=constructor.newInstance();

對象object就是User類的實例化對象。

##示例2
假設我現在知道了一個對象,我想調用它類裏面的方法,也可以通過反射完成

public class User {

    private String name;
    private int age;

    private void doSomething(){
        System.out.println("doing something!");
    }

    private void Say(String say){
        System.out.println(this.name+" says:"+say);
    }
    public User(){

    }
    public User(String name,int age){
        this.name=name;
        this.age=age;
    }

    public static void main(String[] args)throws Exception {
    	//常規做法
        User user=new User("小明",15);
        user.Say("你好");
        //反射獲取無參數構造器創建對象
        Class clazz1=user.getClass();
        User user1=(User)clazz1.getConstructor().newInstance();
        user1.doSomething();

        //反射獲取有參數構造器創建對象
        Class clazz2=User.class;
        User user2=(User)clazz2.getConstructor(String.class,int.class).newInstance("小紅",14);
        user2.Say("hello");

        //調用某一個私有方法
        Method doSomething=clazz2.getDeclaredMethod("doSomething");
        doSomething.invoke(user2);

    }

輸出分別爲:

  • 小明 says:你好
  • doing something!
  • 小紅 says:hello
  • doing something!

好像有點脫褲子放屁,直接調用不行嗎,非要反射。
但是在某些場景反射非常有利,尤其我們事先不確定會調用那個函數。比如在web開發中,可以根據用戶傳過來的參數調用某個對應的方法,這樣動態調用將會大量減少代碼量。
##總結一下常用的

  • 獲取Class對象的三種方法。
    1. 通過全限定類名加載(比如我bean包下的User類)
Class clazz=Class.forName("bean.User");

2.使用.Class

Class clazz=User.Class;

3.通過已有對象調用getClass()獲取

Class clazz=user.getClass();
  • 使用Class對象獲取構造函數實例化對象
    通過 Class 對象的 newInstance() 方法、通過 Constructor 對象的 newInstance() 方法
Class clz = User.class;
User user= (User)clz.newInstance();
//-----------------
Class clz = User.class;
Constructor constructor = clz.getConstructor();
User user= (User)constructor.newInstance();
  • 獲取對象的全部方法存於數組(上面的例子是通過參數獲取對應的一個方法)
  //創建class對象
   Class<?> clazz = Class.forName(ClassName);
   // 返回聲明的所有方法,包括公共、保護、默認(包)訪問和私有方法,但不包括繼承的方法。 
   Method[] getDeclaredMethods()//返回可被訪問的公共方法存於一個Method數組,我們可以遍歷數組    
   Method method[] = clazz.getMethods();
  • 獲取類的全部字段,存於一個數組中()
		Class<?> clazz = Class.forName(classname);
       // 取得本類已聲明的所有字段,包括私有的、保護的存於一個Filed數組
        Field[] field = clazz.getDeclaredFields();
      // 取得本類中可訪問的所有公共字段存於一個Filed數組
        Field[] filed1 = clazz.getFields();
  • 操作對象的私有屬性
Class<?> clazz = Class.forName(classname);

//返回一個 Field 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明字段。 包括公共、私有、保護的字段。
  Field field = clazz.getDeclaredField(字段名);

//禁用Java權限修飾符的作用,無視方法權限限制進行訪問,包括private
 field.setAccessible(true);

// void set(Object obj, Object value) 將指定對象變量上此 Field 對象表示的字段設置爲指定的新值。           
  field.set(該類的一個對象, 字段值);
  • 最常用的使用invoke()調用類的方法(像user那個例子一樣)
 //調用某一個私有方法
Method doSomething=clazz2.getDeclaredMethod("doSomething");
doSomething.invoke(user2);
/*--------------------------*/
//獲取一個公有函數
  Method method = clazz.getMethod(方法名,參數類型);
//調用具體某個實例對象的這個公有方法
  method.invoke(實例對象,參數值);

/*--------------------------*/
//獲取一個私有函數
Method privateMethod=class.getDeclaredMethod(函數名,參數類型);

//禁用Java權限限定符的作用,使私有函數可訪問
 privateMethod.setAccessible(true);

//調用具體實例對象的這個方法
 privateMethod.invoke(實例對象,參數);

還有很多用法具體查API即可,運用和上面類似

##反射常見問題

  • 哪裏會應用到反射?
  1. JDBC中,利用反射動態加載了數據庫驅動程序。Class.forName(“com.mysql.jdbc.Driver”)
  2. Web服務器中利用反射調用了Sevlet的服務方法。
  3. Eclispe等開發工具利用反射動態刨析對象的類型與結構,動態提示對象的屬性和方法。
    4.反射是框架的靈活,很多框架都用到反射機制,注入屬性,調用方法,如Spring;在動態代理模式中也常用到反射
  • 反射優缺點

缺點:
1.反射包括了一些動態類型,所以JVM無法對這些代碼進行優化。因此,反射操作的效率要比那些非反射操作低得多。我們應該避免在經常被 執行的代碼或對性能要求很高的程序中使用反射。
2.內部暴露問題,使用反射會模糊程序內內部邏輯。
3.損失了編譯時類型檢測的優勢,包括異常檢測。如果反射調用了不存在的或不可訪問的方法,在它運行時纔會失敗,產生錯誤。
優點:
1.可以動態執行,在運行期間根據業務功能動態執行方法、訪問屬性,最大限度發揮了java的靈活性。

  • 反射的原理及應用

Java語言在編譯之後會生成一個class文件(Java的源代碼會被編譯成.class文件字節碼),然後通過使用java.lang.reflect包下的API以達到各種動態需求反射就是通過字節碼文件找到其類中的方法和屬性等。

  • 如何獲取Class對象?
  • 上面提到的三種方法,最常用就是Class.forName();
  • 如何操作類的成員變量?

Field類的常用方法。
// 取得本類已聲明的所有字段,包括私有的、保護的
Field[] field = clazz.getDeclaredFields();
// 取得本類中可訪問的所有公共字段
Field[] filed1 = clazz.getFields();

  • 如何操作類的方法(Method)?

Method類的常用方法
getDeclaredMethod()
getDeclaredMethods()
…等等

  • 如何利用反射機制來訪問一個類的私有成員?

在使用反射機制訪問私有成員的時候,他們的可訪問性是爲false的。需要調用setAccessible(true)方法,把原本不可訪問的私有成員變爲可以訪問以後,才能進行成功的訪問或調用。

  • 何利用反射來覆蓋數據對象的toString()方法?

Field的使用 對於數據類型的類的toString()方法,覆蓋它的基本思路,主要有以下幾點:
1.通過getDeclaredFields()方法得到所有的Field對象。
2.把上一步得到的Field對象數組進行遍歷。
3.每一次循環加上字段名和字段值。
4.返回循環疊加以後的字符串結果。

發佈了16 篇原創文章 · 獲贊 11 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章