JAVA反射機制是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱爲java語言的反射機制。
JAVA反射(放射)機制:「程序運行時,容許改變程序結構或變量類型,這種語言稱爲動態語言」。從這個觀點看,Perl,Python,Ruby是動態語言,C++,Java,C#不是動態語言。可是JAVA有着一個很是突出的動態相關機制:Reflection,用在Java身上指的是咱們能夠於運行時加載、探知、使用編譯期間徹底未知的classes。換句話說,Java程序能夠加載一個運行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其對象實體、或對其fields設值、或喚起其methods。
中文名 JAVA反射機制 外文名 JAVA Reflection 所屬語言 JAVA 屬 性動態語言
目錄
1 功能
2 Class
3 取得途徑
4 API
5 源碼改動
6 更多信息
7 關於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將在稍後的探險活動中一一亮相。
1
2
3
4
5
6
7
8
9
public final class Class<T> implements Serializable,
java.lang.reflect.GenericDeclaration,
java.lang.reflect.Type,
java.lang.reflect.AnnotatedElement {
private Class() {}
public String toString() {
return ( isInterface() ? "interface " : (isPrimitive() ? "" : "class "))
+ getName();
}
上述爲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)
extendsAbstractSequentialList<E> //(6)
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);html
本文選自南京樓鳳java