你對繼承的理解貌似錯了“抽象方法是一定能夠被繼承的并且一定要繼承”,只要你用了extends,子類就可以使用父類的非私有方法和非私有變量了;對于繼承抽象類,父類的抽象方法不是一定要繼承,而是一定要重寫的,而非抽象方法不一定要重寫,重寫就覆蓋了父類的相應的方法(實際是隱藏,說覆蓋形象點).
下個答案:
關于運行時環境 ? ?插件應該將自己所需的庫文件和class文件都放到libs目錄下,這些文件將被加載到插件的classpath中。以來關系也非常重要,在插件加載的過程中,插件管理器會根據plugin。xml中描述的依賴關系選擇插件加載的次序。
不正確的插件依賴關系會導致加載失敗。 1。2邏輯實體 ? ?一個簡單的接口定義了插件的邏輯實體 Java代碼 1。public interface Plugin { ? 2。 ? ?public void init(); ? 3。
public void start(); ? 4。 ? ?public void destory(); ? 5。 ? ?public IExtension getExtension(String id); ? 6。
} ?public interface Plugin { public void init(); public void start(); public void destory(); public IExtension getExtension(String id);} 所有實現了這個接口的類都可以認為是一個插件,其中init方法在插件初始化的時候調用并且只調用一次,start方法在插件啟動的時候調用,destory在插件銷毀的時候調用。
為了確保重復加載,start方法和destory方法應該保證對稱。 2 插件管理器 ? ?插件管理器負責管理和插件有關的一切,在系統啟動的時候,插件管理期掃描插件目錄,找到符合定義的插件目錄,驗證合法性,讀取插件描述文件,掃描庫文件。
然后根據插件描述文件構建插件依賴關系圖,確定插件啟動順序,然后按照順序啟動插件,設定安全管理器,設定classloader,執行init方法,執行start方法。以上就是插件管理器加載插件的全過程,在詳細講解每一步之前我們先了解下Java的classloader。
以下是JDK中關于ClassLoader的解釋[3] 類加載器是負責加載類的對象。ClassLoader 類是一個抽象類。如果給定類的二進制名稱,那么類加載器會試圖查找或生成構成類定義的數據。一般策略是將名稱轉換為某個文件名,然后從文件系統讀取該名稱的“類文件”。
每個 Class 對象都包含一個對定義它的 ClassLoader 的引用。數組類的 Class 對象不是由類加載器創建的,而是由 Java 運行時根據需要自動創建。數組類的類加載器由 Class。getClassLoader() 返回,該加載器與其元素類型的類加載器是相同的;如果該元素類型是基本類型,則該數組類沒有類加載器。
應用程序需要實現 ClassLoader 的子類,以擴展 Java 虛擬機動態加載類的方式。類加載器通常由安全管理器使用,用于指示安全域。ClassLoader 類使用委托模型來搜索類和資源。每個 ClassLoader 實例都有一個相關的父類加載器。
需要查找類或資源時,ClassLoader 實例會在試圖親自查找類或資源之前,將搜索類或資源的任務委托給其父類加載器。虛擬機的內置類加載器(稱為 “bootstrap class loader”)本身沒有父類加載器,但是可以將它用作 ClassLoader 實例的父類加載器。
下面這張圖說明了Java類加載器的結構 寫道+—————————–+| ? ? ? ? ?Bootstrap ? ? ? ? ? ? || ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ?|| ? ? ? ? ? System ? ? ? ? ? ? ? ?|| ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ?|| ? ? ? ? Application ? ? ? ? ?|| ? ? ? ? ? ? ? ?/ \ ? ? ? ? ? ? ? ? ? ? || ? ? ?PluginFramework ?|| ? ? ? ? ? ? ? ? ? ? / \ ? ? ? ? ? ? ? ?|| ? ? ? ? ?Plugin1 Plugin2 |+—————————–+ ? 上圖很好的解釋了Java程序的類加載過程,首先是Bootstrap加載jre的類,然后時System加載CLASSPATH下的類,然后是應用 程序的類加載器(如果有的話)加載屬于應用程序自己的類,接著PluginFrameWork加載插件管理器所需類,從圖中我們可以看 到插件管理器加載的類被所有的插件共享,但是每個插件都有自己的命名空間,這很好的解決了插件之間的庫隔離問題,兩個 不同的插件現在可以加載同一個類的兩個版本而不會有任何沖突。
為了實現上面的類加載方式,我們簡單的擴展URLClassLoader實現了簡單的PluginClassLoaderJava代碼 1。public class PluginClassLoader extends URLClassLoader { ? 2。
private PluginImage image; ? 3。 ? ?private ClassLoader[] requireClassLoader; ? 4。 ? ?public PluginClassLoader(URL[] urls) { ? 5。
super(urls); ? 6。 ? ?} ? 7。 ? ?public PluginClassLoader(PluginImage image,ClassLoader parent) { ? 8。
super(image。getUrls(),parent); ? 9。 ? ? ? ?this。image = image; ? 10。
image。pluginClassLoader = this; ? 11。 ? ? ? ?if (image。getRequires() == null) { ? 12。
requireClassLoader = null; ? 13。 ?14。 ? ? ? ?} else { ? 15。
requireClassLoader = new ClassLoader[image。getRequires()。size()]; ? 16。 ? ? ? ? ? ?for(int i=0;i<image。
getRequires()。size();i++){ ? 17。 ? ? ? ? ? ? ? ?requireClassLoader[i] = PluginManager。
getInstance() ? 18。 ? ? ? ? ? ?。getPluginImage(image。getRequires()。get(i)。getId())。getPluginClassLoader(); ? 19。
} ? 20。 ? ? ? ?} ? 21。 ? ?} ? 22。 ? ?protected Class<?> findClass(String name) throws ClassNotFoundException { ? 23。
Class<?> c = null; ? 24。 ? ? ? ?try { ? 25。 ? ? ? ? ? ?c = super。
findClass(name); ? 26。 ? ? ? ?} catch (ClassNotFoundException e) { ? 27。 ? ? ? ? ? ?if (requireClassLoader == null) ? 28。
throw new ClassNotFoundException(); ? 29。 ? ? ? ? ? ?else { ? 30。
for (ClassLoader l : requireClassLoader) { ? 31。 ? ? ? ? ? ? ? ? ? ?try { ? 32。
if (l != null) ? 33。 ? ? ? ? ? ? ? ? ? ? ? ? ? ?c = l。
loadClass(name); ? 34。 ? ? ? ? ? ? ? ? ? ?} catch (ClassNotFoundException ee) {} ? 35。
36。 ? ? ? ? ? ? ? ?} ? 37。
if (c == null) { ? 38。 ? ? ? ? ? ? ? ? ? ?throw new ClassNotFoundException(); ? 39。
} ? 40。 ? ? ? ? ? ?} ? 41。
} ? 42。 ? ? ? ?return c; ? 43。 ? ?} ? 44。protected String findLibrary(String name) { ? 45。
Hashtable<String, String> libs = image。getLibrarys(); ? 46。 ? ? ? ?if (libs。
containsKey(name)) { ? 47。 ? ? ? ? ? ?return libs。get(name); ? 48。 ? ? ? ? ? ?// full name ? 49。
} else if (libs。containsKey(“lib” + name + “。so”)) { ? 50。 ? ? ? ? ? ?return libs。
get(“lib” + name + “。so”); ? 51。 ? ? ? ?} else { ? 52。 ? ? ? ? ? ?return null; ? 53。
} ? 54。 ?55。 ? ?} ? 56。 ? ?public PluginImage getImage() { ? 57。
return image; ? 58。 ? ?} ? 59。}。
下個答案:
abstract class和interface是Java語言中對于抽象類定義進行支持的兩種機制,正是由于這兩種機制的存在,才賦予了Java強大的面向對象能力。abstract class和interface之間在對于抽象類定義的支持方面具有很大的相似性,甚至可以相互替換,因此很多開發者在進行抽象類定義時對于abstract class和interface的選擇顯得比較隨意。
其實,兩者之間還是有很大的區別的,對于它們的選擇甚至反映出對于問題領域本質的理解、對于設計意圖的理解是否正確、合理。本文將對它們之間的區別進行一番剖析,試圖給開發者提供一個在二者之間進行選擇的依據。 理解抽象類 abstract class和interface在Java語言中都是用來進行抽象類(本文中的抽象類并非從abstract class翻譯而來,它表示的是一個抽象體,而abstract class為Java語言中用于定義抽象類的一種方法,請讀者注意區分)定義的,那么什么是抽象類,使用抽象類能為我們帶來什么好處呢? 在面向對象的概念中,我們知道所有的對象都是通過類來描繪的,但是反過來卻不是這樣。
并不是所有的類都是用來描繪對象的,如果一個類中沒有包含足夠的信息來描繪一個具體的對象,這樣的類就是抽象類。抽象類往往用來表征我們在對問題領域進行分析、設計中得出的抽象概念,是對一系列看上去不同,但是本質上相同的具體概念的抽象。比如:如果我們進行一個圖形編輯軟件的開發,就會發現問題領域存在著圓、三角形這樣一些具體概念,它們是不同的,但是它們又都屬于形狀這樣一個概念,形狀這個概念在問題領域是不存在的,它就是一個抽象概念。
正是因為抽象的概念在問題領域沒有對應的具體概念,所以用以表征抽象概念的抽象類是不能夠實例化的。 在面向對象領域,抽象類主要用來進行類型隱藏。我們可以構造出一個固定的一組行為的抽象描述,但是這組行為卻能夠有任意個可能的具體實現方式。這個抽象描述就是抽象類,而這一組任意個可能的具體實現則表現為所有可能的派生類。
模塊可以操作一個抽象體。由于模塊依賴于一個固定的抽象體,因此它可以是不允許修改的;同時,通過從這個抽象體派生,也可擴展此模塊的行為功能。熟悉OCP的讀者一定知道,為了能夠實現面向對象設計的一個最核心的原則OCP(Open-Closed Principle),抽象類是其中的關鍵所在。
從語法定義層面看abstract class和interface 在語法層面,Java語言對于abstract class和interface給出了不同的定義方式,下面以定義一個名為Demo的抽象類為例來說明這種不同。 使用abstract class的方式定義Demo抽象類的方式如下: abstract class Demo { abstract void method1(); abstract void method2(); … } 使用interface的方式定義Demo抽象類的方式如下: interface Demo { void method1(); void method2(); … } 在abstract class方式中,Demo可以有自己的數據成員,也可以有非abstarct的成員方法,而在interface方式的實現中,Demo只能夠有靜態的不能被修改的數據成員(也就是必須是static final的,不過在interface中一般不定義數據成員),所有的成員方法都是abstract的。
從某種意義上說,interface是一種特殊形式的abstract class。 從編程的角度來看,abstract class和interface都可以用來實現”design by contract”的思想。但是在具體的使用上面還是有一些區別的。
首先,abstract class在Java語言中表示的是一種繼承關系,一個類只能使用一次繼承關系。但是,一個類卻可以實現多個interface。也許,這是Java語言的設計者在考慮Java對于多重繼承的支持方面的一種折中考慮吧。 其次,在abstract class的定義中,我們可以賦予方法的默認行為。
但是在interface的定義中,方法卻不能擁有默認行為,為了繞過這個限制,必須使用委托,但是這會 增加一些復雜性,有時會造成很大的麻煩。 在抽象類中不能定義默認行為還存在另一個比較嚴重的問題,那就是可能會造成維護上的麻煩。因為如果后來想修改類的界面(一般通過abstract class或者interface來表示)以適應新的情況(比如,添加新的方法或者給已用的方法中添加新的參數)時,就會非常的麻煩,可能要花費很多的時間(對于派生類很多的情況,尤為如此)。
但是如果界面是通過abstract class來實現的,那么可能就只需要修改定義在abstract class中的默認行為就可以了。 同樣,如果不能在抽象類中定義默認行為,就會導致同樣的方法實現出現在該抽象類的每一個派生類中,違反了”one rule,one place”原則,造成代碼重復,同樣不利于以后的維護。
因此,在abstract class和interface間進行選擇時要非常的小心。 從設計理念層面看abstract class和interface 上面主要從語法定義和編程的角度論述了abstract class和interface的區別,這些層面的區別是比較低層次的、非本質的。
本小節將從另一個層面:abstract class和interface所反映出的設計理念,來分析一下二者的區別。作者認為,從這個層面進行分析才能理解二者概念的本質所在。 前面已經提到過,abstarct class在Java語言中體現了一種繼承關系,要想使得繼承關系合理,父類和派生類之間必須存在”is a”關系,即父類和派生類在概念本質上應該是相同的(參考文獻〔3〕中有關于”is a”關系的大篇幅深入的論述,有興趣的讀者可以參考)。
對于interface 來說則不然,并不要求interface的實現者和interface定義在概念本質上是一致的,僅僅是實現了interface定義的契約而已。為了使論述便于理解,下面將通過一個簡單的實例進行說明。 考慮這樣一個例子,假設在我們的問題領域中有一個關于Door的抽象概念,該Door具有執行兩個動作open和close,此時我們可以通過abstract class或者interface來定義一個表示該抽象概念的類型,定義方式分別如下所示: 使用abstract class方式定義Door: abstract class Door { abstract void open(); abstract void close(); } 使用interface方式定義Door: interface Door { void open(); void close(); } 其他具體的Door類型可以extends使用abstract class方式定義的Door或者implements使用interface方式定義的Door。
看起來好像使用abstract class和interface沒有大的區別。 如果現在要求Door還要具有報警的功能。我們該如何設計針對該例子的類結構呢(在本例中,主要是為了展示abstract class和interface反映在設計理念上的區別,其他方面無關的問題都做了簡化或者忽略)?下面將羅列出可能的解決方案,并從設計理念層面對這些不同的方案進行分析。
解決方案一: 簡單的在Door的定義中增加一個alarm方法,如下: abstract class Door { abstract void open(); abstract void close(); abstract void alarm(); } 或者 interface Door { void open(); void close(); void alarm(); } 那么具有報警功能的AlarmDoor的定義方式如下: class AlarmDoor extends Door { void open() { … } void close() { … } void alarm() { … } } 或者 class AlarmDoor implements Door { void open() { … } void close() { … } void alarm() { … } } 這種方法違反了面向對象設計中的一個核心原則ISP(Interface Segregation Priciple),在Door的定義中把Door概念本身固有的行為方法和另外一個概念”報警器”的行為方法混在了一起。
這樣引起的一個問題是那些僅僅依賴于Door這個概念的模塊會因為”報警器”這個概念的改變(比如:修改alarm方法的參數)而改變,反之依然。 解決方案二: 既然open、close和alarm屬于兩個不同的概念,根據ISP原則應該把它們分別定義在代表這兩個概念的抽象類中。
定義方式有:這兩個概念都使用abstract class方式定義;兩個概念都使用interface方式定義;一個概念使用abstract class方式定義,另一個概念使用interface方式定義。 顯然,由于Java語言不支持多重繼承,所以兩個概念都使用abstract class方式定義是不可行的。
后面兩種方式都是可行的,但是對于它們的選擇卻反映出對于問題領域中的概念本質的理解、對于設計意圖的反映是否正確、合理。我們一一來分析、說明。 如果兩個概念都使用interface方式來定義,那么就反映出兩個問題:1、我們可能沒有理解清楚問題領域,AlarmDoor在概念本質上到底是Door還是報警器?2、如果我們對于問題領域的理解沒有問題,比如:我們通過對于問題領域的分析發現AlarmDoor在概念本質上和Door是一致的,那么我們在實現時就沒有能夠正確的揭示我們的設計意圖,因為在這兩個概念的定義上(均使用interface方式定義)反映不出上述含義。
如果我們對于問題領域的理解是:AlarmDoor在概念本質上是Door,同時它有具有報警的功能。我們該如何來設計、實現來明確的反映出我們的意思呢?前面已經說過,abstract class在Java語言中表示一種繼承關系,而繼承關系在本質上是”is a”關系。
所以對于Door這個概念,我們應該使用abstarct class方式來定義。另外,AlarmDoor又具有報警功能,說明它又能夠完成報警概念中定義的行為,所以報警概念可以通過interface方式定義。如下所示: abstract class Door { abstract void open(); abstract void close(); } interface Alarm { void alarm(); } class AlarmDoor extends Door implements Alarm { void open() { … } void close() { … } void alarm() { … } } 這種實現方式基本上能夠明確的反映出我們對于問題領域的理解,正確的揭示我們的設計意圖。
其實abstract class表示的是”is a”關系,interface表示的是”like a”關系,大家在選擇時可以作為一個依據,當然這是建立在對問題領域