1. 程式人生 > >深入理解java的反射機制(轉載)

深入理解java的反射機制(轉載)

今天將從以下4方面來系統的學習一下java的反射機制:

  • java反射是什麼

  • java反射(Reflection)底層實現原理

  • java反射的簡單演示

  • java反射的應用場景

1,java反射是什麼

首先大家應該先了解兩個概念,編譯期執行期,編譯期就是編譯器幫你把原始碼翻譯成機器能識別的程式碼,比如編譯器把java程式碼編譯成jvm識別的位元組碼檔案,而執行期指的是將可執行檔案交給作業系統去執行,JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意方法和屬性;這種動態獲取資訊以及動態呼叫物件方法的功能稱為java語言的反射機制

2,java反射(Reflection)的底層實現原理

眾所周知Java有個Object 類,是所有Java 類的繼承根源,其內聲明瞭數個應該在所有Java 類中被改寫的方法:hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一個Class 物件

而這個Class 類十分特殊。它和一般類一樣繼承自Object,當一個class被載入,或當載入器(class loader)的defineClass()被JVM呼叫,JVM 便自動產生一個Class 物件。

而Class物件是java反射故事起源。Class類提供了大量的例項方法來獲取該Class物件所對應的詳細資訊,我們只是做簡單的介紹,詳細請參考JDK文件,這邊提供一個線上的中文文件給大家如下:

http://tool.oschina.net/apidocs/apidoc?api=jdk-zh

這邊列了下Class類其中的很少yibufen方法,

獲取公共構造器 getConstructors()
獲取所有構造器 getDeclaredConstructors
獲取包含的方法 getMethod()
獲取包含的屬性 getField(String name)
獲取內部類 getDeclaredClasses()
獲取外部類 getDeclaringClass()
獲取所實現的介面 getInterfaces()
獲取修飾符 getModifiers()
獲取所在包 getPackage()
獲取類名包含包路徑  getName()
類名不包含包路徑  getSimpleName()

3,java反射的簡單演示

上面介紹了下java反射的實現機制,接下來我主要來演示一下反射的使用

首先定義一個user物件類如下:

/**
 * 定義一個使用者物件
 *
 * @author zhangqh
 * @date 2018年4月24日
 */
public class User implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    /**
     * 使用者id
     */
    private Integer userId;
    /**
     * 使用者名稱稱
     */
    private String userName;

    /**
     * 公共元素
     */
    public String age;

    private User(Integer userId){}

    public User(){}
    public User(Integer userId, String userName) {
        super();
        this.userId = userId;
        this.userName = userName;
    }
    /**
     * @return the userId
     */
    public Integer getUserId() {
        return userId;
    }
    /**
     * @param userId the userId to set
     */
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    /**
     * @return the userName
     */
    public String getUserName() {
        return userName;
    }
    /**
     * @param userName the userName to set
     */
    public void setUserName(String userName) {
        this.userName = userName;
    }

}

測試反射類:

/**
 * 測試反射類
 *
 * @author zhangqh
 * @date 2018年4月25日
 */
public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        // 第一種方法 直接通過類名獲取class (一般不會這麼用,因為例項都獲取到了沒必要多此一舉)
        User user = new User();
        Class class1 = user.getClass();
        System.out.println("我是方法1反射處理的類 = "+class1);
        System.out.println("我是方法1反射處理的類的父類 = "+class1.getSuperclass());
        System.out.println("----------------------------------------------------");
        // 第二種辦法通過類名的方法獲取class 需要增加對應的類引用
        Class class2 = User.class;
        System.out.println("我是方法2反射處理的類 = "+class2);
        System.out.println("我是方法2反射處理的類的父類 = "+class2.getSuperclass());
        System.out.println("----------------------------------------------------");
        // 第三種辦法通過全類名獲取,用的比較多,也推薦使用這種方式
        Class class3 = Class.forName ("com.zhang.reflection.User");
        System.out.println("我是方法3反射處理的類 = "+class3);
        System.out.println("我是方法3反射處理的類的父類 = "+class3.getSuperclass());
        System.out.println("----------------------------------------------------");
        // 反射獲取User物件的公共構造器
        Constructor<User>[] constructors = class3.getConstructors();
        for(int i=0;i<constructors.length;i++){
            System.out.println("我是公共構造器"+(i+1)+" = "+constructors[i]);
        }
        System.out.println("----------------------------------------------------");
        //反射獲取User物件的所有構造器
        Constructor<User>[] dconstructors = class3.getDeclaredConstructors();
        for(int i=0;i<dconstructors.length;i++){
            System.out.println("我是所有構造器"+(i+1)+" = "+dconstructors[i]);
        }
        System.out.println("----------------------------------------------------");
        // 反射獲取User物件的 Method 物件的陣列,這些物件反映此 Class 物件所表示的類或介面(包括那些由該類或介面宣告的以及從超類和超介面繼承的那些的類或介面)的公共 member 方法
        Method[]  methods = class3.getMethods();
        for(int i=0;i<methods.length;i++){
            System.out.println("我是公共方法"+(i+1)+" = "+methods[i]);
        }
        System.out.println("----------------------------------------------------");
        // 反射獲取User物件的 Method 物件的一個數組,這些物件反映此 Class 物件表示的類或介面宣告的所有方法,包括公共、保護、預設(包)訪問和私有方法,但不包括繼承的方法
        Method[]  dmethods = class3.getDeclaredMethods();
        for(int i=0;i<dmethods.length;i++){
            System.out.println("我是所有的方法"+(i+1)+" = "+dmethods[i]);
        }
        System.out.println("----------------------------------------------------");
        // 獲取包含某些 Field 物件的陣列,這些物件反映此 Class 物件所表示的類或介面的所有可訪問公共欄位
        Field[] fields = class3.getFields();
        for(int i=0;i<fields.length;i++){
            System.out.println("我是公共的field"+(i+1)+" = "+fields[i]);
        }
        System.out.println("----------------------------------------------------");
        Field[] dfields = class3.getDeclaredFields();
        for(int i=0;i<dfields.length;i++){
            System.out.println("我是所有的field"+(i+1)+" = "+dfields[i]);
        }
        System.out.println("----------------------------------------------------");
        String sname = class3.getSimpleName();
        System.out.println("我是類名不包含包路徑 = "+sname);
        System.out.println("----------------------------------------------------");
        String name = class3.getName();
        System.out.println("我是類名包含包路徑 = "+name);
        System.out.println("----------------------------------------------------");
        Package packA=class3.getPackage();
        System.out.println("我是類的包路徑 = "+packA.getName());
        System.out.println("----------------------------------------------------");
        Class<?>[] interfaces=class3.getInterfaces();
        for(int i=0;i<interfaces.length;i++){
            System.out.println("我是實現的介面"+(i+1)+" = "+interfaces[i]);
        }
        System.out.println("----------------------------------------------------");
        // 返回此類或介面以整數編碼的 Java 語言修飾符。修飾符由 Java 虛擬機器的 public、 protected、 private、 final、 static、 abstract 和 interface 對應的常量組成;
        // 它們應當使用 Modifier 類的方法來解碼
        Integer modefiers = class3.getModifiers();
        System.out.println("我是類的modefiers = "+modefiers);
    }
}

以上程式碼中有詳細的程式碼註釋,這邊就不過多的解釋了

4,java反射的應用場景

a,註解的使用

不知道大家是否有看過之前寫的深入理解java註解的實現原理它的底層實現就是java反射,主要有如下方法:

getAnnotations()
getAnnotation(Class annotationClass) 
getDeclaredAnnotations() 
isAnnotation() 
isAnnotationPresent(Class annotationClass)

b,編寫基礎框架

有一句話這麼說來著:反射機制是很多Java框架的基石,經典的就是在xml檔案或者properties裡面寫好了配置,然後在Java類裡面解析xml或properties裡面的內容,得到一個字串,然後用反射機制,根據這個字串獲得某個類的Class例項,這樣就可以動態配置一些東西,spring,Hibernate底層都有類似的實現

c,其他在編碼階段不知道那個類名,要在執行期從配置檔案讀取類名配置

如下:這段程式碼想必大家肯定都有寫過,這個資料庫的連線驅動類就是編譯的時候不知道你到底是用的mysql,oracle還是其他資料庫,而是由執行期動態載入的

// 1.載入驅動程式
        Class.forName("com.mysql.jdbc.Driver");
        // 2.獲得資料庫的連線
        Connection conn = DriverManager.getConnection(URL, NAME, PASSWORD);
        // 3.通過資料庫的連線操作資料庫,實現增刪改查
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt
                .executeQuery("select user_name,age from user");
        while (rs.next()) {// 如果物件中有資料,就會迴圈打印出來
            System.out.println(rs.getString("user_name") + ","
                    + rs.getInt("age"));
        }

注:以上介紹了反射的應用場景,程式猿開發業務程式碼中應儘量少用反射,一個是程式碼可讀性不是特別好,第二是反射需要執行期jvm去重新解析效能上也沒有直接使用好,唯一比較合理的地方是業務中需要用到AOP可以大大簡化業務程式碼建議使用

以上是今天文章的所有內容,歡迎大家吐槽