摘要:示例如下動態類重新加載動態類重新加載有一些挑戰。為了上面代碼的正常運行,你當然需要實現自己的類加載器,讓接口或超類由其父類加載。
Java中可以在運行時加載和重新加載類,雖然并不像我們想像中那么簡單。本文將解釋何時、怎樣在Java中加載、重新加載類。
你可以爭論動態加載類是Java反射的一部分還是Java核心的一部分。不管怎樣,我把它放在了Java反射中,因為沒有更好的地方放置它。
Java程序的所有類都是使用 java.lang.ClassLoader的一些子類加載的。因此,動態加載類也必須使用 java.lang.ClassLoader的子類。
當一個類加載,它所引用的類也會被加載。類加載模式是遞歸加載的,直到所有需要的類加載完畢。這可能并不是應用程序的所有類。未被引用的類在引用前不會被加載。
類加載在Java中被組織成層級。當你創建一個獨立的ClassLoader,你必須提供一個父級ClassLoader。如果ClassLoader被請求加載一個類,它會請求它的父級ClassLoader去加載它。如果父級類加載器找不到這個類,子類加載器會嘗試自加載。
類加載類加載器加載類的步驟如下:
檢查該類是否已被加載
如類未加載,請求父類加載器加載它
如父類加載器不能加載該類,嘗試使用當前類加載器加載它
當你實現一個能夠重載類的類加載器時,你需要從這個序列中偏離一點。不應請求父類加載程序加載要重裝的類。稍后再談。
動態類加載動態加載類非常簡單。所有你需要做的是獲得一個ClassLoader并調用它的loadClass()方法。示例如下:
public class MainClass { public static void main(String[] args){ ClassLoader classLoader = MainClass.class.getClassLoader(); try { Class aClass = classLoader.loadClass("com.jenkov.MyClass"); System.out.println("aClass.getName() = " + aClass.getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } }動態類重新加載
動態類重新加載有一些挑戰。Java內建的類加載器在加載類之前總會檢查類是否已被加載。因此,使用Java的內置類加載器不可能重新加載類。重新加載一個類你必須實現自己的ClassLoader子類。
即使使用類加載器的自定義子類,也會遇到挑戰。所有已被加載的類都需要被鏈接。這個方法是final的,因此不能被你的ClassLoader子類重載。resolve()方法不允許ClassLoader實例鏈接一個類2次。因此,每當你需要重新加載類時,你必須重新創建一個ClassLoader類的實例。這不是不可能的,但必須知道何時設計類重新加載。
如上文述,不能使用加載指定類的ClassLoader重新加載這個類。因此,必須使用不同的ClassLoader加載這個類。但是,這會帶來新的問題。
Java程序中加載的每一個類都以其全限定名(包名+類名)標識,并且由ClassLoader實例加載。這意味著,類MyObject由類加載器A加載,是和由類加載器B加載的同一個類MyObject不相同。模擬代碼如下:
MyObject object = (MyObject) myClassReloadingFactory.newInstance("com.jenkov.MyObject");
注意,類MyObject在代碼中是如何引用的,是作為object類型的變量。這導致MyObject類被已加載過這個類的駐留代碼的類加載器加載。
如果myClassReloadingFactory對象工廠使用與駐留代碼不同的類加載器加載MyObject,你不能強制轉換重新加載的Object類型的變量MyObject為MyObject類型。因為這兩個MyObject由不同的類加載器加載,他們被視為不同的類,盡管他們擁有相同的全限定名。嘗試強轉一個object的類為另一個類的引用將拋出ClassCastException。
有可能繞過這個限制,但是你必須用兩種方式來改變你的代碼:
使用接口作為變量類型,并且只重新加載實現類
使用超類作為變量類型,并且只重新加載子類
這里是示例代碼:
MyObjectInterface object = (MyObjectInterface) myClassReloadingFactory.newInstance("com.jenkov.MyObject");
MyObjectSuperclass object = (MyObjectSuperclass) myClassReloadingFactory.newInstance("com.jenkov.MyObject");
如果變量類型是接口或超類,上面的代碼都會正常運行,接口或超類在重新加載實現或子類時不會被重新加載。
為了上面代碼的正常運行,你當然需要實現自己的類加載器,讓接口或超類由其父類加載。當你的類加載器被請求加載MyObject時,它也會被請求加載MyObjectInterface接口或者MyObjectSuperclass類,因為它們被MyObject類在內部引用。你的類加載器必須把類加載委派給相同的類加載器,即加載了接口或超類的類加載器。
上文包含了很多內容。讓我們看一下簡單的示例。下面是一個簡單的ClassLoader子類。注意它如何將類加載委托給它的父類,除了它想要重裝的一個類之外。如果類加載被委派給了它的父類,它以后將不能被重新加載。記住,一個類只能被同一個ClassLoader實例加載。
如前所述,這只是一個示例,它顯示了類加載器的行為的基本知識。這并不是一個你的類加載器的生產就緒的模板。你的類加載器可能并不僅限于一個類,可能是一個你想要重新加載的類的集合。此外,你也不能硬編碼class path。
public class MyClassLoader extends ClassLoader{ public MyClassLoader(ClassLoader parent) { super(parent); } public Class loadClass(String name) throws ClassNotFoundException { if(!"reflection.MyObject".equals(name)) return super.loadClass(name); try { String url = "file:C:/data/projects/tutorials/web/WEB-INF/" + "classes/reflection/MyObject.class"; URL myUrl = new URL(url); URLConnection connection = myUrl.openConnection(); InputStream input = connection.getInputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int data = input.read(); while(data != -1){ buffer.write(data); data = input.read(); } input.close(); byte[] classData = buffer.toByteArray(); return defineClass("reflection.MyObject", classData, 0, classData.length); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } }
下面是使用MyClassLoader的示例:
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { ClassLoader parentClassLoader = MyClassLoader.class.getClassLoader(); MyClassLoader classLoader = new MyClassLoader(parentClassLoader); Class myObjectClass = classLoader.loadClass("reflection.MyObject"); AnInterface2 object1 = (AnInterface2) myObjectClass.newInstance(); MyObjectSuperClass object2 = (MyObjectSuperClass) myObjectClass.newInstance(); //create new class loader so classes can be reloaded. classLoader = new MyClassLoader(parentClassLoader); myObjectClass = classLoader.loadClass("reflection.MyObject"); object1 = (AnInterface2) myObjectClass.newInstance(); object2 = (MyObjectSuperClass) myObjectClass.newInstance(); }
reflection.MyObject類是由自定義類加載器加載的。注意,它是如何繼承一個超類、實現一個接口的。這只是為了這個例子。在你的代碼中,只需要兩個中的一個,繼承超類或實現接口。
public class MyObject extends MyObjectSuperClass implements AnInterface2{ //... body of class ... override superclass methods // or implement interface methods }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71703.html
摘要:動態編程使用場景通過配置生成代碼,減少重復編碼,降低維護成本。動態生成字節碼操作字節碼的工具有,其中有兩個比較流行的,一個是,一個是。 作者簡介 傳恒,一個喜歡攝影和旅游的軟件工程師,先后從事餓了么物流蜂鳥自配送和蜂鳥眾包的開發,現在轉戰 Java,目前負責物流策略組分流相關業務的開發。 什么是動態編程 動態編程是相對于靜態編程而言的,平時我們討論比較多的靜態編程語言例如Java, 與動態...
摘要:的動態性反射機制動態編譯動態執行代碼動態字節碼操作動態語言程序運行時可以改變程序得結構或變量類型典型語言等如下代碼不是動態語言但有一定的動態性我們可以利用反射機制字節碼操作獲得類似動態語言的特性的動態性讓編程的時候更加靈活反射機制反射機制指 1.Java的動態性 反射機制 動態編譯 動態執行JavaScript代碼 動態字節碼操作 2.動態語言 程序運行時,可以改變程序得結構或變量...
摘要:通過反射獲取帶參無返回值成員方法并使用設置安全檢查,訪問私有構造函數必須創建實例這種不行,注意和方法需要傳遞參數測試復制這個功能獲取私有方法,同樣注意和的區別賦予訪問權限調用方法。 反射 目錄介紹 1.反射概述 1.1 反射概述 1.2 獲取class文件對象的三種方式 1.3 反射常用的方法介紹 1.4 反射的定義 1.5 反射的組成 1.6 反射的作用有哪些 2.反射的...
閱讀 2636·2021-11-11 16:55
閱讀 1279·2021-09-22 15:25
閱讀 1793·2019-08-29 16:26
閱讀 925·2019-08-29 13:21
閱讀 2306·2019-08-23 16:19
閱讀 2795·2019-08-23 15:10
閱讀 761·2019-08-23 14:24
閱讀 1850·2019-08-23 13:48