qileilove

          blog已經(jīng)轉(zhuǎn)移至github,大家請訪問 http://qaseven.github.io/

          深入理解Java:類加載機制及反射

           一、Java類加載機制
            1.概述
            Class文件由類裝載器裝載后,在JVM中將形成一份描述Class結(jié)構(gòu)的元信息對象,通過該元信息對象可以獲知Class的結(jié)構(gòu)信息:如構(gòu)造函數(shù),屬性和方法等,Java允許用戶借由這個Class相關的元信息對象間接調(diào)用Class對象的功能。
            虛擬機把描述類的數(shù)據(jù)從class文件加載到內(nèi)存,并對數(shù)據(jù)進行校驗,轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機直接使用的Java類型,這就是虛擬機的類加載機制。
            2.工作機制
            類裝載器就是尋找類的字節(jié)碼文件,并構(gòu)造出類在JVM內(nèi)部表示的對象組件。在Java中,類裝載器把一個類裝入JVM中,要經(jīng)過以下步驟:
            (1) 裝載:查找和導入Class文件;
            (2) 鏈接:把類的二進制數(shù)據(jù)合并到JRE中;
            (a)校驗:檢查載入Class文件數(shù)據(jù)的正確性;
            (b)準備:給類的靜態(tài)變量分配存儲空間;
            (c)解析:將符號引用轉(zhuǎn)成直接引用;
            (3) 初始化:對類的靜態(tài)變量,靜態(tài)代碼塊執(zhí)行初始化操作
            Java程序可以動態(tài)擴展是由運行期動態(tài)加載和動態(tài)鏈接實現(xiàn)的;比如:如果編寫一個使用接口的應用程序,可以等到運行時再指定其實際的實現(xiàn)(多態(tài)),解析過程有時候還可以在初始化之后執(zhí)行;比如:動態(tài)綁定(多態(tài));
            【類初始化】
            (1) 遇到new、getstatic、putstatic或invokestatic這4條字節(jié)碼指令時,如果類沒有進行過初始化,則需要先觸發(fā)其初始化。生成這4條指令的最常見的Java代碼場景是:使用new關鍵字實例化對象的時候,讀取或設置一個類的靜態(tài)字段(被final修飾、已在編譯期把結(jié)果放入常量池的靜態(tài)字段除外)的時候,以及調(diào)用一個類的靜態(tài)方法的時候。
            (2) 使用java.lang.reflect包的方法對類進行反射調(diào)用的時候,如果類沒有進行過初始化,則需要先觸發(fā)其初始化。
            (3) 當初始化一個類的時候,如果發(fā)現(xiàn)其父類還沒有進行過初始化,則需要先觸發(fā)其父類的初始化。
            (4)當虛擬機啟動時,用戶需要指定一個要執(zhí)行的主類(包含main()方法的那個類),虛擬機會先初始化這個主類。
            只有上述四種情況會觸發(fā)初始化,也稱為對一個類進行主動引用,除此以外,所有其他方式都不會觸發(fā)初始化,稱為被動引用
            代碼清單1
            上述代碼運行后,只會輸出【---SuperClass init】, 而不會輸出【SubClass init】,對于靜態(tài)字段,只有直接定義這個字段的類才會被初始化,因此,通過子類來調(diào)用父類的靜態(tài)字段,只會觸發(fā)父類的初始化,但是這是要看不同的虛擬機的不同實現(xiàn)。
            代碼清單2
            此處不會引起SuperClass的初始化,但是卻觸發(fā)了【[Ltest.SuperClass】的初始化,通過arr.toString()可以看出,對于用戶代碼來說,這不是一個合法的類名稱,它是由虛擬機自動生成的,直接繼承于Object的子類,創(chuàng)建動作由字節(jié)碼指令newarray觸發(fā),此時數(shù)組越界檢查也會伴隨數(shù)組對象的所有調(diào)用過程,越界檢查并不是封裝在數(shù)組元素訪問的類中,而是封裝在數(shù)組訪問的xaload,xastore字節(jié)碼指令中.
          代碼清單3
            對常量ConstClass.value 的引用實際都被轉(zhuǎn)化為NotInitialization類對自身常量池的引用,這兩個類被編譯成class后不存在任何聯(lián)系。
            【裝載】
            在裝載階段,虛擬機需要完成以下3件事情
            (1) 通過一個類的全限定名來獲取定義此類的二進制字節(jié)流
            (2) 將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)結(jié)構(gòu)
            (3) 在Java堆中生成一個代表這個類的java.lang.Class對象,作為方法區(qū)這些數(shù)據(jù)的訪問入口。
            虛擬機規(guī)范中并沒有準確說明二進制字節(jié)流應該從哪里獲取以及怎樣獲取,這里可以通過定義自己的類加載器去控制字節(jié)流的獲取方式。
            【驗證】
            虛擬機如果不檢查輸入的字節(jié)流,對其完全信任的話,很可能會因為載入了有害的字節(jié)流而導致系統(tǒng)奔潰。
            【準備】
            準備階段是正式為類變量分配并設置類變量初始值的階段,這些內(nèi)存都將在方法區(qū)中進行分配,需要說明的是:
            這時候進行內(nèi)存分配的僅包括類變量(被static修飾的變量),而不包括實例變量,實例變量將會在對象實例化時隨著對象一起分配在Java堆中;這里所說的初始值“通常情況”是數(shù)據(jù)類型的零值,假如:
            public static int value = 123;
            value在準備階段過后的初始值為0而不是123,而把value賦值的putstatic指令將在初始化階段才會被執(zhí)行
            二、類加載器與雙親委派模型
            類加載器
            (1) Bootstrap ClassLoader : 將存放于<JAVA_HOME>\lib目錄中的,或者被-Xbootclasspath參數(shù)所指定的路徑中的,并且是虛擬機識別的(僅按照文件名識別,如 rt.jar 名字不符合的類庫即使放在lib目錄中也不會被加載)類庫加載到虛擬機內(nèi)存中。啟動類加載器無法被Java程序直接引用
            (2) Extension ClassLoader : 將<JAVA_HOME>\lib\ext目錄下的,或者被java.ext.dirs系統(tǒng)變量所指定的路徑中的所有類庫加載。開發(fā)者可以直接使用擴展類加載器。
            (3) Application ClassLoader : 負責加載用戶類路徑(ClassPath)上所指定的類庫,開發(fā)者可直接使用。
            雙親委派模型
            工作過程:如果一個類加載器接收到了類加載的請求,它首先把這個請求委托給他的父類加載器去完成,每個層次的類加載器都是如此,因此所有的加載請求都應該傳送到頂層的啟動類加載器中,只有當父加載器反饋自己無法完成這個加載請求(它在搜索范圍中沒有找到所需的類)時,子加載器才會嘗試自己去加載。
            好處:java類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關系。例如類java.lang.Object,它存放在rt.jar中,無論哪個類加載器要加載這個類,最終都會委派給啟動類加載器進行加載,因此Object類在程序的各種類加載器環(huán)境中都是同一個類。相反,如果用戶自己寫了一個名為java.lang.Object的類,并放在程序的Classpath中,那系統(tǒng)中將會出現(xiàn)多個不同的Object類,java類型體系中最基礎的行為也無法保證,應用程序也會變得一片混亂。
            java.lang.ClassLoader中幾個最重要的方法:
            //加載指定名稱(包括包名)的二進制類型,供用戶調(diào)用的接口
            public Class<?> loadClass(String name);
            //加載指定名稱(包括包名)的二進制類型,同時指定是否解析(但是,這里的resolve參數(shù)不一定真正能達到解析的效果),供繼承用
            protected synchronized Class<?> loadClass(String name, boolean resolve);
            protected Class<?> findClass(String name)
            //定義類型,一般在findClass方法中讀取到對應字節(jié)碼后調(diào)用,可以看出不可繼承(說明:JVM已經(jīng)實現(xiàn)了對應的具體功能,解析對應的字節(jié)碼,產(chǎn)生對應的內(nèi)部數(shù)據(jù)結(jié)構(gòu)放置到方法區(qū),所以無需覆寫,直接調(diào)用就可以了)
            protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError{}
            如下是實現(xiàn)雙親委派模型的主要代碼:
            三、反射
            Reflection機制允許程序在正在執(zhí)行的過程中,利用Reflection APIs取得任何已知名稱的類的內(nèi)部信息,包括:package、 type parameters、 superclass、 implemented interfaces、 inner classes、 outer classes、 fields、 constructors、 methods、 modifiers等,并可以在執(zhí)行的過程中,動態(tài)生成instances、變更fields內(nèi)容或喚起methods。
            1、獲取構(gòu)造方法
            Class類提供了四個public方法,用于獲取某個類的構(gòu)造方法。
            Constructor getConstructor(Class[] params)
            根據(jù)構(gòu)造函數(shù)的參數(shù),返回一個具體的具有public屬性的構(gòu)造函數(shù)
            Constructor getConstructors()
            返回所有具有public屬性的構(gòu)造函數(shù)數(shù)組
            Constructor getDeclaredConstructor(Class[] params)
            根據(jù)構(gòu)造函數(shù)的參數(shù),返回一個具體的構(gòu)造函數(shù)(不分public和非public屬性)
            Constructor getDeclaredConstructors()
            返回該類中所有的構(gòu)造函數(shù)數(shù)組(不分public和非public屬性)
            2、獲取類的成員方法
            與獲取構(gòu)造方法的方式相同,存在四種獲取成員方法的方式。
            Method getMethod(String name, Class[] params)
            根據(jù)方法名和參數(shù),返回一個具體的具有public屬性的方法
            Method[] getMethods()
            返回所有具有public屬性的方法數(shù)組
            Method getDeclaredMethod(String name, Class[] params)
            根據(jù)方法名和參數(shù),返回一個具體的方法(不分public和非public屬性)
            Method[] getDeclaredMethods()
            返回該類中的所有的方法數(shù)組(不分public和非public屬性)
            3、獲取類的成員變量(成員屬性)
            存在四種獲取成員屬性的方法
            Field getField(String name)
            根據(jù)變量名,返回一個具體的具有public屬性的成員變量
            Field[] getFields()
            返回具有public屬性的成員變量的數(shù)組
            Field getDeclaredField(String name)
            根據(jù)變量名,返回一個成員變量(不分public和非public屬性)
            Field[] getDelcaredFields()
            返回所有成員變量組成的數(shù)組(不分public和非public屬性)

          posted on 2014-09-24 15:36 順其自然EVO 閱讀(176) 評論(0)  編輯  收藏 所屬分類: 測試學習專欄

          <2014年9月>
          31123456
          78910111213
          14151617181920
          21222324252627
          2829301234
          567891011

          導航

          統(tǒng)計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 成安县| 林口县| 清丰县| 厦门市| 清水河县| 宁波市| 平南县| 晋中市| 佛学| 共和县| 镇远县| 永兴县| 恭城| 金秀| 柘城县| 阳高县| 仁怀市| 京山县| 昭苏县| 南昌市| 乐昌市| 合作市| 高清| 西盟| 淮北市| 青河县| 永宁县| 锡林郭勒盟| 勐海县| 辉南县| 长岭县| 汽车| 庆阳市| 上饶市| 阜南县| 岐山县| 孝昌县| 呼和浩特市| 柏乡县| 定日县| 阳西县|