To build a better world !

          Java反射機(jī)制的學(xué)習(xí)

          Java反射機(jī)制的學(xué)習(xí)

          Java反射機(jī)制是Java語(yǔ)言被視為準(zhǔn)動(dòng)態(tài)語(yǔ)言的關(guān)鍵性質(zhì)。Java反射機(jī)制的核心就是允許在運(yùn)行時(shí)通過(guò)Java Reflection APIs來(lái)取得已知名字的class類的相關(guān)信息,動(dòng)態(tài)地生成此類,并調(diào)用其方法或修改其域(甚至是本身聲明為private的域或方法)。

          也許你使用Java已經(jīng)很長(zhǎng)時(shí)間了,可是幾乎不會(huì)用到Java反射機(jī)制。你會(huì)嗤之以鼻地告訴我,Java反射機(jī)制沒(méi)啥用。或許在J2EEJ2SE等平臺(tái),Java反射機(jī)制沒(méi)啥用(具體我也不了解,不多做評(píng)論),但是在Android應(yīng)用開(kāi)發(fā)中,該機(jī)制會(huì)帶給你許多驚喜。

          如果熟悉Android,那么你應(yīng)該知道,Google不知出于什么原因,在系統(tǒng)源碼中一些類或方法中經(jīng)常加上“@hide”注釋標(biāo)記。它的作用是使這個(gè)方法或類在生成SDK時(shí)不可見(jiàn),因此由此注釋的東西,你在編譯期是不可見(jiàn)的。這就出現(xiàn)了一些問(wèn)題。一些明明可以訪問(wèn)的東西編譯期卻無(wú)法訪問(wèn)了!這使得你的程序有些本來(lái)可以完成的功能無(wú)法編譯通過(guò)。

          當(dāng)然,有一種辦法是自己去掉Android源碼中的所有“@hide”標(biāo)記,然后重新編譯一份自己的SDK。另一種辦法就是使用Java反射機(jī)制。當(dāng)然,你還可以利用反射來(lái)訪問(wèn)存在訪問(wèn)限制的方法和修改其域。不過(guò)這種使用方法比較特殊,我們?cè)谖恼碌淖詈髥为?dú)討論。

          從Class類說(shuō)起

          如果你使用Java,那么你應(yīng)該知道Java中有一個(gè)Class類。Class類本身表示Java對(duì)象的類型,我們可以通過(guò)一個(gè)Object(子)對(duì)象的getClass方法取得一個(gè)對(duì)象的類型,此函數(shù)返回的就是一個(gè)Class類。當(dāng)然,獲得Class對(duì)象的方法有許多,但是沒(méi)有一種方法是通過(guò)Class的構(gòu)造函數(shù)來(lái)生成Class對(duì)象的。

          也許你從來(lái)沒(méi)有使用過(guò)Class類,也許你曾以為這是一個(gè)沒(méi)什么用處的東西。不管你以前怎么認(rèn)為,Class類是整個(gè)Java反射機(jī)制的源頭。一切關(guān)于Java反射的故事,都從Class類開(kāi)始。

          因此,要想使用Java反射,我們首先得到Class類的對(duì)象。下表列出了幾種得到Class類的方法,以供大家參考。

          Class object 誕生管道

          示例

          運(yùn)用getClass()

          注:每個(gè)class 都有此函數(shù)

          String str = "abc";

          Class c1 = str.getClass();

          運(yùn)用

          Class.getSuperclass()

          Button b = new Button();

          Class c1 = b.getClass();

          Class c2 = c1.getSuperclass();

          運(yùn)用static method

          Class.forName()

          (最常被使用)

          Class c1 = Class.forName ("java.lang.String");

          Class c2 = Class.forName ("java.awt.Button");

          Class c3 = Class.forName ("java.util.LinkedList$Entry");

          Class c4 = Class.forName ("I");

          Class c5 = Class.forName ("[I");

          運(yùn)用

          .class 語(yǔ)法

          Class c1 = String.class;

          Class c2 = java.awt.Button.class;

          Class c3 = Main.InnerClass.class;

          Class c4 = int.class;

          Class c5 = int[].class;

          運(yùn)用

          primitive wrapper classes

          的TYPE 語(yǔ)法

          Class c1 = Boolean.TYPE;

          Class c2 = Byte.TYPE;

          Class c3 = Character.TYPE;

          Class c4 = Short.TYPE;

          Class c5 = Integer.TYPE;

          Class c6 = Long.TYPE;

          Class c7 = Float.TYPE;

          Class c8 = Double.TYPE;

          Class c9 = Void.TYPE;

          獲取一些基本信息

          在我們得到一個(gè)類的Class類對(duì)象之后,Java反射機(jī)制就可以大施拳腳了。首先讓我們來(lái)了解下如何獲取關(guān)于某一個(gè)類的一些基本信息。

          Java class 內(nèi)部模塊

          Java class 內(nèi)部模塊說(shuō)明

          相應(yīng)之Reflection API,多半為Class methods。

          返回值類型(return type)

          package

          class隸屬哪個(gè)package

          getPackage()

          Package

          import

          class導(dǎo)入哪些classes

          無(wú)直接對(duì)應(yīng)之API。可間接獲取。

          modifier

          class(或methods, fields)的屬性

          int getModifiers()

          Modifier.toString(int)

          Modifier.isInterface(int)

          int

          String

          bool

          class name or interface name

          class/interface

          名稱getName()

          String

          type parameters

          參數(shù)化類型的名稱

          getTypeParameters()

          TypeVariable <Class>[]

          base class

          base class(只可能一個(gè))

          getSuperClass()

          Class

          implemented interfaces

          實(shí)現(xiàn)有哪些interfaces

          getInterfaces()

          Class[]

          inner classes

          內(nèi)部classes

          getDeclaredClasses()

          Class[]

          outer class

          如果我們觀察的class 本身是inner classes,那么相對(duì)它就會(huì)有個(gè)outer class。

          getDeclaringClass()

          Class

          上表中,列出了一些Java class內(nèi)部信息的獲取方式。所采用的方法幾乎都是調(diào)用Class對(duì)象的成員方法(由此你就可以了解到Class類的用處了吧)。當(dāng)然,表中所列出的信息并不是全部,有很大一部分沒(méi)有列出,你可以通過(guò)查閱Java文檔得到更全面的了解。另外,下面將重點(diǎn)介紹一下類的構(gòu)造函數(shù)、域和成員方法的獲取方式。

          類中最重要的三個(gè)信息

          如果要對(duì)一個(gè)類的信息重要性進(jìn)行排名的話,那么這三個(gè)信息理應(yīng)獲得前三的名次。它們分別是:構(gòu)造函數(shù)、成員函數(shù)、成員變量。

          也許你不同意我的排名,沒(méi)關(guān)系。對(duì)于Java反射來(lái)說(shuō),這三個(gè)信息與之前介紹的基本信息相比較而言,有著本質(zhì)的區(qū)別。那就是,之前的信息僅僅是只讀的,而這三個(gè)信息可以在運(yùn)行時(shí)被調(diào)用(構(gòu)造函數(shù)和成員函數(shù))或者被修改(成員變量)。所以,我想無(wú)可否認(rèn),至少站在Java反射機(jī)制的立場(chǎng)來(lái)說(shuō),這三者是最重要的信息。

          下面,讓我們分別了解一下這三個(gè)重要信息的獲取方式。另外,我們將在后面的章節(jié),詳細(xì)介紹他們的調(diào)用方式或者修改方式。

          構(gòu)造函數(shù)

          如果我們將Java對(duì)象視為一個(gè)二進(jìn)制的生活在內(nèi)存中生命體的話,那么構(gòu)造函數(shù)無(wú)疑可以類比為Java對(duì)象生命體的誕生過(guò)程。我們?cè)跇?gòu)造函數(shù)調(diào)用時(shí)為對(duì)象分配內(nèi)存空間,初始化一些屬性,于是一個(gè)新的生命誕生了。

          Java是純面向?qū)ο蟮恼Z(yǔ)言,Java中幾乎所有的一切都是類的對(duì)象,因此可想而知構(gòu)造函數(shù)的重要性。

          Java反射機(jī)制能夠得到構(gòu)造函數(shù)信息實(shí)在應(yīng)該是一件令人驚喜的事情。正因?yàn)榇耍瓷錂C(jī)制實(shí)質(zhì)上才擁有了孵化生命的能力。換句話言之,我們可以通過(guò)反射機(jī)制,動(dòng)態(tài)地創(chuàng)建新的對(duì)象。

          獲取構(gòu)造函數(shù)的方法有以下幾個(gè):

          Constructor getConstructor(Class[] params) 

          Constructor[] getConstructors()

          Constructor getDeclaredConstructor(Class[] params) 

          Constructor[] getDeclaredConstructors()

          我們有兩種方式對(duì)這四個(gè)函數(shù)分組。

          首先可以由構(gòu)造函數(shù)的確定性進(jìn)行分類。我們知道,一個(gè)類實(shí)際上可以擁有很多個(gè)構(gòu)造函數(shù)。那么我們獲取的構(gòu)造函數(shù)是哪個(gè)呢?我們可以根據(jù)構(gòu)造函數(shù)的參數(shù)標(biāo)簽對(duì)構(gòu)造函數(shù)進(jìn)行明確的區(qū)分,因此,如果我們?cè)?font face="Times New Roman">Java反射時(shí)指定構(gòu)造函數(shù)的參數(shù),那么我們就能確定地返回我們需要的那個(gè)“唯一”的構(gòu)造函數(shù)。getConstructor(Class[] params) getDeclaredConstructor(Class[] params)正是這種確定唯一性的方式。但是,如果我們不清楚每個(gè)構(gòu)造函數(shù)的參數(shù)表,或者我們出于某種目的需要獲取所有的構(gòu)造函數(shù)的信息,那么我們就不需要明確指定參數(shù)表,而這時(shí)返回的就應(yīng)該是構(gòu)造函數(shù)數(shù)組,因?yàn)闃?gòu)造函數(shù)很可能不止一個(gè)。getConstructors()getDeclaredConstructors()就是這種方式。

          另外,我們還可以通過(guò)構(gòu)造函數(shù)的訪問(wèn)權(quán)限進(jìn)行分類。在設(shè)計(jì)類的時(shí)候,我們往往有一些構(gòu)造函數(shù)需要聲明為“private”、“protect”或者“default”,目的是為了不讓外部的類調(diào)用此構(gòu)造函數(shù)生成對(duì)象。于是,基于訪問(wèn)權(quán)限的不同,我們可以將構(gòu)造函數(shù)分為public和非public兩種。

          getConstructor(Class[] params) getConstructors()僅僅可以獲取到public的構(gòu)造函數(shù),而getDeclaredConstructor(Class[] params) getDeclaredConstructors()則能獲取所有(包括public和非public)的構(gòu)造函數(shù)。

          成員函數(shù)

          如果構(gòu)造函數(shù)類比為對(duì)象的誕生過(guò)程的話,成員函數(shù)無(wú)疑可以類比為對(duì)象的生命行為過(guò)程。成員函數(shù)的調(diào)用執(zhí)行才是絕大多數(shù)對(duì)象存在的證據(jù)和意義。Java反射機(jī)制允許獲取成員函數(shù)(或者說(shuō)成員方法)的信息,也就是說(shuō),反射機(jī)制能夠幫助對(duì)象踐行生命意義。通俗地說(shuō),Java反射能使對(duì)象完成其相應(yīng)的功能。

          和獲取構(gòu)造函數(shù)的方法類似,獲取成員函數(shù)的方法有以下一些:

          Method getMethod(String name, Class[] params)

          Method[] getMethods()

          Method getDeclaredMethod(String name, Class[] params) 

          Method[] getDeclaredMethods() 

          其中需要注意,String name參數(shù),需要寫入方法名。關(guān)于訪問(wèn)權(quán)限和確定性的問(wèn)題,和構(gòu)造函數(shù)基本一致。

          成員變量

          成員變量,我們經(jīng)常叫做一個(gè)對(duì)象的域。從內(nèi)存的角度來(lái)說(shuō),構(gòu)造函數(shù)和成員函數(shù)都僅僅是Java對(duì)象的行為或過(guò)程,而成員變量則是真正構(gòu)成對(duì)象本身的細(xì)胞和血肉。簡(jiǎn)單的說(shuō),就是成員變量占用的空間之和幾乎就是對(duì)象占用的所有內(nèi)存空間。

          獲取成員變量的方法與上面兩種方法類似,具體如下:

          Field getField(String name)

          Field[] getFields()

          Field getDeclaredField(String name)

          Field[] getDeclaredFields()

          其中,String name參數(shù),需要寫入變量名。關(guān)于訪問(wèn)權(quán)限和確定性的問(wèn)題,與前面兩例基本一致。

          讓動(dòng)態(tài)真正動(dòng)起來(lái)

          在本文的一開(kāi)始就說(shuō),Java反射機(jī)制是Java語(yǔ)言被視為準(zhǔn)動(dòng)態(tài)語(yǔ)言的關(guān)鍵性質(zhì)。如果Java反射僅僅能夠得到Java類(或?qū)ο螅┻\(yùn)行時(shí)的信息,而不能改變其行為和屬性,那么它當(dāng)然算不上“動(dòng)態(tài)”。百度了一把何謂“動(dòng)態(tài)語(yǔ)言”,解釋如下:動(dòng)態(tài)語(yǔ)言,是指程序在運(yùn)行時(shí)可以改變其結(jié)構(gòu):新的函數(shù)可以被引進(jìn),已有的函數(shù)可以被刪除等在結(jié)構(gòu)上的變化。由此看來(lái),Java確實(shí)不能算作“動(dòng)態(tài)語(yǔ)言”。但是和CC++等純靜態(tài)語(yǔ)言相比,Java語(yǔ)言允許使用者在運(yùn)行時(shí)加載、探知、使用編譯期間完全未知的classes,所以我們說(shuō)Java是“準(zhǔn)動(dòng)態(tài)”語(yǔ)言。

          細(xì)心地讀者可能已經(jīng)發(fā)現(xiàn),在“類中最重要的三個(gè)信息”一節(jié)中,我們獲取的信息其實(shí)都是屬于類的,而不是對(duì)象。對(duì)于類的信息提取,其實(shí)并不涉及到對(duì)象內(nèi)存,在程序編譯完成的那一刻起,一切都已經(jīng)是確定的了。因此,它并不能算“動(dòng)態(tài)”。而如何對(duì)對(duì)象內(nèi)存進(jìn)行操作和訪問(wèn),才是“動(dòng)”的真正含義。

          說(shuō)了這么多,關(guān)鍵還在于如何利用反射讓Java真正動(dòng)起來(lái)。下面我將按照創(chuàng)生、行為與屬性三個(gè)方面來(lái)介紹反射機(jī)制是如何讓Java動(dòng)的。

          創(chuàng)生

          不知是否本性使然,人類偏愛(ài)于思索起源與終結(jié)的話題。如果將程序類比于一個(gè)二進(jìn)制的世界的話,那么我們程序員則是這個(gè)世界的上帝。我們掌控著這個(gè)世界的起源和終結(jié),熟悉世界中一草一木的屬性和所有生靈的習(xí)性。現(xiàn)在就讓我們開(kāi)始創(chuàng)世紀(jì)吧!

          在 “構(gòu)造函數(shù)”那一小節(jié)中,我們列出了獲取構(gòu)造函數(shù)的四種方法。這四種方法的返回值不知是否引起了各位的注意,那就是Constructor類。Constructor就類比于女?huà)z吹給泥人的那一口真氣,有了它,一個(gè)生命才真正出現(xiàn)。

          Constructor支持泛型,也就是它本身應(yīng)該是Constructor<T>。這個(gè)類有一個(gè)public成員函數(shù),newInstance(Object... args),其中args為對(duì)應(yīng)的參數(shù)。我們正是通過(guò)它來(lái)實(shí)現(xiàn)創(chuàng)生的過(guò)程。

          行為

          行為踐行著生命的意義,而眾多事物的行為才得以構(gòu)成整個(gè)世界的運(yùn)轉(zhuǎn)。盡管道家的老子主張“無(wú)為而治”,宣揚(yáng)“圣人處無(wú)為之事,行不言之教”,但那是因?yàn)樗旧砭褪?nbsp;“無(wú)”的信仰者(“道”即“無(wú)”)。我們是唯物主義的信徒,所以必然要以“有”為價(jià)值。那么,在二進(jìn)制的世界里,我們?nèi)绾握{(diào)用Java對(duì)象的行為呢?

          同樣,我們首先回顧“成員函數(shù)”小節(jié)中四種方法的返回值。對(duì),那就是Method類。此類有一個(gè)public成員函數(shù),Object invoke(Object receiver, Object... args)。我們能很好理解此函數(shù)的第二個(gè)參數(shù)args,它代表這個(gè)方法所需要接收的參數(shù)。也許大家對(duì)第一個(gè)參數(shù)receiver還存在疑惑之處。這得從編程語(yǔ)言的發(fā)展歷程講起。

          如果你關(guān)注幾種主流編程語(yǔ)言的起源,那么你能有這樣的印象:C從匯編而來(lái),C++C而來(lái),而JavaC/C++而來(lái)。有這樣一種印象就足夠了。從這樣的發(fā)展史我們可以看出,C++Java這兩種面向?qū)ο蟮木幊陶Z(yǔ)言都是從面向過(guò)程的C語(yǔ)言基礎(chǔ)上發(fā)展而來(lái)的。OOP是一種思想,它本身與編程語(yǔ)言無(wú)關(guān)。也就是說(shuō),我們用C也能寫出面向?qū)ο蟮某绦颍@也是C++Java能夠以C為基礎(chǔ)的根本所在。然而,C無(wú)法實(shí)現(xiàn)類似object.method()這種表現(xiàn)形式,因?yàn)?/font>C語(yǔ)言的結(jié)構(gòu)體中并不支持函數(shù)定義。那么我們用C實(shí)現(xiàn)OOP的時(shí)候,如何調(diào)用對(duì)象的方法呢?

          本質(zhì)上說(shuō),object.method()這種調(diào)用方式是為了表明具體method()的調(diào)用對(duì)象。而invoke(Object receiver, Object... args)的第一個(gè)參數(shù)正是指明調(diào)用對(duì)象。在C++中,object.method()其實(shí)是有隱含參數(shù)的,那就是object對(duì)象的指針,method原型的第一個(gè)參數(shù)其實(shí)是this指針,于是原型為method(void* this)

          這樣一溯源,也許你更清楚了Object receiver參數(shù)的含義,或許更迷糊了?不管怎樣,歷史就是如此,只不過(guò)我個(gè)人能力有限,說(shuō)不清楚而已。

          另外需要注意的是,如果某個(gè)方法是Java類的靜態(tài)方法,那么Object receiver參數(shù)可以傳入null,因?yàn)殪o態(tài)方法不從屬于對(duì)象。

          屬性

          同樣是人類,令狐沖和岳不群是如何被區(qū)分開(kāi)的?那是因?yàn)樗麄冇兄煌膶傩浴M瑯樱粋€(gè)類可以生成多個(gè)對(duì)象,幾個(gè)同類型的對(duì)象之間如何區(qū)分?屬性起著決定性的作用。說(shuō)到這里,想起一個(gè)科幻故事。人體瞬移機(jī),作用的根本原理就是人進(jìn)入A位置,被完全掃描之后,再在B位置重新組成它的細(xì)胞、血肉等屬性,從而完全創(chuàng)造出另一個(gè)一模一樣的人。當(dāng)然,這是唯物主義的極致,它假設(shè)了只要一切物質(zhì)相同,連記憶和靈魂都不會(huì)出現(xiàn)偏差,另外還存在倫理的問(wèn)題,例如A位置的人會(huì)被銷毀掉嗎?

          盡管這是一個(gè)科幻,但是在程序的世界里,我們?cè)缫呀?jīng)用上了這類似幻想的技術(shù)。Java中如何遠(yuǎn)程傳遞一個(gè)對(duì)象?我們已經(jīng)使用上了Java對(duì)象序列化的接口。不僅如此,利用序列化接口,我們甚至可以將一個(gè)生命保存起來(lái),在需要的時(shí)候?qū)⑺鼜?fù)活,這就是對(duì)象的持久化。不得不感慨,在程序的世界里,我們就是上帝啊!

          對(duì)象序列化如此強(qiáng)大,那么它的本質(zhì)是什么呢?它的工作原理是怎樣的呢?簡(jiǎn)單的說(shuō),對(duì)象序列化的本質(zhì)就是屬性的序列化。原理就是我們崇尚的唯物主義,如果同一個(gè)類的兩個(gè)對(duì)象所有屬性值都完全相同,那么我們可以認(rèn)為這是同一個(gè)對(duì)象。

          說(shuō)了這么多,只是想說(shuō)明一件事情,屬性對(duì)于對(duì)象而言是多么的重要。那么如何讀寫對(duì)象中屬性的值呢?回顧獲取屬性信息的方法返回值類型,那是FieldField類有兩個(gè)public方法,分別對(duì)應(yīng)讀與寫,它們是:

          Object get(Object object)

          void set(Object object, Object value)

          object參數(shù)需要傳入的對(duì)象,原理類似于成員方法需要指明對(duì)象一樣。如果是靜態(tài)屬性,此值同樣可以為null

          關(guān)于反射的一些高級(jí)話題

          如果說(shuō)前面那些屬于Java反射的基本知識(shí),那么在文章的最后,我們來(lái)探討一下反射的一些高級(jí)話題。另外,本文對(duì)基礎(chǔ)知識(shí)的講解僅屬于抓主干,具體的一些旁支可以自己參看文檔。需要提一下的是,Java反射中對(duì)數(shù)組做過(guò)單獨(dú)的優(yōu)化處理,具體可查看java.lang.reflect.Array類;還有關(guān)于泛型的支持,可查看java.lang.reflect.ParameterizedType及相關(guān)資料。

          暫時(shí)想到的高級(jí)話題有三個(gè),由于對(duì)Java反射理解的也不算深入,所以僅僅從思路上進(jìn)行探討,具體實(shí)現(xiàn)上,大家可以參考其他相關(guān)資料,做更深入研究。

          Android編譯期問(wèn)題

          Android的安全權(quán)限問(wèn)題我把它簡(jiǎn)單的劃分成三個(gè)層次,最不嚴(yán)格的一層就是僅僅騙過(guò)編譯器的“@hide”標(biāo)記。對(duì)于一款開(kāi)源的操作系統(tǒng)而言,這個(gè)標(biāo)記本身并不具備安全上的限制。不過(guò),從上次Google過(guò)來(lái)的負(fù)責(zé)Android工程師的說(shuō)法來(lái)看,這個(gè)標(biāo)記的作用更多的是方便硬件廠商做閉源的二次開(kāi)發(fā)。這樣解釋倒也說(shuō)得過(guò)去。

          不過(guò)這并不影響我們使用反射機(jī)制以繞過(guò)原生Android的第一層安全措施。如果你熟悉源碼的話,會(huì)發(fā)現(xiàn)這可以應(yīng)用到很多地方。并且最關(guān)鍵的是你并不需要放在源碼中編譯,而是像普通應(yīng)用程序的開(kāi)發(fā)過(guò)程一樣。

          具體使用范圍我不能一一列舉了,例如自定義窗口、安裝程序等等。簡(jiǎn)單的說(shuō),在Android上使用反射技術(shù),你才會(huì)對(duì)Android系統(tǒng)有更深的理解和更高的控制權(quán)。

          軟件的解耦合

          我們?cè)诩軜?gòu)代碼的時(shí)候,經(jīng)常提到解耦合、弱耦合。其實(shí),解耦和不僅僅只能在代碼上做文章。我們可以考慮這樣一種情況:軟件的功能需求不可能一開(kāi)始就完全確定,有一些功能在軟件開(kāi)發(fā)的后期甚至是軟件已經(jīng)發(fā)布出去之后才想到要加入或者去掉。

          按我們慣有的思維,這種情況就得改動(dòng)源碼,重新編譯。如果軟件已經(jīng)發(fā)布出去,那么就得讓客戶重新安裝一次軟件。反思一下,我們是否認(rèn)為軟件和程序是同一回事呢?事實(shí)上,如果你能將軟件和程序分開(kāi)來(lái)理解,那么你會(huì)發(fā)現(xiàn),為了應(yīng)對(duì)以上的情況,我們還有其他的解決辦法。

          我國(guó)有一個(gè)很重要但是很麻煩的制度,那就是戶籍制度。它的本意是為了更好的管理人口事宜。每當(dāng)一個(gè)孩子出生,我們就需要在戶籍管理的地方去給他辦理戶籍入戶;而每當(dāng)一個(gè)人去世,我們也需要在相應(yīng)的地方銷去他的戶籍。既然我們可以視類為生命,那么我們能否通過(guò)學(xué)習(xí)這樣的戶籍管理制度來(lái)動(dòng)態(tài)地管理類呢?

          事實(shí)上這樣的管理是可行的,而且Java虛擬機(jī)本身正是基于這樣的機(jī)制來(lái)運(yùn)行程序的。因此我們是否可以這樣來(lái)架構(gòu)軟件框架。首先,我們的軟件有一個(gè)配置文件,配置文件其實(shí)是一個(gè)文本,里面詳細(xì)描述了,我們的軟件核心部分運(yùn)行起來(lái)后還需要從什么路徑加載些什么類需要何時(shí)調(diào)用什么方法等。這樣當(dāng)我們需要加或減某些功能時(shí),我們只需要簡(jiǎn)單地修改配置文本文件,然后刪除或者添加相應(yīng)的.class文件就可以了。

          如果你足夠敏感,你或許會(huì)發(fā)現(xiàn),這種方式形成的配置文件幾乎可以相當(dāng)于一門腳本語(yǔ)言了。而且這個(gè)腳本的解釋器也是我們自己寫的,另外關(guān)鍵是它是開(kāi)發(fā)的,你可以為它動(dòng)態(tài)地加入一些新的類以增加它的功能。

          不要以為這僅僅是一個(gè)設(shè)想,雖然要開(kāi)發(fā)成一門完備的腳本語(yǔ)言確實(shí)比較麻煩。但是在一些網(wǎng)絡(luò)端的大型項(xiàng)目中,通過(guò)配置文件 + ClassLoader + 反射機(jī)制結(jié)合形成的這種軟件解耦和方式已經(jīng)用得比較普遍了。

          所以,在此我不是在提出一種設(shè)想,而是在介紹業(yè)界處理此類問(wèn)題的一種解決方案。

          反射安全

          文章讀到這里,我想你應(yīng)該由衷地感嘆,Java反射機(jī)制實(shí)在是太強(qiáng)大了。但是,如果你有一些安全意識(shí)的話,就會(huì)發(fā)現(xiàn)Java這個(gè)機(jī)制強(qiáng)大得似乎有些過(guò)頭了。前面我們提到,Java反射甚至可以訪問(wèn)private方法和屬性。為了讓大家對(duì)Java反射有更全面的了解,樹(shù)立正確的人生觀價(jià)值觀,本小節(jié)將對(duì)Java的安全問(wèn)題做一個(gè)概要性的介紹。

          相對(duì)于C++來(lái)說(shuō),Java算是比較安全的語(yǔ)言了。這與它們的運(yùn)行機(jī)制有密切的關(guān)系,C++運(yùn)行于本地,也就是說(shuō)幾乎所有程序的權(quán)限理論上都是相同的。而Java由于是運(yùn)行于虛擬機(jī)中,而不直接與外部聯(lián)系,所以實(shí)際上Java的運(yùn)行環(huán)境是一個(gè)“沙盒”環(huán)境。

          Java的安全機(jī)制其實(shí)是比較復(fù)雜的,至少對(duì)于我來(lái)說(shuō)是如此。作為Java的安全模型,它包括了:字節(jié)碼驗(yàn)證器、類加載器、安全管理器、訪問(wèn)控制器等一系列的組件。之前文中提到過(guò),我把Android安全權(quán)限劃分為三個(gè)等級(jí):第一級(jí)是針對(duì)編譯期的“@hide”標(biāo)記;第二級(jí)是針對(duì)訪問(wèn)權(quán)限的private等修飾;第三級(jí)則是以安全管理器為托管的Permission機(jī)制。

          Java反射確實(shí)可以訪問(wèn)private的方法和屬性,這是繞過(guò)第二級(jí)安全機(jī)制的方法(之一)。它其實(shí)是Java本身為了某種目的而留下的類似于“后門”的東西,或者說(shuō)是為了方便調(diào)試?不管如何,它的原理其實(shí)是關(guān)閉訪問(wèn)安全檢查。

          如果你具有獨(dú)立鉆研的精神的話,你會(huì)發(fā)現(xiàn)之前我們提到的FieldMethodConstructor類,它們都有一個(gè)共同的父類AccessibleObject AccessibleObject 有一個(gè)公共方法:void setAccessible(boolean flag)。正是這個(gè)方法,讓我們可以改變動(dòng)態(tài)的打開(kāi)或者關(guān)閉訪問(wèn)安全檢查,從而訪問(wèn)到原本是private的方法或域。另外,訪問(wèn)安全檢查是一件比較耗時(shí)的操作,關(guān)閉它反射的性能也會(huì)有較大提升。

          不要認(rèn)為我們繞過(guò)了前兩級(jí)安全機(jī)制就沾沾自喜了,因?yàn)檫@兩級(jí)安全并不是真正為了安全而設(shè)置的。它們的作用更多的是為了更好的完善規(guī)則。而第三級(jí)安全才是真正為了防止惡意攻擊而出現(xiàn)的。在這一級(jí)的防護(hù)下,你甚至可能都無(wú)法完成反射(ReflectPermission),其他的一切自然無(wú)從說(shuō)起。

          對(duì)于這一級(jí),我的了解還太少,并且也與本文的主題相關(guān)甚少。以后有機(jī)會(huì),深入學(xué)習(xí)之后再聊吧!

          參考文獻(xiàn)

          轉(zhuǎn) 侯捷談Java反射機(jī)制

          java反射機(jī)制

          java反射的若干高級(jí)應(yīng)用

          Java反射經(jīng)典實(shí)例 Java Reflection Cookbook

          Java ClassLoader詳解》

          《自定義ClassLoader的簡(jiǎn)單例子》

          《提高Java反射速度的方法以及對(duì)setAccessable的誤解》

          《深入理解Java安全模型》

          Java安全管理器(Security Manager)

          posted on 2011-03-26 20:53 zh.weir 閱讀(34166) 評(píng)論(17)  編輯  收藏 所屬分類: Java的深入理解

          評(píng)論

          # re: Java反射機(jī)制的學(xué)習(xí)[未登錄](méi) 2011-03-29 17:45 me

          好久冒看到反射了,溫故之!  回復(fù)  更多評(píng)論   

          # re: Java反射機(jī)制的學(xué)習(xí) 2011-12-15 17:30 vanezkw

          好文章就要留言才是,呵呵  回復(fù)  更多評(píng)論   

          # re: Java反射機(jī)制的學(xué)習(xí) 2012-05-06 20:02 yuquan

          好文章啊,留言留言  回復(fù)  更多評(píng)論   

          # re: Java反射機(jī)制的學(xué)習(xí) 2012-05-08 12:48 eee

          非常好的文章,閱讀起來(lái)很爽快!  回復(fù)  更多評(píng)論   

          # re: Java反射機(jī)制的學(xué)習(xí) 2012-05-10 11:13 zhangsan

          好文章,好作者  回復(fù)  更多評(píng)論   

          # re: Java反射機(jī)制的學(xué)習(xí)[未登錄](méi) 2012-06-14 21:01 啊啊

          不錯(cuò)不錯(cuò)  回復(fù)  更多評(píng)論   

          # re: Java反射機(jī)制的學(xué)習(xí) 2012-07-19 17:25 ¥¥¥¥¥¥¥¥¥

          果然很牛逼  回復(fù)  更多評(píng)論   

          # re: Java反射機(jī)制的學(xué)習(xí) 2012-08-29 21:50 cat

          要是有個(gè)例子就更好了!  回復(fù)  更多評(píng)論   

          # re: Java反射機(jī)制的學(xué)習(xí) 2012-09-11 14:34 成者之劍

          文章寫得灰常深入本質(zhì),讓人有豁然開(kāi)朗的感覺(jué),期待以后有更多類似的文章供Java有一定理解的學(xué)習(xí)者閱讀,學(xué)習(xí),另外非常感謝博主  回復(fù)  更多評(píng)論   

          # re: Java反射機(jī)制的學(xué)習(xí) 2012-12-25 16:48 毛毛蟲(chóng)

          好文章,學(xué)習(xí)了  回復(fù)  更多評(píng)論   

          # re: Java反射機(jī)制的學(xué)習(xí) 2013-05-03 12:05 cs

          你呀的文采太好了吧。  回復(fù)  更多評(píng)論   

          # re: Java反射機(jī)制的學(xué)習(xí) 2013-05-10 11:49 周爹爹

          好強(qiáng)大啊  回復(fù)  更多評(píng)論   

          # re: Java反射機(jī)制的學(xué)習(xí) 2013-09-13 17:01 andios

          nice  回復(fù)  更多評(píng)論   

          # re: Java反射機(jī)制的學(xué)習(xí) 2013-12-04 16:31 Melody或 追風(fēng)

          受教了  回復(fù)  更多評(píng)論   

          # re: Java反射機(jī)制的學(xué)習(xí)[未登錄](méi) 2014-01-19 18:48 aa

          好文章!  回復(fù)  更多評(píng)論   

          # re: Java反射機(jī)制的學(xué)習(xí)[未登錄](méi) 2014-01-20 11:51 lucky

          好文章!  回復(fù)  更多評(píng)論   

          # re: Java反射機(jī)制的學(xué)習(xí) 2015-01-22 17:34 zhuzhumouse

          好文章  回復(fù)  更多評(píng)論   

          公告

          大家好!歡迎光臨我的 Android 技術(shù)博客!



          本博客旨在交流與 Android 操作系統(tǒng)相關(guān)的各種技術(shù)及信息。

          博客內(nèi)的文章會(huì)盡量以開(kāi)源的形式提供給大家,希望我們能相互交流,共同提高!

          有不足之處,請(qǐng)不吝賜教!

          我的郵箱:zh.weir@gmail.com
          我的新浪微博:@囧虎張建偉

           

          導(dǎo)航

          <2011年3月>
          272812345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          統(tǒng)計(jì)

          • 隨筆 - 18
          • 文章 - 1
          • 評(píng)論 - 290
          • 引用 - 0

          留言簿(19)

          隨筆分類(24)

          隨筆檔案(18)

          文章檔案(1)

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 安乡县| 玉龙| 麻阳| 嵩明县| 大邑县| 文登市| 普宁市| 喜德县| 大渡口区| 丁青县| 德昌县| 来安县| 威海市| 新巴尔虎右旗| 枣庄市| 阿鲁科尔沁旗| 长垣县| 柏乡县| 项城市| 通州区| 南安市| 吴堡县| 荣成市| 新竹县| 东海县| 锦屏县| 志丹县| 满城县| 东乌珠穆沁旗| 小金县| 离岛区| 峨边| 临漳县| 鹰潭市| 淮南市| 灵璧县| 宝坻区| 北票市| 古浪县| 玛沁县| 宜昌市|