JAVA反射機制及CLASS.FORNAME的作用及含義
最近由於工作上需要,對reflection做了一番瞭解,以下是學習總結,有不少內容是借鑑的,但已無法找到源文出處,還請原文作者見諒。
Reflection 是Java被視為動態(或準動態)語言的一個關鍵性質。這個機制允許程式在執行時透過Reflection APIs取得任何一個已知名稱的class
的內部資訊,包括其modifiers(諸如public, static 等等)、superclass(例如Object)、實現之interfaces(例如Cloneable),也包括fields
和methods的所有資訊,並可於執行時改變fields內容或喚起methods。
Java為什麼能夠支援Reflection?答案是Java執行時仍然擁有型別資訊,它包含了這個類一切:它有哪些欄位、哪些方法,各是何種保護級別等等,還有這個類依賴於哪些類。在Java中,類資訊以物件的形式存放,這些物件是一種元物件,它們的型別就是Class。擁有了這些資訊,無論是動態建立物件還是呼叫某些方法都是輕而易舉的。在C 中,通過RTTI(執行時型別識別),我們也可以知道類的一些資訊,但為什麼C 中卻沒有Reflection,原因是型別資訊不完整。RTTI這個名字本身就告訴我們,C 的型別資訊是用來進行型別識別的,因此,它也不需要其它額外的資訊。並不是C 無法做到這一點,而是C 不希望給使用者增加額外的負擔。有所得,必然有所失,因此,C 放棄了元物件。關於這一點,C 之父Bjarne Stroustrup在他的《C 語言的設計與演化》的14.2.8節中進行了深入的討論。
“Class”class
眾所周知Java有個Object class,是所有Java classes的繼承根源,其內聲明瞭數個應該在所
有Java class中被改寫的methods:hashCode()、equals()、clone()、toString()、
getClass()等。其中getClass()返回一個Class object。
Class class十分特殊。它和一般classes一樣繼承自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 object。如果您想借由“修改Java標準庫原始碼”來觀察Class object的實際生成
時機(例如在Class的constructor內新增一個println()),不能夠!因為Class並沒有
public constructor。
Class是Reflection故事起源。針對任何您想探勘的class,唯有先為它產生一個Class
object,接下來才能經由後者喚起為數十多個的Reflection APIs。這些APIs將在稍後的探險
活動中一一亮相。
#001 public final
#002 class Class<T> implements java.io.Serializable,
#003 java.lang.reflect.GenericDeclaration,
#004 java.lang.reflect.Type,
#005 java.lang.reflect.AnnotatedElement {
#006 private Class() {}
#007 public String toString() {
#008 return ( isInterface() ? "interface " :
#009 (isPrimitive() ? "" : "class "))
#010 + getName();
#011 }
...
圖1:Class class片段。注意它的private empty ctor,意指不允許任何人經由程式設計方式產生Class object。是的,其object 只能由
JVM 產生。
“Class” object的取得途徑
Java允許我們從多種管道為一個class生成對應的Class object。圖2是一份整理。
Class object 誕生管道示例
運用getClass()
注:每個class 都有此函式
String str = "abc";
Class c1 = str.getClass();
運用 Class.getSuperclass()
Button b = new Button();
Class c1 = b.getClass();
Class c2 = c1.getSuperclass();
運用static methodClass.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");
運用 .class 語法
Class c1 = String.class;
Class c2 = java.awt.Button.class;
Class c3 = Main.InnerClass.class;
Class c4 = int.class;
Class c5 = int[].class;
Class.forName(xxx.xx.xx) 返回的是一個類, .newInstance() 後才建立一個物件 Class.forName(xxx.xx.xx);的作用是要求JVM查詢並載入指定的類,也就是說JVM會執行該類的靜態程式碼段
Class aClass = Class.forName(xxx.xx.xx);
Object anInstance = aClass.newInstance();
Class.forName("").newInstance()返回的是object
但是建立例項時有個限制,你的class建構函式不能包括引數,而且你需要自己來手工cast例項
用時:例項:
protected final static String CLASS_TEST = "Test";
protected final static String CLASS_PROBLEM = "Problem";
protected final static String CLASS_PARAMETER = "Parameter";
getProblem(vx, vy){
Object result;
result = Class.forName(CLASS_PROBLEM).newInstance();
........
}
getParameters(){
Object result;
result = Class.forName(CLASS_PARAMETER).newInstance();
........
}
buildTest(){
String error_msg = (String) invokeMethod(
Class.forName(CLASS_TEST).newInstance(),
"add",
new Class[]{
Class.forName(CLASS_PROBLEM),
Class.forName(CLASS_PARAMETER)},
new Object[]{
getProblem(vx, vy),
getParameters()});
}
protected Object invokeMethod(Object o, String name, Class[] paramClasses, Object[] paramValues) {
Method m;
Object result;
result = null;
try {
m = o.getClass().getMethod(name, paramClasses);
result = m.invoke(o, paramValues);
}
catch (Exception e) {
e.printStackTrace();
result = null;
}
return result;
}
class Test{
public static model train_model(problem prob, parameter param)
}
class Problem{
...........
}
Parameter{
...............
}
附:
大家或許有些疑問,jdbc連線資料庫時,有的寫法是Class.forName(xxx.xx.xx);而有一些:Class.forName(xxx.xx.xx).newInstance(),為什麼會有這兩種寫法呢?
Class.forName(xxx.xx.xx) 返回的是一個類,
.newInstance() 後才建立一個物件
Class.forName(xxx.xx.xx);的作用是要求JVM查詢並載入指定的類,也就是說JVM會執行該類的靜態程式碼段
在JDBC規範中明確要求這個Driver類必須向DriverManager註冊自己,即任何一個JDBC Driver的Driver類的程式碼都必須類似如下:
public class MyJDBCDriver implements Driver {
static {
DriverManager.registerDriver(new MyJDBCDriver());
}
}
所以我們在使用JDBC時只需要Class.forName(XXX.XXX);就可以了