java基礎-----(反射)

1.什麼是反射?

    引用百度百科的解釋。java反射機制是在運行狀態中,對於任意一個實體類,都能夠知道這個實體類的所有屬性和方法;對於任意一個對象,都能夠調用他的屬性和方法;這種動態獲取類信息以及調用對象方法的功能稱爲java語言的反射機制。

2.反射可以做什麼?

     我們知道反射機制允許程序在運行時取得任何一個已知名稱的class的內部信息,包括包括其modifiers(修飾符),fields(屬性),methods(方法)等,並可於運行時改變fields內容或調用methods。那麼我們便可以更靈活的編寫代碼,代碼可以在運行時裝配,無需在組件之間進行源代碼鏈接,降低代碼的耦合度;還有動態代理的實現等等;但是需要注意的是反射使用不當會造成很高的資源消耗!

3.Class類的使用

①.萬事萬物皆對象,(當然,基本數據類型,靜態成員不是面向對象(屬於類的)),所以我們創建的每一個類也都是對象,即類本身是java.lang.Class類的實例對象,但是這些對象都不需要new出來,因爲java.lang.Class類的構造方法是私有的。

②.任何一個類都是Class類的實例對象,這個實例對象有三種表示方式:(我們新建一個Student類)

Class c1 = Student.class;//實際告訴我們任何一個類都有一個隱含的靜態成員變量class(知道類名時用)
Class c2 = stu.getClass();//已知該類的對象通過getClass方法(知道對象時用)  
Class c3 = Class.forName("類的全名");//會有一個ClassNotFoundException異常

官網解釋說:c1,c2表示了Student類的類類型(class type),萬事萬物皆對象,類也是對象,是Class類的實例對象,這個對象我們稱爲該類的類類型。這裏有一點值得注意,當我們執行System.out.println(c1==c2);語句,結果返回的是true,這是爲什麼呢?原因是不管c1還是c2都代表了Student類的類類型。我們完全可以通過類的類類型創建該類的對象實例,即通過c1或c2創建Student的實例。Student stu = (Student)c1.newInstance();//前提是必須要有無參的構造方法,因爲該語句會去調用其無參構造方法。該語句會拋出異常。

4.動態加載類

①.編譯時加載類是靜態加載類

new 創建對象是靜態加載類,在編譯時刻就需要加載所有可用使用到的類,如果有一個用不了,那麼整個文件都無法通過編譯

 ②.運行時加載類是動態加載類      

    Class c =  Class.forName("類的全名"),不僅表示了類的類型,還表示了動態加載類,編譯不會報錯,在運行時纔會加載,使用接口標準能更方便動態加載類的實現。功能性的類儘量使用動態加載,而不用靜態加載。很多軟件比如QQ,360的在線升級,並不需要重新編譯文件,只是動態的加載新的東西

5.獲取方法信息

①基本的數據類型,void關鍵字都存在類類型

Class c1 =int.class;//int的類類型
Class c2 =String.class;//String類的類類型,可以理解爲編譯生成的那個String.class字節碼文件,
//當然,這並不是官方的說法
Class c3 =double.class;
Class c4 =Double.class;
Class c5 =void.class;

②Class類的基本API操作 

 /**
 * 打印類的信息,包括類的成員函數,成員變量
 * @param obj 該對象所屬類的信息
 */
publicstaticvoid printClassMessage(Object obj){
    //要獲取類的信息,首先要獲取類的類類型
    Class c = obj.getClass();//傳遞的是哪個子類的對象,c就是該子類的類類型
    //獲取類的名稱
    System.out.println("類的名稱是:"+c.getName());

    /*
     * Method類,方法的對象
     * 一個成員方法就是一個Method對象
     * getMethods()方法獲取的是所有的public的函數,包括父類繼承而來的
     * getDeclaredMethods()獲取的是多有該類自己聲明的方法,不問訪問權限
     */
    Method[] ms = c.getMethods();//c.getDeclaredMethods();
    for(int i =0; i < ms.length; i++){
        //得到方法的返回值類型的類類型
        Class retrunType = ms[i].getReturnType();
        System.out.print(retrunType.getName()+" ");
        //得到方法的名稱
        System.out.print(ms[i].getName()+"(");
        //獲取的參數類型--->得到的是參數列表的類型的類類型
        Class[] paraTypes = ms[i].getParameterTypes();
        for(Class class1 : paraTypes){
            System.out.print(class1.getName()+",");
        }
        System.out.println(")");
    }
}   

Class的API中還有很多其他的方法,可以得到interface、Package、Annotation等很多信息,具體使用請參考幫助手冊,本文就不在詳細講解。特別注意的一點是,如果你想得到一個類的信息,首先就要獲取該類的類類型。

6.獲取成員變量和構造函數信息

/**
 * 成員變量也是對象,是java.lang.reflect.Field這個類的的對象
 * Field類封裝了關於成員變量的操作
 * getFields()方法獲取的是所有public的成員變量的信息
 * getDeclareFields()方法獲取的是該類自己聲明的成員變量的信息
 */
Field[] fs = c.getDeclaredFields();
for(Field field : fs){
    //得到成員變量的類型的類類型
    Class fieldType = field.getType();
    String typeName = fieldType.getName();
    //得到成員變量的名稱
    String fieldName = field.getName();
    System.out.print(typeName+" "+fieldName);
}


/**
 * 構造函數也是對象
 * java.lang.Constructor中封裝了構造函數的信息
 * getConstructor()方法獲取所有的public的構造函數
 * getDeclaredConstructors得到所有的構造函數
 */
Constructor[] cs = c.getDeclaredConstructors();
for(Constructor constructor : cs){
    System.out.print(constructor.getName()+"(");
    //獲取構造函數的參數列表---》得到的是參數雷彪的類類型
     Class[] paramTypes = constructor.getParameterTypes();
     for(Class class1 : paramTypes){
        System.out.print(class1.getName()+",");
    }
    System.out.println(")");
}

7.方法反射的基本操作

7.1如何獲取某個方法

   方法的名稱和方法的參數列表才能唯一決定某個方法

   Method m = c.getDeclaredMethod("方法名",可變參數列表(參數類型.class))

7.2方法的反射操作

   m.invoke(對象,參數列表)

   方法如果沒有返回值,返回null,如果有返回值返回Object類型,然後再強制類型轉換爲原函數的返回值類型

8.通過反射了解泛型集合的本質

ArrayList list1 =newArrayList();
ArrayList<String> list2 =newArrayList<String>();

Class c1 = list1.getClass();
Class c2 = list2.getClass();

System.out.println(c1==c2);//結果爲true,爲什麼??

結果分析:因爲反射的操作都是編譯之後的操作,也就是運行時的操作,c1==c2返回true,說明編譯之後集合的泛型是去泛型化的。

       那麼我們就可以理解爲,Java集合中的泛型,是用於防止錯誤類型元素輸入的,比如在list2中我們add一個int,add(10)就會編譯報錯,那麼這個泛型就可以只在編譯階段有效,通過了編譯階段,泛型就不存在了。可以驗證,我們繞過編譯,用反射動態的在list2中add一個int是可以成功的,只是這時因爲list2中存儲了多個不同類型的數據(String型,和int型),就不能用for-each來遍歷了,會拋出類型轉換錯誤異常ClassCastException。

9.關於java類加載器內容的詳解

9.1類的加載

當程序要使用某個類時,如果該類還未被加載到內存中,則系統會通過加載,連接,初始化三步來實現對這個類進行初始化

·加載:

       就是指將class文件讀入內存,併爲之創建一個Class對象,任何類被使用時系統都會建立一個Class對象

·連接:

       驗證:確保被加載類的正確性

       準備:負責爲類的靜態成員分配內存,並設置默認初始化值

       解析:將類中的符號引用替換爲直接引用

·初始化:

        局部變量保存在棧區:必須手動初始化

        new 的對象保存在堆區:虛擬機會進行默認初始化,基本數據類型初始化值爲0,引用類型初始化值爲null

9.2類加載的時機(只加載一次)

以下時機僅表示第一次的時候

① 創建類的實例的時候

② 訪問類的靜態變量的時候

③ 調用類的靜態方法的時候

④ 使用反射方式來強制創建某個類或接口對應的java.lang.Class對象

⑤ 初始化某個類的子類的時候

⑥ 直接使用java.exe命令來運行某個主類

9.3類加載器

負責將.class文件加載到內存中,併爲之生成對應的Class對象

雖然我們在開發過程中不需要關心類加載機制,但是瞭解這個機制我們就能更好的理解程序的運行

9.4類加載器的組成

①Bootstrap ClassLoader 根類加載器

也被稱爲引導類加載器,負責Java核心類的加載,比如System類,在JDK中JRE的lib目錄下rt.jar文件中的類。

②Extension ClassLoader 擴展類加載器

負責JRE的擴展目錄中jar包的加載,在JDK中JRE的lib目錄下ext目錄。

③System ClassLoader 系統類加載器

負責在JVM啓動時加載來自java命令的class文件,以及classpath環境變量所指定的jar包和類路徑,主要是我們開發者自己寫的類。

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章