java是單繼承的可是現實存在着支持多繼承,例如「金絲猴」是一個動物,同時他也是一個值錢的東西,在代碼中寫exrends動物
再extends值錢的 這是確定不能夠的,java只支持單繼承,這時就能夠用接口了
接口是一種特殊的抽象類,也就是說,接口也存在類的特性,假如說這個類中常量是final的而且方法也是抽象的,這時候就能夠定義成接口
下面就是一個典型的接口:
public interface Runner{ public static final int id = 1; public void start(); public void run(); public void stop(); }
接口是一種特殊的抽象類,在這個抽象類裏面全部的方法都是抽象方法 的而且成員變量都是public static final 類型 常量名,java 默認是public static final 固然能夠直接寫 類型 常量名 ,例如:java
public static final int id=10,能夠寫成int id =10,而且接口中的方法也能夠不寫abstract 關鍵字來標識,由於在接口中全部的方法默認都是抽象的,雖說在上面的代碼中 start方法和run方法與stop方法沒有寫abstract 可是要知道他們是抽像的,只不過省略了abstract而已,注意:在接口裏面聲明的抽象方法默認是「public(公共的)」的,也只能是「public(公共的),成員變量是public static final的也只能是public static final 的,」java接口中的成員變量爲何要定義成public static final呢,這是腰修正c++中的多繼承出現問題,多繼承多個父類之間若是出現相同變量的時候,在運行會出現各類問題,因此,java改爲static fianal的,讓成員變量只屬於這個類,不屬於某個對象,在多繼承中子類包含多個父類對象,而單繼承來講子類裏面就只有一個父類對象,多繼承子類對象就有多個父類對象,而這些父類對象之間可能會出現有重複的成員變量,這就很是容易出現問題,所以,在java中避免了這種問題的出現,採用了接口來實現多繼承,c++
做爲接口來講,一個類從接口繼承(或者是實現接口),這也是多繼承,接口裏面的成員變量不屬於某個對象,都是靜態的成員變量,是屬於整個類,所以一個類去實現多個接口也是無所謂的,不會存在對象之間相互衝突的問題,實現多個接口也就是實現了多繼承,並且又避免了多重繼承容易出現問題的地方,這就是接口實現的好處ide
接口特性:
下面分析一下一個例子 :函數
package 接口; interface Singer{ public void sing(); public void sleep(); } interface Painter{ public void paint(); public void eat(); } class Student implements Singer{ private String name; Student(String name){ this.name = name; } public void study() { System.out.println("studying"); } public String getName() { return name; } public void sing() { System.out.println("student is singing"); } public void sleep() { System.out.println("student is sleeping"); } } class Teacher implements Singer,Painter{ private String name; public String getString() { return name; } Teacher(String name){ this.name = name; } public void teach() { System.out.println("teachinging"); } public void sing() { System.out.println("teacher is singing"); } public void sleep() { System.out.println("teacher is sleeping"); } public void paint() { System.out.println("treacher is painting"); } public void eat() { System.out.println("teacher is eating"); } } class 接口 { public static void main(String[] args) { // TODO Auto-generated method stub Singer s1 = new Student("le"); s1.sing(); s1.sleep(); Singer s2 = new Teacher("steven"); s2.sing(); s2.sleep(); Painter p1 = (Painter)s2; p1.paint(); p1.eat(); } }
首先從函數的入口main函數開始 this
Singer s1 = new Student("le");lua
定義一個Singer接口的變量叫作s1,接口Singer是Student類實現的,即至關於Student類從Singer接口的繼承,所以,s1是父類的一個引用,這句話的意思就是父類的一個對象s1的引用指向了子類對象Student,後面執行spa
Student(String name){3d
this.name = name;指針
}code
經過調用Student的構造方法將Student類中的屬性name賦值爲"le",
這個Student對象可以訪問位於代碼區裏面的sleep()方法和sing()方法,由於Student類從父類Sing繼承而來,所以天然能夠訪問到這兩個方法,除此以外,還能訪問Student類裏面自定義的Study()方法。所以代碼區裏面存放着這三個方法等待着Student類的對象去訪問,也就是去調用。一個正常的Student能夠直接調用這三個方法。那麼怎麼找獲得位於代碼區的這三個方法呢?Student對象裏面存在着能找獲得這個三個方法的函數指針,引用對象經過這個指針的索引指向就能找到代碼區裏面的這三個方法。
s1是父類對象的索引,但此時s1指向的倒是子類對象,即一個父類對象的索引指向了子類對象。這裏很不幸的是,因爲這個s1是一個父類對象的引用,站在s1的角度上,它就是隻把你這個子類對象Student當成是一個Singer,s1只能看到Student對象裏面的sing()和sleep這兩個方法的方法指針,所以使用這個s1引用對象只能去訪問從父類繼承下來的sleep()和sing()這兩個方法,但因爲這兩個方法在子類Student裏面被重寫了,那麼如今就是這種狀況了,子類Student從父類Singer繼承,在子類裏面重寫了從父類繼承下來的sing()和sleep()這兩個方法,父類對象的引用指向了子類對象,這三種狀況加在一塊兒就使得多態能夠存在了,這樣調用位於代碼區裏面的方法時,會根據new出來的實際對象去調用代碼區裏面的方法,所以這裏在s1眼裏雖然是把這個new出的Student當成一個Singer,但這個對象實際上就是一個Student,所以使用父類對象的引用s1調用代碼區裏面的sleep()和sing()方法時,調用的是在子類裏面重寫事後的sing()和sleep()方法。
此時的內存圖以下:
Singer s2 = new Teacher("stven");
Teacher這個類實現了Singer接口和Painter接口,即至關於從兩個父類繼承,一個父類是Singer,另外一個父類是Painter
這裏的s2也是父類對象Singer的引用,指向的倒是子類對象Teacher,所以也是一個父類對象的引用指向子類對象。
創造這個Teacher對象的時候,調用Teacher(String name)構造方法,其定義以下:
Teacher(String name){
this.name=name;
}
調用構造方法後,Teacher有了本身的名字steven,因此Teacher的name屬性值爲steven,因爲這個Teacher實現了Painter接口和Singer接口,所以也繼承這兩個接口裏面的方法,所以一個正常的Teacher能夠訪問的方法有:paint()、eat()和sing()、sleep。前面兩個方法是從Painter類繼承過來的,後面兩個方法是從Singer類繼承過來的。除了這四個方法外,還有本身定義的Teach()方法。但是很不幸的是,因爲s2是一個Singer類對象的引用,所以站在s2的角度來看,它只把Teacher當成是一個普通的Singer,所以它看到的只是Teacher對象裏面的sing()和sleep()這兩方法,而後要調用時就經過Teacher對象裏面的函數指針找到位於代碼區的sleep()和sing()這兩個方法。別的方法s2是看不到的,所以也調用不了。
Painter p1=(Painter)s2;
這裏把s2強制轉換成Painter,s2對象實際是指向Teacher的,把s2強制轉換成Painter之後,就能夠把Teacher當成Painter來用,因此p1會把Teacher當成Painter來看待,所以p1只能看到Teacher裏面的painter()方法和eat()方法,所以可以訪問到的也只有這兩個方法。因此接口對於咱們實際當中的對象來講,每個接口暴露了咱們這個實際對象的一部分方法。你使用什麼樣的接口,就只能訪問這個接口裏面定義的方法,別的接口定義的方法就沒辦法訪問獲得。
輸出:
student is singing
student is sleeping
teacher is singing
teacher is sleeping
treacher is painting
teacher is eating
下面用另外一個例子進行驗證接口的近一步特性
package javastudy.summary; /** * 把「值錢的東西」這個類定義成一個接口Valuable。在接口裏面定義了一個抽象方法getMoney() * * */ interface Valuable { public double getMoney(); } /** * 把「應該受到保護的東西」這個類定義成一個接口Protectable。 * 在接口裏面定義了一個抽象方法beProtected(); * */ interface Protectable { public void beProteced(); } /** * 這裏是接口與接口之間的繼承,接口A繼承了接口Protectable, * 所以天然而然地繼承了接口Protectable裏面的抽象方法beProtected()。 * 所以某一類去實現接口A時,除了要實現接口A裏面定義的抽象方法m()之外, * 還要實現接口A從它的父接口繼承下來的抽象方法beProtected()。 * 只有把這兩個抽象方法都實現了纔算是實現了接口A。 * * */ interface A extends Protectable { void m(); } /** * 這裏定義了一個抽象類Animal。 * * */ abstract class Animal { private String name; /** * 在Animal類裏面聲明瞭一個抽象方法enjoy() */ abstract void enjoy(); } /** * 這裏是爲了實現了咱們原來的語義: * 「金絲猴是一種動物」同時「他也是一種值錢的東西」同時「他也是應該受到保護的東西」。而定義的一個類GoldenMonKey。 * 爲了實現上面的語義,這裏把「值錢的東西」這個類定義成了一個接口Valuable, * 把「應該受到保護的東西」這個類也定義成了一個接口Protectable。這樣就能夠實現多繼承了。 * GoldenMonKey類首先從Animal類繼承,而後GoldenMonKey類再去實現Valuable接口和Protectable接口, * 這樣就能夠實現GoldenMonKey類同時從Animal類,Valuable類,Protectable類繼承了,即實現了多重繼承, * 實現了原來的語義。 * * */ class GoldenMonKey extends Animal implements Valuable,Protectable { /** * 在GoldenMoKey類裏面重寫了接口Protectable裏面的beProtected()這個抽象方法, * 實現了接口Protectable。 */ @Override public void beProteced() { System.out.println("live in the Room"); } /** * 在GoldenMoKey類裏面重寫了接口Valuable裏面的getMoney()這個抽象方法,實現了接口Valuable。 */ @Override public double getMoney() { return 10000; } /** * 這裏重寫了從抽象類Animal繼承下來的抽象方法enjoy()。 * 實現了這抽象方法,不過這裏是空實現,空實現也是一種實現。 */ @Override void enjoy() { } public static void test() { /** * 實際當中在內存裏面咱們new的是金絲猴,在金絲猴裏面有不少的方法, * 可是接口的引用對象v能看到的就只有在接口Valuable裏面聲明的getMoney()方法, * 所以可使用v.getMoney()來調用方法。而別的方法v都看不到,天然也調用不到了。 */ Valuable v = new GoldenMonKey(); System.out.println(v.getMoney()); /** * 把v強制轉換成p,至關於換了一個窗口,經過這個窗口只能看獲得接口Protectable裏面的beProtected()方法 */ Protectable p = (Protectable)v; p.beProteced(); } } /** * 這裏讓Hen類去實現接口A,接口A又是從接口Protectable繼承而來,接口A本身又定義了一個抽象方法m(), * 因此此時至關於接口A裏面有兩個抽象方法:m()和beProtected()。 * 所以Hen類要去實現接口A,就要重寫A裏面的兩個抽象方法,實現了這兩個抽象方法後纔算是實現了接口A。 * * */ class Hen implements A { @Override public void beProteced() { } @Override public void m() { } } /** * java中定義接口 */ public class JavaInterfacesTest { public static void main(String[] args) { GoldenMonKey.test(); } }
接口總結:接口和接口之間能夠相互繼承,類和類之間能夠相互繼承,類和接口之間,只能是類來實現接口