ch18 類加載機制與反射java
類的加載,鏈接和初始化數據庫
系統可能在第一次使用某個類時加載該類,也可能採用預加載機制來加載某個類數組
JVM和類緩存
一個Java程序就是一個Java虛擬機進程網絡
兩個JVM之間數據獨立,因此一個類的靜態屬性並不會跨虛擬機進程共享框架
類的加載spa
加載->鏈接->初始化代理
類加載是指將類的class文件讀入內存,併爲之建立一個java.lang.Class對象,當程序中使用任何類時,系統都將爲之創建一個java.lang.Class對象orm
系統中的全部類實際上也是實例,它們都是java.lang.Class的實例對象
類的加載由類加載器完成,類加載器一般由JVM提供,JVM提供的這些類加載器一般被稱爲系統類加載器。除此以外,開發者能夠經過繼承ClassLoader基類來建立本身的類加載器
類的來源:
本地文件系統class
JAR包
網絡
動態編譯Java文件
類的鏈接
驗證->準備->解析
類的初始化
在類的初始化階段,虛擬機負責對類進行初始化,主要就是對靜態Field進行初始化
Java初始化靜態Field:聲明時指定初始值或使用靜態初始化塊
JVM初始化一個類的步驟:
如未加載和鏈接,則加載並鏈接該類
如其直接父類未初始化,則先初始化其直接父類
若有初始化語句,則依次執行這些初始化語句
當執行步驟2時,系統對直接父類的初始化步驟也遵循此步驟1~3,依次迭代直到java.lang.Object類。當程序主動
使用任何一個類時,系統會保證該類以及全部父類(包括直接父類和間接父類)都被初始化
類初始化的時機
當Java程序首次經過下面6種方式使用某個類或者接口時,系統就會初始化該類或接口:
建立類的實例 (new,反射,反序列化)
調用某個類的靜態方法
訪問(讀寫)某個類或接口的靜態Field
使用反射方式強制建立某個類或接口對應的java.lang.Class對象,如Class.forName(「Person」)
初始化某個類的子類
直接使用java.exe運行某個主類
final型靜態Field在編譯時就能定下來,故不會觸發類初始化行爲
當使用ClassLoader類的loadClass()方法來加載某個類時,該方法只是加載該類,並不會執行該類的初始化。
使用Class的forName()靜態方法纔會致使強制初始化該類
類加載器
負責將.class文件加載到內存中,併爲之生成對應的java.lang.Class對象
類加載器簡介
在JVM中,一個類用其全限定類名和其類加載器做爲其惟一標識
JVM啓動時,會造成由3個類加載器組成的初始類加載器層次結構
Bootstrap ClassLoader:根類加載器(引導類加載器,負責加載Java的核心類)
Extension ClassLoader:擴展類加載器
System ClassLoader:系統類加載器(在JVM啓動時加載來自java命令的-classpath選項,java.class.path
系統屬性或者CLASSPATH環境變量所指定的JAR包和類路徑。程序均可以經過ClassLoader的靜態方法getSystemClassLoader()獲取系統類加載器)
類加載機制
類型:全盤負責,父類委託,緩存機制
層級關係:用戶類加載器->系統類加載器->擴展類加載器->根類加載器
getParent()方法
根類加載器不是由Java實現的
系統類加載器是AppClassLoader的實例,擴展類加載器是ExtClassLoader的實例,這兩個類都是URLClassLoader的實例
類加載class的8個步驟
建立並使用自定義的類加載器
JVM中除了根類加載器以外的全部類加載器都是ClassLoader子類的實例
經過擴展ClassLoader子類,重寫包含的方法能夠實現自定義類加載器
兩個關鍵方法:
loadClass(String name,boolean resolve)
findClass(String name)
推薦重寫findClass()方法,而不是loadClass()方法
loadClass()執行步驟:
用findLoadedClass(String)來檢查是否已經加載類,若是已經加載則返回;
在父類加載器上調用loadClass()方法。若是父類加載器爲null,則使用根類加載器來加載;
調用findClass(String)方法查找類。
核心方法Class defineClass(String name,byte[] b,int off,int len),該方法負責將指定類的字節碼文件(class文件)讀入字節數組byte[] b內,並把它轉換爲Class對象
URLClassLoader類
兩個構造器建立ClassLoader對象
經過loadClass()方法能夠加載指定類
應用:從文件系統加載MySQL驅動,並使用該驅動來獲取數據庫鏈接、
經過反射查看類信息
獲取Class對象
三種方式:
Class.forName(String),傳入包含完整包名的全限定類名稱
調用某個類的class屬性來獲取該類對應的Class對象
調用某個對象的getClass()方法
從Class中獲取信息
構造器
Field
方法
Annotation
使用反射生成並操做對象
建立對象
使用Class對象的newInstance()方法來建立該Class對象對應類的實例,要求該Class對象的對應類有默認構造器
使用Class對象獲取指定的Constructor對象,再調用Constuctor對象的newInstance()方法來建立該Class對象對應的實例。經過這種方式能夠選擇使用指定的構造器來建立實例
在不少JavaEE框架中都須要根據配置文件信息來建立Java對象,從配置文件讀取的只是某個類的字符串類名,程序須要根據該字符串來建立對應的實例,就必須使用反射
調用方法
geMethods()方法或者getMethod()方法獲取所有方法或者指定方法——分別返回Method對象數組或者Method對象
得到Method對象後,就可經過該Method的invoke(Object obj,Object...args)調用相應的方法,obj是執行該方法的主調,後面的args是執行該方法時傳入該方法的實參
權限問題:setAccessible(boolean flag),實現經過反射來調用private方法,private構造器和訪問private屬性
訪問屬性值
經過Class對象的getFields()或getField()方法能夠獲取該類所包括的所有Field或指定Field
操做數組
java.lang.reflect包下還提供了一個Array類,Array對象能夠表明全部的數組。程序能夠經過Array來動態建立數組,操做數組元素等
Object arr=Array.newInstance(String.class,10)
使用反射生成JDK動態代理
使用Proxy和InvocationHandler建立動態代理
Proxy提供了用於建立動態代理類和代理對象的靜態方法,它也是全部動態代理類的父類。若是在程序中爲一個或多個接口動態的生成實現類。就可使用Proxy來建立動態代理類;若是須要爲一個或多個接口動態地建立實例,也可使用Proxy來建立動態代理實例。
系統生成的每一個代理對象都有一個與之關聯的InvocationHandler對象,定義一個InvocationHandler實現類須要重寫invoke()方法——調用代理對象的全部方法時都會被替換成調用該invoke()方法,Object invoke(Object proxy,Method method,Object[] args)
動態代理和AOP
JDK動態代理只能爲接口建立動態代理
採用動態代理能夠很是靈活的實現解耦,一般都是爲指定的目標對象生成動態代理
這種動態代理在AOP(Aspect Orient Programming)中被稱爲AOP代理,AOP代理包含目標對象的所有方法,能夠替代目標對象。可是兩者存在差別:AOP代理裏的方法能夠在執行目標方法先後插入一些通用處理
反射和泛型
泛型和Class類
使用Class<T>泛型能夠避免強制類型轉換
對象工廠
使用反射來獲取泛型信息
經過指定類對應的Class對象,能夠得到類中的全部Field及其類型:
Class<?> a=f.getType()//普通類型
獲取泛型類型:Type gType=f.getGenericType()
ParameterizedType對象
getRawType():返回沒有泛型信息的原始類型
getActualTypeArguments():返回泛型參數的類型