Java的反射機制的實現依賴於四個類:類、構造函數、字段、方法;;其中,class表示時間類對象、構造函數類構造函數對象、字段類屬性對象和方法類方法對象。通過這四個對象,我們可以大致看到壹個類的組成部分。
類:當程序運行時,java運行時系統將處理運行時類型中的所有對象。這些信息記錄了每個對象所屬的類,虛擬機通常使用運行時類型信息來選擇執行它的正確方法(來自白皮書)。但是我們如何獲得這些信息呢?這取決於類對象。getClass()方法在Object類中定義。我們可以通過這個方法得到指定對象的類對象。然後我們就可以通過分析這個對象來獲取我們想要的信息。
例如:ArrayList數組列表;
class clazz = ArrayList . getclass();
然後我會處理這個對象,clazz。
當然,Class類有很多方法,這裏我們將重點介紹與構造函數、字段和方法類相關的方法。
反射是Java編程語言的特征之壹,它允許正在運行的Java程序進行自我檢查,或“自我檢查”,並可以直接操縱程序的內部屬性。java的這種能力在實際應用中可能用得不多,但我個人認為要想對Java有更深入的了解,就應該掌握它。
1.檢測類別:
反思的工作機制
考慮下面這個簡單的例子,讓我們看看反射是如何工作的。
導入Java . lang . reflect . *;
公共類轉儲方法{
公共靜態void main(String args【】){
嘗試{
class c = class . forname(args【0】);
方法m【】= c . getdeclaredmethods();
for(int I = 0;我& ltm .長度;i++)
System.out.println。toString());
}接球(可投擲的e ){
system . err . println(e);
}
}
}
根據以下語句執行:
java轉儲方法java.util.ArrayList
該程序使用Class.forName加載指定的類,然後調用getDeclaredMethods獲取該類中定義的方法列表。Java.lang.reflect.Methods是壹個用於描述類中單個方法的類。
Java類反射中的主要方法
對於以下三種類型的組件(構造函數、字段和方法)中的任何壹種,java.lang.Class都提供了四個獨立的反射調用來以不同的方式獲取信息。呼叫遵循標準格式。以下是壹組用於查找構造函數的反射調用:
構造函數Get constructor(class【】params)-使用特殊參數類型獲取公共構造函數。
constructor【】Get constructors()-獲取該類的所有公共構造函數。
構造函數getDeclaredConstructor(class【】params)-獲取使用特定參數類型的構造函數(不考慮訪問級別)。
constructor【】getDeclaredConstructors()-獲取該類的所有構造函數(不考慮訪問級別)。
獲取字段信息的類反射調用不同於訪問構造函數的類反射調用,字段名用於參數類型數組中:
獲取指定的公共字段。
field【】Get fields()-獲取該類的所有公共字段。
獲取類聲明的命名字段。
field【】getDeclaredFields()-獲取該類聲明的所有字段。
獲取方法信息功能:
method get method(string name,class【】params)-使用特定的參數類型獲取命名的公共方法。
method【】Get methods()-獲取該類的所有公共* * *方法。
方法getdeclaredmethod(string name,class【】params)-通過使用特寫參數類型獲取類聲明的命名方法。
method【】getDeclaredMethods()-獲取該類聲明的所有方法。
使用反射:
反射類(如Method)可以在Java . lang . re fect包中找到。使用這些類時,必須遵循三個步驟:第壹步是獲取要操作的類的java.lang.Class對象。在運行的Java程序中,類和接口由java.lang.Class類描述。
以下是獲取類對象的方法之壹:
class c = class . forname(“Java . lang . string“);
該語句獲取字符串類的類對象。還有另壹種方法,例如下面的語句:
Class c = int.class
或者
c類=整數。類型;
他們可以獲得基本類型的課堂信息。在後壹種方法中,訪問基本類型封裝類(如Intege)中預定義的類型字段。
第二步是調用getDeclaredMethods之類的方法來獲取該類中定義的所有方法的列表。
獲得這些信息後,您可以進入第三步-使用反射API來操作這些信息,如以下代碼所示:
class c = class . forname(“Java . lang . string“);
方法m【】= c . getdeclaredmethods();
system . out . println(m【0】)。toString());
它將以文本形式打印出String中定義的第壹個方法的原型。
處理對象:
A.創建壹個類對象
B.通過getField創建壹個字段對象。
C.調用field . get XXX(Object)方法(XXX是Int、Float等。,如果是對象則省略;對象引用實例)。
例如:
導入Java . lang . reflect . *;
導入Java . awt . *;
類樣本獲取{
公共靜態void main(String[] args) {
矩形r =新矩形(100,325);
打印高度(r);
}
靜態空打印高度(矩形r ){
Field heightField
整數heightValue
class c = r . getclass();
嘗試{
height field = c . get field(“height“);
height value =(Integer)height field . get(r);
system . out . println(“Height:“+Height value . tostring());
} catch(NoSuchFieldException e ){
system . out . println(e);
} catch(安全異常e ){
system . out . println(e);
} catch(IllegalAccessException e ){
system . out . println(e);
}
}
}
安全和反思:
在處理反射時,安全性是壹個復雜的問題。幀代碼經常使用反射。正因為如此,我們可能希望框架可以完全訪問代碼,而無需考慮常規的訪問限制。然而,在其他情況下,不受控制的訪問會帶來嚴重的安全風險,例如當代碼在不受信任的代碼享有的環境中運行時。
由於這些相互沖突的需求,Java編程語言定義了壹種多級方法來處理反射的安全性。基本模式是對反射施加與源代碼訪問相同的限制:
從任何位置訪問類似公共組件
不能訪問類本身之外的私有組件。
對受保護和打包組件的有限訪問(默認訪問)
但至少有時,有壹種簡單的方法可以繞過這些限制。我們可以在自己編寫的類中擴展壹個普通的基本類Java . lang . reflect . accessible object類。該類定義了壹個setAccessible方法,該方法使我們能夠打開或關閉這些類之壹的實例的訪問檢測。唯壹的問題是,如果使用安全管理器,它將檢測關閉訪問檢測的代碼是否允許它。如果沒有,安全管理器將拋出壹個異常。
下面是壹個程序,它使用TwoString類實例上的反射來顯示安全性正在運行:
公共類反映安全性{
公共靜態void main(String[] args) {
嘗試{
TwoString ts =新的two string(“a“,“b“);
field field = clas . getdeclaredfield(“m _ s 1“);
//field . set accessible(true);
System.out.println(“檢索的值為“+
field . get(inst));
} catch(例外情況){
ex . printstacktrace(system . out);
}
}
}
如果我們編譯這個程序並直接從命令行運行它而不使用任何特定參數,它將在字段中引發IllegalAccessException。接到(安裝)電話。如果我們不註釋field . set accessible(true)行,然後重新編譯並重新運行代碼,它將成功編譯。最後,如果我們在命令行中添加JVM參數-Djava.security.manager來實現安全管理器,它仍然無法編譯,除非我們定義了ReflectSecurity類的權限限制。
反思性表現:(模仿他人)
反思是壹個強大的工具,但它也有壹些缺點。壹個主要的缺點是它對性能有影響。使用反射基本上是壹種解釋操作,我們可以告訴JVM我們想做什麽,它滿足我們的要求。這種操作總是比直接執行相同的操作慢。
以下程序是現場訪問性能測試的示例,包括基本測試方法。每種方法都測試壹種形式的字段訪問- accessSame與同壹對象的成員字段協作,accessOther使用可以直接訪問的另壹個對象的字段,accessReflection使用可以通過反射訪問的另壹個對象的字段。在每種情況下,該方法執行相同的計算-循環中簡單的加法/乘法序列。
該過程如下:
公共int access same(int循環){
m _ value = 0;
for(int index = 0;index & lt循環;index++ ){
m值=(m值+附加值)*
乘數_值;
}
返回m _ value
}
公共int訪問
s reference(int循環){
timing class timing = new timing class();
for(int index = 0;index & lt循環;index++ ){
timing . m _ VALUE =(timing . m _ VALUE+ADDITIVE _ VALUE)*
乘數_值;
}
返回timing.m _ value
}
公共int access reflection(int循環)引發異常{
timing class timing = new timing class();
嘗試{
Field field = TimingClass.class
getDeclaredField(“m _ value“);
for(int index = 0;index & lt循環;index++ ){
int value =(field . getint(timing )+
add _ VALUE)* MULTIPLIER _ VALUE;
field.setInt(時序,值);
}
返回timing.m _ value
} catch(例外情況){
System.out.println(“使用反射時出錯“);
扔ex;
}
}
在上面的例子中,測試程序重復調用每個方法,使用大量的周期,從而平均多次調用的時間來測量結果。平均值不包括每個方法的第壹次調用時間,因此初始化時間不是影響結果的因素。下圖清楚地顯示了每個方法字段的訪問時間:
圖1:現場訪問時間:
我們可以看到在前兩個圖(Sun JVM)中,使用反射的執行時間是使用直接訪問的1000倍以上。相比之下,IBM JVM可能稍好壹些,但反射方法仍然比其他方法花費700多倍的時間。在任何JVM上,其他兩種方法在時間上沒有顯著差異,但IBM JVM的速度幾乎是Sun JVM的兩倍。最有可能的是,這種差異反映了Sun Hot Spot JVM的專業優化,該JVM在簡單的基準測試中表現不佳。反射性能是Sun在開發1.4 JVM時關註的壹個方面,它顯示在反射方法調用結果中。就這種操作的性能而言,Sun 1.4.1 JVM比1.3.1版本有了很大的提高。
如果我們為創建使用反射的對象編寫壹個類似的計時測試程序,我們會發現這種情況下的差異不像字段和方法調用那樣顯著。使用newInstance()創建java.lang.Object的簡單實例的時間大約是在Sun 1.3.1 JVM上使用new Object()的12倍,是使用IBM 1.4.0 JVM的4倍,只有Sun1.4。在任何測試的JVM上使用array . new instance(type,size)創建數組的時間是使用new type【size】的兩倍。隨著數組大小的增加,差異逐漸縮小。隨著jdk6.0的推出,反射機制的性能有了很大的提高。期待...
總結:
Java語言反射提供了動態鏈接程序組件的多功能方法。它允許程序創建和控制任何類的對象(根據安全限制),而無需事先對目標類進行硬編碼。這些特征使得反射特別適合於創建以非常常見的方式與對象協作的庫。例如,在持久存儲對象是數據庫、XML或其他外部格式的框架中經常使用反射。Java反射非常有用。它使類和數據結構能夠通過名稱動態檢索相關信息,並允許它們在運行的程序中操作。Java的這壹特性非常強大,它是其他常用語言所不具備的,如C、C++、Fortran或Pascal。
但是反射有兩個缺點。首先是性能問題。當用於字段和方法訪問時,反射比直接代碼慢得多。性能問題的程度取決於程序中如何使用反射。如果它是程序中很少涉及的壹部分,那麽緩慢的性能將不是問題。即使測試中最壞情況的時序圖顯示反射操作只需要幾微秒。只有在性能關鍵型應用程序的核心邏輯中使用反射時,性能問題才會變得至關重要。
在許多應用程序中,壹個更嚴重的缺點是使用反射會掩蓋程序內部實際發生的事情。程序員希望在源代碼中看到繞過源代碼的邏輯、反射等技術會帶來維護問題。從性能比較的代碼示例中可以看出,反射代碼比相應的直接代碼更復雜。這些問題的最佳解決方案是保守地使用反射——只有在它真正能夠增加靈活性的地方——以記錄它在目標類中的使用。
以下是相應零件的示例:
特定應用:
1,模仿操作實例的符號
A級{}
公共類實例1 {
公共靜態void main(字符串參數【】)
{
嘗試{
class cls = class . forname(“A“);
布爾型b1
= cls.isInstance(新整數(37));
system . out . println(b 1);
boolean B2 = cls . is instance(new A());
system . out . println(B2);
}
接住(可投擲的e ){
system . err . println(e);
}
}
}
2.在類中找到指定的方法,同時獲取該方法的參數列表、異常和返回值。
導入Java . lang . reflect . *;
公共類方法1 {
private int f1(
Object p,int x)拋出NullPointerException
{
if(p = = null)
拋出新的NullPointerException();
返回x;
}
公共靜態void main(字符串參數【】)
{
嘗試{
class cls = class . forname(“method 1“);
方法列表【】
= cls . getdeclaredmethods();
for(int I = 0;我& ltmethlist.length
i++)
方法m = meth list【I】;
system . out . println(“name
=“+m . getname());
system . out . println(“decl class =“+
m . getdeclaringclass());
class pvec【】= m . getparametertypes();
for(int j = 0;j & ltpvec.lengthj++)
System.out.println("
param #“+j+“+pvec【j】);
class evec【】= m . get exception types();
for(int j = 0;j & ltevec.lengthj++)
system . out . println(“exc #“+j
+““+evec【j】);
system . out . println(“return type =“+
m . get returntype());
system . out . println(“-“);
}
}
接住(可投擲的e ){
system . err . println(e);
}
}
}
3.獲取類的構造函數信息與獲取方法基本相同。
導入Java . lang . reflect . *;
公共類構造器1 {
公共構造器1()
{
}
受保護的構造器1(int I,double d)
{
}
公共靜態void main(字符串參數【】)
{
嘗試{
class cls = class . forname(“constructor 1“);
構造函數目錄列表【】
= cls . getdeclaredconstructors();
for(int I = 0;我& ltctorlist.lengthi++) {
構造函數CT = ctor list【I】;
system . out . println(“name
=“+CT . getname());
system . out . println(“decl class =“+
CT . getdeclaringclass());
class pvec【】= CT . getparametertypes();
for(int j = 0;j & ltpvec.lengthj++)
system . out . println(“param #“
+j+““+pvec【j】);
class evec【】= CT . get exception types();
for(int j = 0;j & ltevec.lengthj++)
System.out.println(
“exc #“+j+““+evec【j】);
system . out . println(“-“);
}
}
接住(可投擲的e ){
system . err . println(e);
}
}
}
4.獲取類中的所有數據成員對象,包括名稱。類型和訪問修飾符
導入Java . lang . reflect . *;
公共類字段1 {
私立雙d;
public static final int i = 37
string s =“testing“;
公共靜態void main(字符串參數【】)
{
嘗試{
class cls = class . forname(“field 1“);
字段列表【】
= cls . getdeclaredfields();
for(int I
= 0;我& ltfield list . length;i++) {
field fld = field list【I】;
system . out . println(“name
=“+fld . getname());
system . out . println(“decl class =“+
fld . getdeclaringclass());
system . out . println(“type
=“+fld . gettype());
int mod = fld . get modifiers();
system . out . println(“modifiers =“+
modifier . tostring(mod));
system . out . println(“-“);
}
}
接住(可投擲的e ){
system . err . println(e);
}
}
}
5.使用方法的名稱調用該方法。
導入Java . lang . reflect . *;
公共類方法2 {
公共int add(int a,int b)
{
返回a+b;
}
公共靜態void main(字符串參數【】)
{
嘗試{
class cls = class . forname(“method 2“);
Class party pes【】=新類【2】;
party pes【0】=整數。類型;
party pes【1】=整數。類型;
Method meth = cls.getMethod(
“添加”,party pes);
method 2 methobj = new method 2();
Object arglist【】=新對象【2】;
arglist【0】=新整數(37);
arglist【1】=新整數(47);
對象retobj
= meth . invoke(methobj,arglist);
Integer retval =(Integer)ret obj;
system . out . println(retval . int value());
}
接住(可投擲的e ){
system . err . println(e);
}
}
}
6.創建新對象
導入Java . lang . reflect . *;
公共類構造器2 {
公共構造器2()
{
}
公共建築商2(int a,int b)
{
System.out.println(
“a =“+a+“b =“+b);
}
公共靜態void main(字符串參數【】)
{
嘗試{
class cls = class . forname(“constructor 2“);
Class party pes【】=新類【2】;
party pes【0】=整數。類型;
party pes【1】=整數。類型;
構造者ct
= cls . get constructor(party pes);
Object arglist【】=新對象【2】;
arglist【0】=新整數(37);
arglist【1】=新整數(47);
object ret obj = CT . new instance(arg list);
}
接住(可投擲的e ){
system . err . println(e);
}
}
}
7.更改類實例中的數據值
導入Java . lang . reflect . *;
公共類字段2 {
公立雙d;
公共靜態void main(字符串參數【】)
{
嘗試{
class cls = class . forname(“field 2“);
field fld = cls . get field(“d“);
field 2 F2 obj = new field 2();
system . out . println(“d =“+F2 obj . d);
fld . set double(f2obj,12.34);
system . out . println(“d =“+F2 obj . d);
}
接住(可投擲的e ){
system . err . println(e);
}
}
}
使用反射創建可重用代碼:
1、對象工廠
對象工廠(字符串p ){
c類;
Object o = null
嘗試{
c = class . forname(p);//獲取類定義
o = c . new instance();//制作壹個新的
} catch(異常e ){
System.err.println(“不能生成“+p“);
}
返回o;
}
公共類ObjectFoundry {
公共靜態對象工廠(字符串p)
拋出ClassNotFoundException,
實例化異常,
IllegalAccessException {
class c = class . forname(p);
object o = c . new instance();
返回o;
}
}
2.動態檢測對象的身份而不是實例的身份。
公共靜態布爾
isKindOf(對象對象,字符串類型)
引發ClassNotFoundException {
//獲取對象和類型的類定義
class c = obj . getclass();
class tClass = class . forname(type);
而(c!=null)
if(c = = tClass)返回true
c = c . get superclass();
}
返回false
}