Java和其他語言不同的是,Java是運行於Java虛擬機器(JVM)。這意味著編譯後的程式碼是以一種和平台無關的格式保存的,而不是某種特定的機器上運行的格式。這種格式和傳統的可執行程式碼格式有許多重要的區別。具體來說,不同於C或C++程序,Java程式不是一個獨立的可執行文件,而是由很多分開的類別文件組成,每個類別文件對應一個Java類別。 另外,這些類別文件並不是馬上載入到內存,而是當程式需要的時候才會載入。 類別載入器就是Java虛擬機器中用來把類別載入到記憶體的工具。而且,Java類別載入器也是用Java實現的。這樣你就不需要對Java虛擬機有深入的理解就可以輕鬆創建自己的類別載入器了。
為什麼要創建類別載入器?
既然Java虛擬機已經有了類別載入器,我們還要自己創建其他的嗎?問得好。預設的類別載入器只知道如何從本機系統載入類別。當你的程式完全在本機編譯的話,預設的類別載入器一般都運作的很好。但是Java中最令人興奮的地方之一就是很容易的從網路上而不只是本地加載類別。
舉個例子,瀏覽器可以透過自訂的類別載入器載入類別。 還有很多載入類別的方式。除了簡單的從本地或網路外,你還可以透過自訂Java中最令人興奮的地方之一:
* 執行非信任代碼前自動驗證數位簽名
* 根據使用者提供的密碼解密代碼
* 根據用戶的需要動態的創建類你關心的任何東西都能方便的以字節碼的形式集成到你的應用中自定義類加載器的例子如果你已經使用過JDK(Java軟體開發包)中的appletviewer(小應用程式瀏覽器)或其他
Java嵌入式瀏覽器,你就已經使用了自訂類別載入器了。 Sun剛發布Java語言的時候,最令人興奮的一件事就是觀看Java如何執行從遠端網站下載的程式碼。執行從遠端站點透過HTT
P連結傳送來的字節碼看起來有點不可思議。之所以能夠運作,因為Java有安裝自訂類別載入器的能力。小型應用程式瀏覽器包含了一個類別載入器,這個類別載入器不從本地找Java類,而是存取遠端伺服器,透過HTTP載入原始字節碼文件,然後在Java虛擬機器中轉化為Java類別。當然類別載入器也做了其他的很多事情:他們阻止不安全的Java類,而且保持不同頁面上的不同小程式不會互相干擾。 Luke Gorrie寫的一個包Echidna是一個開放的Java軟體包,他允許在一個Java虛擬機中安全的運行多個Java應用程式。它透過使用自訂類別載入器給每個應用程式一份類別檔案的拷貝來阻止應用程式之間的干擾。
java類別載入器:
java中預設有三種類載入器:引導類別載入器,擴充類別載入器,系統類別載入器(也叫應用類別載入器)
類別載入器是Java最強大的功能之一。但是開發者常常忘記類別載入元件。類別載入器是在執行時間負責尋找和載入類別檔案的類別。 Java允許使用不同的類別載入器,甚至自訂的類別載入器。
Java 程式包含許多類別文件,每一個都與單一Java類別相對應,這些類別文件不像靜態C程序,一次性載入入內存,它們隨時需要隨時載入。這就是類別載入器與眾不同的地方。它從原始檔(通常是.class 或.jar檔)獲得不依賴平台的字節碼,然後將它們載入到JVM記憶體空間,所以它們可以被解釋和執行。預設狀態下,應用程式的每個類別由java.lang.ClassLoader載入。因為它可以被繼承,所以可以自由地加強其功能。
自訂類別載入器
import java.io.*;
import java.net.*;
import java.util.*;
import java.lang.reflect.Method;
public class CustomClassLoader extends URLClassLoader {
private FileInputStream input = null; //檔案輸入流
private ByteArrayOutputStream out = null; //位元組陣列輸出流
private String[] url = null; //類別檔案載入路徑
private byte[] data = null; //類別檔案字節碼
private String extensionalName = ""; //類別檔案副檔名
public CustomClassLoader(URL[] urls) throws Exception{
super(urls);
this.url = new String[urls.length];
for (int i = 0; i < urls.length; i++) {
this.url[i] = urls[i].toURI().toString();
}
}
/*
* 解析URL
*/
private void setFilePath() {
for (int i = 0; i < this.url.length; i++) {
if (this.url[i].substring(0,4).toLowerCase().equals("file") == true) {
this.url[i] = this.url[i].substring(5);
}
}
}
/*
* 取得指定類別名稱(套件名稱+類別名稱)檔案的字節碼
* @name name String
* @return byte[]
*/
private byte[] getFileData(String name) {
try {
this.setFilePath();
for (String url : this.url) {
String fileName = url + name.replace('.', '/').concat(".") +
this.getExtensionalName();
input = new FileInputStream(new File(fileName));
if (input != null) {
break;
}
}
out = new ByteArrayOutputStream();
data = new byte[1024];
int len = -1;
while ((len = input.read(data)) != -1) {
out.write(data, 0, len);
}
data = out.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (input != null)
input.close();
if (out != null)
out.close();
return data;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
/*
* 根據指定類別名稱尋找類別文件
* @param name String
* @return Class
*/
protected Class findClassByName(String name) {
try {
byte[] data = this.getFileData(name);
if (data == null) {
return null;
}
return this.defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/*
* 重寫loadClass()方法
* @param name String
* @return Class
*/
public Class loadClass(String name) {
Class c = null;
try {
c = super.loadClass(name);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (c == null) //父類別預設方法沒有載入到指定類別時,使用自訂方法尋找
c = this.findClassByName(name);
return c;
}
}
public String getExtensionalName() {
return extensionalName;
}
public void setExtensionalName(String extensionalName) {
this.extensionalName = extensionalName;
}
public static void main(String[] args) throws Exception {
URL[] url = new URL[] {new URL("file:e:/")}; //新增你想要載入類別的路徑
//網路或本地均可
CustomClassLoader csl = new CustomClassLoader(url);
csl.setExtensionalName("rs");
Class c1 = csl.loadClass("com.demo");
Object obj = c1.newInstance();
Method method = c1.getMethod("printText", null);
method.invoke(obj, null);
}