(給ImportNew加星標(biāo),提高Java技能) 轉(zhuǎn)自:博客園,作者:rocomp 鏈接:www.cnblogs.com/rocomp/p/4781987.html
理解反射對學(xué)習(xí)Java框架有很大的幫助,如Spring框架的核心就是使用Java反射實(shí)現(xiàn)的,而且對做一些Java底層的操作會(huì)很有幫助。 一、Class類的使用 1、萬事萬物皆對象,(當(dāng)然,基本數(shù)據(jù)類型,靜態(tài)成員不是面向?qū)ο螅▽儆陬惖模?,所以我們?chuàng)建的每一個(gè)類也都是對象,即類本身是java.lang.Class類的實(shí)例對象,但是這些對象都不需要new出來,因?yàn)閖ava.lang.Class類的構(gòu)造方法是私有的 2、任何一個(gè)類都是Class類的實(shí)例對象,這個(gè)實(shí)例對象有三種表示方式:(我們新建一個(gè)Student類) Class c1 = Student.class;//實(shí)際告訴我們?nèi)魏我粋€(gè)類都有一個(gè)隱含的靜態(tài)成員變量class(知道類名時(shí)用)
Class c2 = stu.getClass();//已知該類的對象通過getClass方法(知道對象時(shí)用) Class c3 = Class.forName('類的全名');//會(huì)有一個(gè)ClassNotFoundException異常
官網(wǎng)解釋說:c1,c2表示了Student類的類類型()class type),萬事萬物皆對象,類也是對象,是Class類的實(shí)例對象,這個(gè)對象我們成為該類的類類型(有點(diǎn)亂,但是慢慢捋一下還是能理解的) 這里有一點(diǎn)值得注意,當(dāng)我們執(zhí)行System.out.println(c1==c2);語句,結(jié)果返回的是true,這是為什么呢?原因是不管c1還是c2都代表了Student類的類類型,一個(gè)類可能是Class類的一個(gè)實(shí)例對象。 我們完全可以通過類的類類型創(chuàng)建該類的對象實(shí)例,即通過c1或c2創(chuàng)建Student的實(shí)例。 Student stu = (Student)c1.newInstance();//前提是必須要有無參的構(gòu)造方法,因?yàn)樵撜Z句會(huì)去調(diào)用其無參構(gòu)造方法。該語句會(huì)拋出異常。 二、動(dòng)態(tài)加載類 編譯時(shí)加載類是靜態(tài)加載類, new 創(chuàng)建對象是靜態(tài)加載類,在編譯時(shí)刻就需要加載所有可用使用到的類,如果有一個(gè)用不了,那么整個(gè)文件都無法通過編譯 運(yùn)行時(shí)加載類是動(dòng)態(tài)加載類 Class c = Class.forName('類的全名'),不僅表示了類的類型,還表示了動(dòng)態(tài)加載類,編譯不會(huì)報(bào)錯(cuò),在運(yùn)行時(shí)才會(huì)加載,使用接口標(biāo)準(zhǔn)能更方便動(dòng)態(tài)加載類的實(shí)現(xiàn)。功能性的類盡量使用動(dòng)態(tài)加載,而不用靜態(tài)加載。
很多軟件比如QQ,360的在線升級,并不需要重新編譯文件,只是動(dòng)態(tài)的加載新的東西 三、獲取方法信息 1、基本的數(shù)據(jù)類型,void關(guān)鍵字都存在類類型 Class c1 =int.class;//int的類類型 Class c2 =String.class;//String類的類類型,可以理解為編譯生成的那個(gè)String.class字節(jié)碼文件, //當(dāng)然,這并不是官方的說法 Class c3 =double.class; Class c4 =Double.class; Class c5 =void.class;
2、Class類的基本API操作 /** * 打印類的信息,包括類的成員函數(shù),成員變量 * @param obj 該對象所屬類的信息 */ publicstaticvoid printClassMessage(Object obj){ //要獲取類的信息,首先要獲取類的類類型 Class c = obj.getClass();//傳遞的是哪個(gè)子類的對象,c就是該子類的類類型 //獲取類的名稱 System.out.println('累的名稱是:' c.getName());
/* * Method類,方法的對象 * 一個(gè)成員方法就是一個(gè)Method對象 * getMethods()方法獲取的是所有的public的函數(shù),包括父類繼承而來的 * getDeclaredMethods()獲取的是多有該類自己聲明的方法,不問訪問權(quán)限 */ 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() '('); //獲取的參數(shù)類型--->得到的是參數(shù)列表的類型的類類型 Class[] paraTypes = ms[i].getParameterTypes(); for(Class class1 : paraTypes){ System.out.print(class1.getName() ','); } System.out.println(')'); } }
Class的API中還有很多其他的方法,可以得到interface、Package、Annotation等很多信息,具體使用請參考幫助手冊,本文就不在詳細(xì)講解。特別注意的一點(diǎn)是,如果你想得到一個(gè)類的信息,首先就要獲取該類的類類型。 四、獲取成員變量構(gòu)造函數(shù)信息 /** * 成員變量也是對象,是java.lang.reflect.Field這個(gè)類的的對象 * Field類封裝了關(guān)于成員變量的操作 * 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); }
/** * 構(gòu)造函數(shù)也是對象 * java.lang.Constructor中封裝了構(gòu)造函數(shù)的信息 * getConstructor()方法獲取所有的public的構(gòu)造函數(shù) * getDeclaredConstructors得到所有的構(gòu)造函數(shù) */ Constructor[] cs = c.getDeclaredConstructors(); for(Constructor constructor : cs){ System.out.print(constructor.getName() '('); //獲取構(gòu)造函數(shù)的參數(shù)列表---》得到的是參數(shù)雷彪的類類型 Class[] paramTypes = constructor.getParameterTypes(); for(Class class1 : paramTypes){ System.out.print(class1.getName() ','); } System.out.println(')'); }
五、方法反射的基本操作 如何獲取某個(gè)方法 方法的名稱和方法的參數(shù)列表才能唯一決定某個(gè)方法 Method m = c.getDeclaredMethod('方法名',可變參數(shù)列表(參數(shù)類型.class)) 方法的反射操作 m.invoke(對象,參數(shù)列表) 方法如果沒有返回值,返回null,如果有返回值返回Object類型,然后再強(qiáng)制類型轉(zhuǎn)換為原函數(shù)的返回值類型
六、通過反射了解集合泛型的本質(zhì) ArrayList list1 =newArrayList(); ArrayList<String> list2 =newArrayList<String>();
Class c1 = list1.getClass(); Class c2 = list2.getClass();
System.out.println(c1==c2);//結(jié)果為true,為什么??
結(jié)果分析:因?yàn)榉瓷涞牟僮鞫际蔷幾g之后的操作,也就是運(yùn)行時(shí)的操作,c1==c2返回true,說明編譯之后集合的泛型是去泛型化的。 那么我們就可以理解為,Java集合中的泛型,是用于防止錯(cuò)誤類型元素輸入的,比如在list2中我們add一個(gè)int,add(10)就會(huì)編譯報(bào)錯(cuò),那么這個(gè)泛型就可以只在編譯階段有效,通過了編譯階段,泛型就不存在了。可以驗(yàn)證,我們繞過編譯,用反射動(dòng)態(tài)的在list2中add一個(gè)int是可以成功的,只是這時(shí)因?yàn)閘ist2中存儲(chǔ)了多個(gè)不同類型的數(shù)據(jù)(String型,和int型),就不能用for-each來遍歷了,會(huì)拋出類型轉(zhuǎn)換錯(cuò)誤異常ClassCastException 補(bǔ)充資料: 七、關(guān)于Java類加載器內(nèi)容的詳解 1、類的加載 當(dāng)程序要使用某個(gè)類時(shí),如果該類還未被加載到內(nèi)存中,則系統(tǒng)會(huì)通過加載,連接,初始化三步來實(shí)現(xiàn)對這個(gè)類進(jìn)行初始化 加載: 就是指將class文件讀入內(nèi)存,并為之創(chuàng)建一個(gè)Class對象,任何類被使用時(shí)系統(tǒng)都會(huì)建立一個(gè)Class對象 連接: 驗(yàn)證:確保被加載類的正確性 準(zhǔn)備:負(fù)責(zé)為類的靜態(tài)成員分配內(nèi)存,并設(shè)置默認(rèn)初始化值 解析:將類中的符號引用替換為直接引用 初始化: 局部變量保存在棧區(qū):必須手動(dòng)初始化 new 的對象保存在堆區(qū):虛擬機(jī)會(huì)進(jìn)行默認(rèn)初始化,基本數(shù)據(jù)類型初始化值為0,引用類型初始化值為null
2、類加載的時(shí)機(jī)(只加載一次) 以下時(shí)機(jī)僅表示第一次的時(shí)候 創(chuàng)建類的實(shí)例的時(shí)候 訪問類的靜態(tài)變量的時(shí)候 調(diào)用類的靜態(tài)方法的時(shí)候 使用反射方式來強(qiáng)制創(chuàng)建某個(gè)類或接口對應(yīng)的java.lang.Class對象 初始化某個(gè)類的子類的時(shí)候 直接使用java.exe命令來運(yùn)行某個(gè)主類
3、類加載器 負(fù)責(zé)將.class文件加載到內(nèi)存中,并為之生成對應(yīng)的Class對象 雖然我們在開發(fā)過程中不需要關(guān)心類加載機(jī)制,但是了解這個(gè)機(jī)制我們就能更好的理解程序的運(yùn)行 4、類加載器的組成 Bootstrap ClassLoader 根類加載器 也被稱為引導(dǎo)類加載器,負(fù)責(zé)Java核心類的加載,比如System類,在JDK中JRE的lib目錄下rt.jar文件中的類 Extension ClassLoader 擴(kuò)展類加載器 負(fù)責(zé)JRE的擴(kuò)展目錄中jar包的加載,在JDK中JRE的lib目錄下ext目錄 System ClassLoader 系統(tǒng)類加載器 負(fù)責(zé)在JVM啟動(dòng)時(shí)加載來自java命令的class文件,以及classpath環(huán)境變量所指定的jar包和類路徑,主要是我們開發(fā)者自己寫的類
|