Java中能夠在運行時加載和從新加載類,雖然並不像咱們想像中那麼簡單。本文將解釋什麼時候、怎樣在Java中加載、從新加載類。
你能夠爭論動態加載類是Java反射的一部分仍是Java核心的一部分。無論怎樣,我把它放在了Java反射中,由於沒有更好的地方放置它。java
Java程序的全部類都是使用 java.lang.ClassLoader
的一些子類加載的。所以,動態加載類也必須使用 java.lang.ClassLoader
的子類。
當一個類加載,它所引用的類也會被加載。類加載模式是遞歸加載的,直到全部須要的類加載完畢。這可能並非應用程序的全部類。未被引用的類在引用前不會被加載。web
類加載在Java中被組織成層級。當你建立一個獨立的ClassLoader
,你必須提供一個父級ClassLoader
。若是ClassLoader
被請求加載一個類,它會請求它的父級ClassLoader
去加載它。若是父級類加載器找不到這個類,子類加載器會嘗試自加載。ide
類加載器加載類的步驟以下:編碼
當你實現一個可以重載類的類加載器時,你須要從這個序列中偏離一點。不該請求父類加載程序加載要重裝的類。稍後再談。url
動態加載類很是簡單。全部你須要作的是得到一個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
類的實例。這不是不可能的,但必須知道什麼時候設計類從新加載。code
如上文述,不能使用加載指定類的ClassLoader
從新加載這個類。所以,必須使用不一樣的ClassLoader
加載這個類。可是,這會帶來新的問題。
Java程序中加載的每個類都以其全限定名(包名+類名)標識,而且由ClassLoader
實例加載。這意味着,類MyObject
由類加載器A加載,是和由類加載器B加載的同一個類MyObject
不相同。模擬代碼以下:orm
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 }