摘要 一, 接口基礎知識 1, java語言不支持一個類有多個直接的父類(多繼承),但可以實現(implements)多個接口,間接的實現了多繼承. 2, 與接口相關的設計模式: 1, 定製服務模式 設計精粒度的接口,每個接口代表相關的一組服務,通過繼承來創建複合接口 2, 適配器模式 當每個系統之間接口不匹配時,用適配器來轉換接口 3, 默認適配器模式 爲接口提供簡單的默認實現 4, 代理模式 爲接口的實現類創建代理類,使用者通過代理來獲得實現類的服務 5, 標識類型模式 用接口來標識一種沒有任何行爲的抽象類型 6, 常量接口模式 在接口中定義靜態常量,在其它類中通過import static語句引入這些常量 3, 接口的特徵歸納: 1, 接口中的成員變量默認都是public,static,final類型的(都可省略),必須被顯示初始化,即接口中的成員變量爲常量(大寫,單詞之間用"_"分隔) 2, 接口中的方法默認都是public,abstract類型的(都可省略),沒有方法體,不能被實例化 public interface A { int CONST = 1; //合法,CONST默認爲public,static,final類型 void method(); //合法,method()默認爲public,abstract類型 public abstract void method2(); //method2()顯示聲明爲public,abstract類型 } 3, 接口中只能包含public,static,final類型的成員變量和public,abstract類型的成員方法 public interface A { int var; //錯,var是常量,必須顯示初始化 void method(){...}; //錯,接口中只能包含抽象方法 protected void method2(); //錯,接口中的方法必須是public類型 static void method3(){...}; //錯,接口中不能包含靜態方法 } 4, 接口中沒有構造方法,不能被實例化 public interface A { public A(){...}; //錯,接口中不能包含構造方法 void method(); } 5, 一個接口不能實現(implements)另一個接口,但它可以繼承多個其它的接口 public interface A { void methodA(); } public interface B { void methodB(); } public interface C extends A, B //C稱爲複合接口 { void methodC(); } public interface C implements A{...} //錯 6, 接口必須通過類來實現它的抽象方法 public class A implements B{...} 7, 當類實現了某個接口時,它必須實現接口中的所有抽象方法,否則這個類必須聲明爲抽象的 8, 不允許創建接口的實例(實例化),但允許定義接口類型的引用變量,該引用變量引用實現了這個接口的類的實例 public class B implements A{} A a = new B(); //引用變量a被定義爲A接口類型,引用了B實例 A a = new A(); //錯誤,接口不允許實例化 9, 一個類只能繼承一個直接的父類,但可以實現多個接口,間接的實現了多繼承. public class A extends B implements C, D{...} //B爲class,C,D爲interface 4, 通過接口,可以方便地對已經存在的系統進行自下而上的抽象,對於任意兩個類,不管它們是否屬於同一個父類,只有它 們存在相同的功能,就能從中抽象出一個接口類型.對於已經存在的繼承樹,可以方便的從類中抽象出新的接口,但從類 中抽象出新的抽象類卻不那麼容易,因此接口更有利於軟件系統的維護與重構.對於兩個系統,通過接口交互比通過抽象 類交互能獲得更好的鬆耦合. 5, 接口是構建鬆耦合軟件系統的重要法寶,由於接口用於描述系統對外提供的所有服務,因此接口中的成員變量和方法都 必須是public類型的,確保外部使用者能訪問它們,接口僅僅描述系統能做什麼,但不指明如何去做,所有接口中的方法 都是抽象方法,接口不涉及和任何具體實例相關的細節,因此接口沒有構造方法,不能被實例化,沒有實例變量.二, 比較抽象類與接口 1, 抽象類與接口都位於繼承樹的上層 相同點 1, 代表系統的抽象層,當一個系統使用一顆繼承樹上的類時,應該儘量把引用變量聲明爲繼承樹的上層抽象類型, 這樣可以提高兩個系統之間的送耦合 2, 都不能被實例化 3, 都包含抽象方法,這些抽象方法用於描述系統能提供哪些服務,但不提供具體的實現 不同點: 1, 在抽象類中可以爲部分方法提供默認的實現,從而避免在子類中重複實現它們,這是抽象類的優勢,但這一優勢 限制了多繼承,而接口中只能包含抽象方法. 由於在抽象類中允許加入具體方法,因此擴展抽象類的功能,即向抽象類中添加具體方法,不會對它的子類造 成影響,而對於接口,一旦接口被公佈,就必須非常穩定,因爲隨意在接口中添加抽象方法,會影響到所有的實 現類,這些實現類要麼實現新增的抽象方法,要麼聲明爲抽象類 2, 一個類只能繼承一個直接的父類,這個父類可能是抽象類,但一個類可以實現多個接口,這是接口的優勢,但這 一優勢是以不允許爲任何方法提供實現作爲代價的三, 爲什麼Java語言不允許多重繼承呢? 當子類覆蓋父類的實例方法或隱藏父類的成員變量及靜態方法時,Java虛擬機採用不同的綁定規則,假如還允許 一個類有多個直接的父類,那麼會使綁定規則更加複雜,因此,爲了簡化系統結構設計和動態綁定機制,Java語言 禁止多重繼承. 而接口中只有抽象方法,沒有實例變量和靜態方法,只有接口的實現類纔會實現接口的抽象方法(接口中的抽象方 法是通過類來實現的),因此,一個類即使有多個接口,也不會增加Java虛擬機進行動態綁定的複雜度.因爲Java虛 擬機永遠不會把方法與接口綁定,而只會把方法與它的實現類綁定.四, 使用接口和抽象類的總體原則: 1, 用接口作爲系統與外界交互的窗口 站在外界使用者(另一個系統)的角度,接口向使用者承諾系統能提供哪些服務,站在系統本身的角度,接口制定 系統必須實現哪些服務,接口是系統中最高層次的抽象類型.通過接口交互可以提高兩個系統之間的送耦合 系統A通過系統B進行交互,是指系統A訪問系統B時, 把引用變量聲明爲系統B中的接口類型,該引用變量引用系統B中接口的實現類的實例. public interface B { } public class C implements B { } public class A { } B a = new C(); 2, 接口本身必須非常穩定,接口一旦制定,就不允許隨遇更加,否則對外面使用者及系統本身造成影響 3, 用抽象類來定製系統中的擴展點 抽象類來完成部分實現,還要一些功能通過它的子類來實現 2008/1/9 一, Java多態機制中的綁定規則深入剖析 class Base { String var = "BaseVar"; //實例變量 static String staticVar = "StaticBaseVar"; //靜態變量 void method() //實例方法 { System.out.println("Base method"); } static void staticMethod() //靜態方法 { System.out.println("Static Base method"); } }public class Sub extends Base { String var = "SubVar"; //實例變量 static String staticVar = "StaticSubVar"; //靜態變量 void method() //隱藏父類的method()方法 { System.out.println("Sub method"); } static void staticMethod() //隱藏父類的staticMethod()方法 { System.out.println("Static Sub method"); } String subVar = "Var only belonging to Sub"; void subMethod() { System.out.println("method only belonging to Sub"); } public static void main(String args[]) { //引用變量who被聲明爲Base類型,引用Sub類的實例 Base who = new Sub(); //成員變量(靜態變量,實例變量)與引用變量所聲明的類型(Base類型)的成員變量綁定 System.out.println("who.var = "+who.var); //所以,打印Base類的var變量 System.out.println("who.staticVar = "+who.staticVar); //所以,打印Base類的staticVar變量 //實例方法與引用變量實際引用的對象(Sub對象)的方法綁定 who.method(); //所以,打印Sub實例的method()方法 //靜態方法與引用變量所聲明的類型(Base類型)的方法綁定 who.staticMethod(); //所以,打印Base類的staticMethod()方法 } } 【分析過程】 1, 對於一個引用類型的變量,Java編譯器按照它聲明的類型來處理. 例如在以下代碼中,編譯器認爲who是Base類型的引用變量,不存在subVar成員變量喝subMethod()方法,編譯報錯 Base who = new Sub(); //引用變量who被聲明爲Base類型,引用Sub類的實例 who.subVar = "123"; //編譯錯,在Base類中沒有subVar屬性 who.subMethod(); //編譯錯,在Base類中沒有submethod()方法 如果要訪問Sub類的成員,必須通過強制類型轉換: Base who = new Sub(); //把Base引用類型的who成員變量強制轉換爲Sub引用類型 //把引用變量轉換爲子類的類型稱爲向下轉型,把引用變量轉換爲父類的類型稱爲向上轉型 ((Sub)who).subVar = "123"; ((Sub)who).subMethod(); Java編譯器允許在具有直接或間接繼承關係的類之間進行類型轉換,對於向上轉型,Java編譯器會自動進行,對於 向下轉型,需要進行強制類型轉換 如果兩種類型之間沒有繼續關係,即不在繼承樹的同一個繼承分支上,那麼Java編譯器不允許進行類型轉換 2, 對於一個引用類型的變量,運行時Java虛擬機按照它實際引用的對象來處理 例如以下代碼雖編譯可通過,但運行時會拋出ClassCastException運行時異常 Base who = new Base(); //who引用Base類的實例 Sub s = (Sub)who; //運行時會拋出ClassCastException 在運行時,子類的對象可以轉換爲父類類型,而父類的對象實際上無法轉換爲子類類型 3, 在運行時環境中,通過引用類型變量來訪問所引用對象的方法和屬性時,Java虛擬機採用以下綁定規則: 1, 實例方法與引用變量實際引用的對象的方法綁定,這種綁定屬於動態綁定,因爲是在運行時由Java虛擬機 動態決定的 2, 靜態方法與引用變量所聲明的類型的方法綁定,這種綁定屬於靜態綁定,因爲實際上是在編譯階段就已經 綁定 3, 成員變量(靜態變量,實例變量)與引用變量所聲明的類型的成員變量綁定,這種綁定屬於靜態綁定,因爲 實際上是在編譯階段就已經綁定
java 接口理解
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.