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