1. 程式人生 > >大白話: JAVA反射機制

大白話: JAVA反射機制

功能

Java反射機制主要提供了以下功能: 在執行時判斷任意一個物件所屬的類;在執行時構造任意一個類的物件;在執行時判斷任意一個類所具有的成員變數和方法;在執行時呼叫任意一個物件的方法;生成動態代理。 有時候我們說某個語言具有很強的動態性,有時候我們會區分動態和靜態的不同技術與作法。我們朗朗上口動態繫結(dynamic binding)、動態連結(dynamic linking)、動態載入(dynamic loading)等。然而“動態”一詞其實沒有絕對而普遍適用的嚴格定義,有時候甚至像面向物件當初被匯入程式設計領域一樣,一人一把號,各吹各的調。 一般而言,開發者社群說到動態語言,大致認同的一個定義是:“程式執行時,允許改變程式結構或變數型別,這種語言稱為動態語言”。從這個觀點看,Perl,Python,Ruby是動態語言,C++,Java,C#不是動態語言。 儘管在這樣的定義與分類下Java不是動態語言,它卻有著一個非常突出的動態相關機制:Reflection。這個字的意思是“反射、映象、倒影”,用在Java身上指的是我們可以於執行時載入、探知、使用編譯期間完全未知的classes。換句話說,Java程式可以載入一個執行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其物件實體、或對其fields設值、或喚起其methods。這種“看透class”的能力(the ability of the program to examine itself)被稱為introspection(內省、內觀、反省)。Reflection和introspection是常被並提的兩個術語。 Java如何能夠做出上述的動態特性呢?這是一個深遠話題,本文對此只簡單介紹一些概念。整個篇幅最主要還是介紹Reflection APIs,也就是讓讀者知道如何探索class的結構、如何對某個“執行時才獲知名稱的class”生成一份實體、為其fields設值、呼叫其methods。本文將談到java.lang.Class,以及
java.lang.reflect
中的Method、Field、Constructor等等classes。

Class

眾所周知Java有個Object 類,是所有Java 類的繼承根源,其內聲明瞭數個應該在所有Java 類中被改寫的方法:hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一個Class 物件。 Class 類十分特殊。它和一般類一樣繼承自Object,其實體用以表達Java程式執行時的classes和interfaces,也用來表達enum、array、primitive Java types(boolean, byte, char, short, int, long, float, double)以及關鍵詞void。當一個class被載入,或當載入器(class loader)的defineClass()被JVM呼叫,JVM 便自動產生一個Class 物件。如果您想借由“修改Java標準庫原始碼”來觀察Class 物件的實際生成時機(例如在Class的constructor內新增一個println()),這樣是行不通的!因為Class並沒有public constructor。 Class是Reflection故事起源。針對任何您想探勘的類,唯有先為它產生一個Class 物件,接下來才能經由後者喚起為數十多個的Reflection APIs。這些APIs將在稍後的探險活動中一一亮相。 public final class Class<T> implements Serializable, java.lang.reflect.Type, java.lang.reflect.AnnotatedElement { private Class() {} public String toString() { return ( isInterface() ? "interface " : (isPrimitive() ? "" : "class ")) + getName(); } ... 圖1:Class class片段。注意它的private Class() {},意指不允許任何人經由程式設計方式產生Class object。是的,其object 只能由JVM 產生。

取得途徑

Java允許我們從多種管道為一個class生成對應的Class object。圖2是一份整理。 Class object 誕生管道 示例: 1)運用getClass() 注:每個class 都有此函式 String str = "abc"; Class c1 = str.getClass(); 2)運用Class.getSuperclass() Button b = new Button(); Class c1 = b.getClass(); Class c2 = c1.getSuperclass(); 3)運用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 (".class"); 4)運用primitive wrapper classes的TYPE 語法 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; 圖2:Java 允許多種管道生成Class object。 Java classes 組成分析 首先容我以圖3的java.util.LinkedList為例,將Java class的定義大卸八塊,每一塊分別對應圖4所示的Reflection API。圖5則是“獲得class各區塊資訊”的程式示例及執行結果,它們都取自本文示例程式的對應片段。 packagejava.util; //(1) import java.lang.*; //(2) public class LinkedList<E> //(3)(4)(5) implements List<E>, Queue<E>, Cloneable, .Serializable //(7) { private static class Entry<E> { … }//(8) public LinkedList() { … } //(9) public LinkedList(Collection<? extends E> c) { … } public E getFirst() { … } //(10) public E getLast() { … } private transient Entry<E> header = …; //(11) private transient int size = 0; } 圖3:將一個Java class 大卸八塊,每塊相應於一個或一組Reflection APIs(圖4)。

API

圖3的各個Java class成份,分別對應於圖4的Reflection API,其中出現的Package、Method、Constructor、Field等等classes,都定義於java.lang.reflect。 Java class 內部模組(參見圖3) Java class 內部模組說明 相應之Reflection API,多半為Class methods。 返回值型別(return type) (1) package class隸屬哪個package getPackage() Package (2) import class匯入哪些classes 無直接對應之API。 解決辦法見圖5-2。 (3) modifier class(或methods, fields)的屬性 int getModifiers() Modifier.toString(int) Modifier.isInterface(int) int String bool (4) class name or interface name class/interface 名稱getName() String (5) type parameters 引數化型別的名稱 getTypeParameters() TypeVariable <Class>[] (6) base class base class(只可能一個) getSuperClass() Class (7) implemented interfaces 實現有哪些interfaces getInterfaces() Class[] (8) inner classes 內部classes getDeclaredClasses() Class[] (8') outer class 如果我們觀察的class 本身是inner classes,那麼相對它就會有個outer class。 getDeclaringClass() Class (9) constructors 建構函式getDeclaredConstructors()不論 public 或private 或其它access level,皆可獲得。另有功能近似之取得函式。 Constructor[] (10) methods 操作函式getDeclaredMethods() Method[] (11) fields 欄位(成員變數) getDeclaredFields() Field[] 圖4:Java class大卸八塊後(如圖3),每一塊所對應的Reflection API。本表並非 Reflection APIs 的全部。 Java Reflection API 運用示例 圖5示範圖4提過的每一個Reflection API,及其執行結果。程式中出現的tName()是個輔助函式,可將其第一自變數所代表的“Java class完整路徑字串”剝除路徑部分,留下class名稱,儲存到第二自變數所代表的一個hashtable去並返回(如果第二自變數為null,就不儲存而只是返回)。 #001 Class c = null; #002 c = Class.forName(args[0]); #003 #004 Package p; #005 p = c.getPackage(); #006 #007 if (p != null) #008 System.out.println("package "+p.getName()+";"); 執行結果(例): package java.util; 圖5-1:找出class 隸屬的package。其中的c將繼續沿用於以下各程式片段。 #001 ff = c.getDeclaredFields(); #002 for (int i = 0; i < ff.length; i++) #003 x = tName(ff.getType().getName(), classRef); #004 #005 cn = c.getDeclaredConstructors(); #006 for (int i = 0; i < cn.length; i++) { #007 Class cx[] = cn.getParameterTypes(); #008 for (int j = 0; j < cx.length; j++) #009 x = tName(cx[j].getName(), classRef); #010 } #011 #012 mm = c.getDeclaredMethods(); #013 for (int i = 0; i < mm.length; i++) { #014 x = tName(mm.getReturnType().getName(), classRef); #015 Class cx[] = mm.getParameterTypes(); #016 for (int j = 0; j < cx.length; j++) #017 x = tName(cx[j].getName(), classRef); #018 } #019 classRef.remove(c.getName()); //不必記錄自己(不需import 自己) 執行結果(例): importjava.util.ListIterator; import java.lang.Object; importjava.util.LinkedList$Entry; importjava.util.Collection; import ObjectOutputStream; import .ObjectInputStream; 圖5-2:找出匯入的classes,動作細節詳見內文說明。 #001 int mod = c.getModifiers(); #002 System.out.print(Modifier.toString(mod)); //整個modifier #003 #004 if (Modifier.isInterface(mod)) #005 System.out.print(" "); //關鍵詞 "interface" 已含於modifier #006 else #007 System.out.print(" class "); //關鍵詞 "class" #008 System.out.print(tName(c.getName(), null)); //class 名稱 執行結果(例): public class LinkedList 圖5-3:找出class或interface 的名稱,及其屬性(modifiers)。 #001 TypeVariable<Class>[] tv; #002 tv = c.getTypeParameters(); //warning: unchecked conversion #003 for (int i = 0; i < tv.length; i++) { #004 x = tName(tv.getName(), null); //例如 E,K,V... #005 if (i == 0) //第一個 #006 System.out.print("<" + x); #007 else //非第一個 #008 System.out.print("," + x); #009 if (i == tv.length-1) //最後一個 #010 System.out.println(">"); #011 } 執行結果(例): public abstract interface Map<K,V> 或 public class LinkedList<E> 圖5-4:找出parameterized types 的名稱 #001 Class supClass; #002 supClass = c.getSuperclass(); #003 if (supClass != null) //如果有super class #004 System.out.print(" extends" + #005 tName(supClass.getName(),classRef)); 執行結果(例): public class LinkedList<E> extends AbstractSequentialList 圖5-5:找出base class。執行結果多出一個不該有的逗號於尾端。此非本處重點,為簡化計,不多做處理。 #001 Class cc[]; #002 Class ctmp; #003 //找出所有被實現的interfaces #004 cc = c.getInterfaces(); #005 if (cc.length != 0) #006 System.out.print(", " + " implements "); //關鍵詞 #007 for (Class cite : cc) //JDK1.5 新式迴圈寫法 #008 System.out.print(tName(cite.getName(), null)+", "); 執行結果(例): public class LinkedList<E> extendsAbstractSequentialList, implements List, Queue, Cloneable, Serializable, 圖5-6:找出implemented interfaces。執行結果多出一個不該有的逗號於尾端。此非本處重點,為簡化計,不多做處理。 #001 cc = c.getDeclaredClasses(); //找出inner classes #002 for (Class cite : cc) #003 System.out.println(tName(cite.getName(), null)); #004 #005 ctmp = c.getDeclaringClass(); //找出outer classes #006 if (ctmp != null) #007 System.out.println(ctmp.getName()); 執行結果(例): LinkedList$Entry LinkedList$ListItr 圖5-7:找出inner classes 和outer class #001 Constructor cn[]; #002 cn = c.getDeclaredConstructors(); #003 for (int i = 0; i < cn.length; i++) { #004 int md = cn.getModifiers(); #005 System.out.print(" " + Modifier.toString(md) + " " + #006 cn.getName()); #007 Class cx[] = cn.getParameterTypes(); #008 System.out.print("("); #009 for (int j = 0; j < cx.length; j++) { #010 System.out.print(tName(cx[j].getName(), null)); #011 if (j < (cx.length - 1)) System.out.print(", "); #012 } #013 System.out.print(")"); #014 } 執行結果(例): publicjava.util.LinkedList(Collection) publicjava.util.LinkedList() 圖5-8a:找出所有constructors #004 System.out.println(cn.toGenericString()); 執行結果(例): publicjava.util.LinkedList(java.util.Collection<? extends E>) publicjava.util.LinkedList() 圖5-8b:找出所有constructors #001 Method mm[]; #002 mm = c.getDeclaredMethods(); #003 for (int i = 0; i < mm.length; i++) { #004 int md = mm.getModifiers(); #005 System.out.print(" "+Modifier.toString(md)+" "+ #006 tName(mm.getReturnType().getName(), null)+" "+ #007 mm.getName()); #008 Class cx[] = mm.getParameterTypes(); #009 System.out.print("("); #010 for (int j = 0; j < cx.length; j++) { #011 System.out.print(tName(cx[j].getName(), null)); #012 if (j < (cx.length - 1)) System.out.print(", "); #013 } #014 System.out.print(")"); #015 } 執行結果(例): public Object get(int) public int size() 圖5-9a:找出所有methods #004 System.out.println(mm.toGenericString()); public Ejava.util.LinkedList.get(int) public intjava.util.LinkedList.size() 圖5-9b:找出所有methods。本例在for 迴圈內使用toGenericString(),省事。 #001 Field ff[]; #002 ff = c.getDeclaredFields(); #003 for (int i = 0; i < ff.length; i++) { #004 int md = ff.getModifiers(); #005 System.out.println(" "+Modifier.toString(md)+" "+ #006 tName(ff.getType().getName(), null) +" "+ #007 ff.getName()+";"); #008 } 執行結果(例): private transient LinkedList$Entry header; private transient int size; 圖5-10a:找出所有fields #004 System.out.println("G: " + ff.toGenericString()); private transientjava.util.LinkedList.java.util.LinkedList$Entry<E> ?? java.util.LinkedList.header private transient intjava.util.LinkedList.size 圖5-10b:找出所有fields。本例在for 迴圈內使用toGenericString(),省事。 找出class參用(匯入)的所有classes 沒有直接可用的Reflection API可以為我們找出某個class參用的所有其它classes。要獲得這項資訊,必須做苦工,一步一腳印逐一記錄。我們必須觀察所有fields的型別、所有methods(包括constructors)的引數型別和回返型別,剔除重複,留下唯一。這正是為什麼圖5-2程式程式碼要為tName()指定一個hashtable(而非一個null)作為第二自變數的緣故:hashtable可為我們儲存元素(本例為字串),又保證不重複。 本文討論至此,幾乎可以還原一個class的原貌(唯有methods 和ctors的定義無法取得)。接下來討論Reflection 的另三個動態性質:(1) 執行時生成instances,(2) 執 行期喚起methods,(3) 執行時改動fields。 執行時生成instances 欲生成物件實體,在Reflection 動態機制中有兩種作法,一個針對“無自變數ctor”, 一個針對“帶引數ctor”。圖6是面對“無自變數ctor”的例子。如果欲呼叫的是“帶引數ctor“就比較麻煩些,圖7是個例子,其中不再呼叫Class的newInstance(),而是呼叫Constructor 的newInstance()。圖7首先準備一個Class[]做為ctor的引數型別(本例指定為一個double和一個int),然後以此為自變數呼叫getConstructor(),獲得一個專屬ctor。接下來再準備一個Object[] 做為ctor實參值(本例指定3.14159和125),呼叫上述專屬ctor的newInstance()。 #001 Class c = Class.forName("DynTest"); #002 Object obj = null; #003 obj = c.newInstance(); //不帶自變數 #004 System.out.println(obj); 圖6:動態生成“Class object 所對應之class”的物件實體;無自變數。 #001 Class c = Class.forName("DynTest"); #002 Class[] pTypes = new Class[] { double.class, int.class }; #003 Constructor ctor = c.getConstructor(pTypes); #004 //指定parameter list,便可獲得特定之ctor #005 #006 Object obj = null; #007 Object[] arg = new Object[] {3.14159, 125}; //自變數 #008 obj = ctor.newInstance(arg); #009 System.out.println(obj); 圖7:動態生成“Class object 對應之class”的物件實體;自變數以Object[]表示。 執行時呼叫methods 這個動作和上述呼叫“帶引數之ctor”相當類似。首先準備一個Class[]做為ctor的引數型別(本例指定其中一個是String,另一個是Hashtable),然後以此為自變數呼叫getMethod(),獲得特定的Method object。接下來準備一個Object[]放置自變數,然後呼叫上述所得之特定Method object的invoke(),如圖8。知道為什麼索取Method object時不需指定回返型別嗎?因為method overloading機制要求signature(署名式)必須唯一,而回返型別並非signature的一個成份。換句話說,只要指定了method名稱和引數列,就一定指出了一個獨一無二的method。 #001 public String func(String s, Hashtable ht) #002 { #003 …System.out.println("func invoked"); return s; #004 } #005 public static void main(String args[]) #006 { #007 Class c = Class.forName("Test"); #008 Class ptypes[] = new Class[2]; #009 ptypes[0] = Class.forName("java.lang.String"); #010 ptypes[1] = Class.forName("java.util.Hashtable"); #011 Method m = c.getMethod("func",ptypes);

相關推薦

大白話 JAVA反射機制

功能 Java反射機制主要提供了以下功能: 在執行時判斷任意一個物件所屬的類;在執行時構造任意一個類的物件;在執行時判斷任意一個類所具有的成員變數和方法;在執行時呼叫任意一個物件的方法;生成動態代理。 有時候我們說某個語言具有很強的動態性,有時候我們會區分動態和靜態的不同技術與作法。我們朗朗上口動態繫

java反射機制學習初始反射機制

pro sta 加載 初始 完整 tac demo 獲得 sys   本人小白一枚,想和大家一起分享我學習java的筆記和心得。 反射機制: 指的是可以於運行時加載、探知、使用編譯期間完全未知的類。 程序在運行狀態中,可以動態加載一個只有名稱的類,對於任意一個已加載的

Spring學習5(1)IoC容器之IoC概述,JAVA反射機制,資源訪問器

spring學習5(1)  在經過了對spring框架基本開發的瞭解以及對spring boot流程的學習,《精通spring4.x…》這本書正式開始了spring的講解,我也跟隨著這本書的腳步進行學習。 IoC概述  首先需要學習的是spring的IoC技術,

java反射機制(2)- 實踐反射機制+動態代理實現模擬RMI遠端方法呼叫

1 涉及主要知識點   1、RMI(Remote Method Invocation):遠端方法呼叫是一種計算機之間利用遠端物件互相呼叫實現雙方通訊的一種通訊機制。使用這種機制,某一臺計算機上

java反射機制

else ++ 類型 應該 動態 error param 字母 什麽 最近在做一個項目。 需求是這樣的,前端傳一個參數param表示要從服務器獲取的參數,後端需要把對應的參數從服務器中取出來。 本來覺得沒什麽,應該蠻簡單。結果一看表,嗯,40多個參數,如果用if...els

Java反射機制深入詳解

const 運行時 設計 應用程序 類加載器 分配 import 程序 為什麽 一.概念   反射就是把Java的各種成分映射成相應的Java類。   Class類的構造方法是private,由JVM創建。   反射是java語言的一個特性,它允程序在運行時(註意不是編譯的

利用JAVA反射機制實現調用私有方法

parse try ble cat 權限 利用 enabled tde mod 1.fragment是AccessibilityFragment的對象。須要被調用的方法的類。 setAccessible(true)並非將方法的訪問權限改成了public。而是取

Java反射機制詳解一

java 反射 反射機制 工廠模式 1反射機制是什麽反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。在面向對象的世界裏,萬事萬物皆對象.在ja

java 反射機制 觀點

pan sse clipboard star -s cin clip att repl 反射,當時經常聽他們說,自己也看過一些資料,也可能在設計模式中使用過,但是感覺對它沒有一個較深入的了解,這次重新學習了一下,感覺還行吧! 一,先看一下反

候捷談Java反射機制

將在 內省 編程 pub pop 批處理 cdecl 清理 void 2013-06-12 22:51 1690人閱讀 評論(0) 收藏 舉報 分類: JAVA(51) 傳送門:http://blog.csdn.net/njchenyi/article

Java反射機制詳解

ride length 數組大小 conf array arraycopy 動態調用 ray info Java反射機制詳解 |目錄 1反射機制是什麽 2反射機制能做什麽 3反射機制的相關API ·通過一個對象獲得完整的包名和類名 ·實例化Class類對象 ·獲

java 反射機制

選擇 count 語言 用戶 方案 獲取 接口 功能 .cn 一、java反射機制是什麽?   JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱為j

Java反射機制demo(五)—獲得並調用一個類中的方法

color 擁有 oca logs over super getmethod equals() bool 這個demo在使用反射機制操作屬性之前,主要原因是因為在.class文件字節碼中,方法排在屬性的前面。 1,獲得一個類中的方法 先看一下方法和運行結果。獲取所有的方

javajava反射機制,動態獲取對象的屬性和對應的參數值,並屬性按照字典序排序,Field.setAccessible()方法的說明【可用於微信支付 簽名生成】

modifier 直接 this 字段值 1-1 讓我 toupper ima play 方法1:通過get()方法獲取屬性值 package com.sxd.test.controller; public class FirstCa{ private

Android小例子使用反射機制來讀取圖片制作一個圖片瀏覽器

onclick lac protected blank pri tar lec 存儲 current 效果圖:                                 工程文件夾:   該例子可供於新手參考練習,如果有哪裏不對的地方,望指正>-< 《

java反射機制淺析

成員變量 java語言 運行時 淺析 表示 運行 如何 信息 動態獲取 Java反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為Java語言的反射

java反射機制實現攔截器

tor 攔截 stat 重要 obj static interface 程序 bject 實現一個攔截器必須要實現一下幾個類: 1 目標類接口:目標類要實現的接口。 package com.lanvis.reflect; public interface ITarge

Java反射機制能夠獲取的信息,與應用

rri 代理 pan [] reflect 語言 子類 list tro 一、什麽是Java反射機制? 【1】反射機制是在運行狀態中,對於任何一個類,都能夠知道這個類的所有屬性和方法; 【2】對於任意一個對象,都能夠調用它的任意一個屬性和方法; 像這種動態獲取類的信

Java基礎——Java反射機制

靜態 load super actual 返回 generic ref 基本 有一個 Reflection(反射)是Java被視為動態語言的關鍵,反射機制允許程序在執行期借助於Reflection API取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法 Java

隨筆12 java反射機制

ron fields () exception 修飾符 等等 img get nbsp 一:反射機制 Java反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方