Java的動態載入和反射
什麼是動態載入?靜態呢?
new建立物件的方式稱作為靜態載入,而使用Class.forName("XXX")稱作為動態載入,它們倆本質的區別在於靜態載入的類的源程式在編譯時期載入(必須存在),而動態載入的類在編譯時期可以缺席(源程式不必存在)。
哪些語言是靜態的?哪些是動態的?
程式執行時,允許改變程式結構或變數型別,這種語言稱為動態語言。 從這個觀點看,Perl,Python,Ruby是動態語言,而C++,Java,C#不是動態語言。 但是JAVA有著一個非常突出的動態相關機制:Reflection,用在Java身上指的是我們可以於執行時載入、探知、使用編譯期間完全未知的classes。
什麼是反射?
Reflection 是 Java被視為動態(或準動態)語言的一個關鍵性質。這個機制允許程式在執行時 通過 Reflection APIs 取得任何一個已知名稱的 class 的內部資訊,包括其 modifiers(諸如 public, static 等等)、superclass(例如 Object)、實現的 interfaces,也包括 fields 和 methods 的所有 資訊,並可於執行時改變 fields內容或呼叫 Constructor,methods。 簡而言之,JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意方法和屬性
原來的載入用的好好的,為什麼要使用反射?
動態載入最大限度體現了程式的靈活性,降低了類之間的耦合性。方便了框架和其他的使用。或者遊戲更新的時候,不可能將遊戲刪除重新下載,只是下載更新包,然後由遊戲程式執行時動態載入程式碼檔案,得到更新的程式碼編譯,這就用到了反射。反射的目的就是為了擴充套件未知的應用。 反射機制的缺點:對效能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什麼並且它滿足我們的要求。這類操作總是慢於只直接執行相同的操作。
話不多說。進入ctrl + v 貼程式碼模式測試。
為了更好的使用反射,這裡寫了兩個類用來熟悉動態載入。
被載入類
準備了公有的屬性、構造方法、無參方法以及有引數的方法,還有私有的屬性、構造方法、無參方法以及有引數的方法。
package com.example.反射;
public class One {
public int a = 1;
private int b = 2;
public One() {}
private One(int a) {
System.out.println("私有構造方法");
}
public void demo1() {
System.out.println("呼叫公有方法demo1");
}
private void demo2() {
System.out.println("呼叫私有方法demo2");
}
public void demo3(int a) {
System.out.println("呼叫有引數的公有方法demo3,引數為"+a);
}
private void demo4(int b) {
System.out.println("呼叫有引數的私有方法demo4,引數為"+b);
}
}
測試
1 首先是對檔案class檔案進行載入,獲得Class檔案物件,獲取Class檔案物件的方法有三種:
1.Object ——> getClass(); 2.任何資料型別(包括基本資料型別)都有一個“靜態”的class屬性 3.通過Class類的靜態方法:forName(String className)(常用),className是'包名.類名'。
Class cl = Class.forName("com.example.反射.One");
2 獲取Class物件的構造方法,通過class物件 cl 呼叫方法獲取構造方法
* 這裡需要用到Constructor類,Constructor 提供關於類的單個構造方法的資訊以及對它的訪問許可權
* setAccessible(true)方法表示取消java語言訪問檢查,在訪問私有屬性、方法之類時需要使用
//獲得公有的構造方法
Constructor con1 = cl.getConstructor();
System.out.println("呼叫公有"+con1);
//獲得私有的構造方法
Constructor con2 = cl.getDeclaredConstructor(int.class);
con2.setAccessible(true);
System.out.println("呼叫私有"+con2);
3 建立 Class類物件,也可以說是上面建立的Class檔案物件的物件
* 建立Class類的物件時,可以通過構造方法建立,也可以通過Class檔案物件建立。
// 建立類的物件,這裡通過Class檔案物件建立
Object obj = cl.newInstance();
* 通過構造方法建立,自定義的構造方法建立物件時可以傳入引數。
Object obj1 = con1.newInstance();
Object obj2 = con2.newInstance(5);
4 獲得Class類的屬性,通過Class類的物件 cl 獲得屬性
//公有屬性
Field field1 = cl.getField("a");
System.out.println(field1.get(obj));//得到變數的值
//私有屬性
Field field2 = cl.getDeclaredField("b");
field2.setAccessible(true);//該方法表示取消java語言訪問檢查
//field2.set(obj, 5);//這裡可以給變數重新賦值
System.out.println(field2.get(obj));
5 獲取Class類的無參方法以及呼叫
* 呼叫方法時通過方法物件找到Class類的物件
// 獲取類物件的無參公有方法
Method m1 = cl.getMethod("demo1");
//無參私有方法
Method m2 = cl.getDeclaredMethod("demo2");
m2.setAccessible(true);//該方法表示取消java語言訪問檢查
// 呼叫方法
m1.invoke(obj);
m2.invoke(obj);
6 獲取Class類的有參方法以及呼叫
//有參公有方法
Method m3 = cl.getMethod("demo3",int.class);
//有參私有方法
Method m4 = cl.getDeclaredMethod("demo4", int.class);
m4.setAccessible(true);
//呼叫方法
m3.invoke(obj,2);
m4.invoke(obj, 10);
Java的反射是框架的基礎,也用於程式的更新,這些都歸功於Java可以實現動態建立物件和編譯。但並不是Java反射是完美的,相反,有利就有弊。反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什麼並且它 滿足我們的要求。這類操作總是慢於只直接執行相同的操作。