JAVA是隻支持單繼承的,但現實之中存在多重繼承這種現象,如「金絲猴是一種動物」,金絲猴從動物這個類繼承,同時「金絲猴是一種值錢的東西」,金絲猴從「值錢的東西」這個類繼承,同時「金絲猴是一種應該受到保護的東西」,金絲猴從「應該受到保護的東西」這個類繼承。這樣金絲猴能夠同時從 「動物類」、「值錢的東西類」、「應該受到保護的東西」 這三個類繼承,但因爲JAVA只支持單繼承,所以金絲猴只能從這三個類中的一個來繼承,不能同時繼承這三個類。所以爲了封裝現實生活中存在的多重繼承現象,爲了實現多繼承,能夠把其中的兩個類封裝成接口。使用接口能夠幫助咱們實現多重繼承。java
接口的本質——接口是一種特殊的抽象類,這種抽象類裏面只包含常量和方法的定義,而沒有變量和方法的實現。ide
抽象類所具備的一些東西接口能夠具備,假如一個抽象類裏面全部的方法全都是抽象的,沒有任何一個方法須要這個抽象類去實現,而且這個抽象類裏面全部的變量都是靜態(static)變量,都是不能改變(final)的變量,這時能夠把這樣的抽象類定義爲一個接口(interface)。把一個類定義成一個接口的格式是把聲明類的關鍵字class用聲明接口的關鍵字interface替換掉便可。函數
1 /** 2 * java中定義接口 3 */ 4 public interface JavaInterfaces { 5 6 }
接口(interface)是一種特殊的抽象類,在這種抽象類裏面,全部的方法都是抽象方法,而且這個抽象類的屬性(即成員變量)都是聲明成「public static final 類型 屬性名」這樣的,默認也是聲明成「public static final」即裏面的成員變量都是公共的、靜態的,不能改變的。所以在接口裏面聲明常量的時候,能夠寫成「public static final 類型 常量名=value(值)」這樣的形式,也能夠直接寫成「類型 常量名=value(值)」如:「public static final int id=10」能夠直接寫成「int id=10」這樣的形式,由於在接口裏面默認的屬性聲明都是「public static final」的,所以「public static final」能夠省略不寫。在接口裏面聲明的抽象方法能夠不寫abstract關鍵字來標識,由於接口裏面全部的方法都是抽象的,所以這個「abstract」關鍵字默認都是省略掉的,如在一個接口裏面聲明這樣的三個方法:「public void start()」、「public void run()」、「public void stop()」這三個方法前面都沒有使用abstract關鍵字來標識,可它們就是抽象方法,由於在接口裏面的聲明的方法都是抽象方法,所以在接口裏面的抽象方法都會把abstract關鍵字省略掉,由於默認聲明的方法都是抽象的,因此就沒有必要再寫「abstract」字了,這一點與在抽象類裏面聲明抽象方法時有所區別,在抽象類裏面聲明抽象方法是必定要使用「abstract」關鍵字的,而在接口裏面聲明抽象方法能夠省略掉「abstract」。注意:在接口裏面聲明的抽象方法默認是「public(公共的)」的,也只能是「public(公共的)」之因此要這樣聲明是爲了修正C++裏面多重繼承的時候容易出現問題的地方,C++的多繼承容易出現問題,問題在於多繼承的多個父類之間若是他們有相同的成員變量的時候,這個引用起來會至關地麻煩,而且運行的時候會產生各類各樣的問題。JAVA爲了修正這個問題,把接口裏面全部的成員變量全都改爲static final,成員變量是static類型,那麼這個成員變量就是屬於整個類裏面的,而不是專屬於某個對象。對於多重繼承來講,在一個子類對象裏面實際上包含有多個父類對象,而對於單繼承來講,子類對象裏面就只有一個父類對象。多繼承子類對象就有多個父類對象,而這些父類對象之間可能又會存在有重複的成員變量,這就很是容易出現問題,所以在JAVA裏面避免了這種問題的出現,採用了接口這種方式來實現多繼承。做爲接口來講,一個類能夠從接口繼承(或者叫實現接口),這也是多繼承,接口裏面的成員變量不專屬於某個對象,都是靜態的成員變量,是屬於整個類的,所以一個類去實現多個接口也是無所謂的,不會存在對象之間互相沖突的問題。實現多個接口,也就實現了多重繼承,並且又避免了多重繼承容易出現問題的地方,這就是用接口實現多重繼承的好處。佈局
1 package javastudy.summary; 2 3 /** 4 * 這裏定義了接口:Painter。 在Painter接口裏面定義了paint()和eat()這兩個抽象方法。 5 * 6 * @author gacl 7 * 8 */ 9 interface Painter { 10 public void eat(); 11 12 public void paint(); 13 } 14 15 /** 16 * 這裏定義了兩個接口:Singer 在Singer接口裏面定義了sing()和sleep()這兩個抽象方法。 17 * 18 * @author gacl 19 * 20 */ 21 interface Singer { 22 public void sing(); 23 24 public void sleep(); 25 } 26 27 /** 28 * 類Student實現了Singer這個接口 29 * 30 * @author gacl 31 * 32 */ 33 class Student implements Singer { 34 35 private String name; 36 37 public Student(String name) { 38 this.name = name; 39 } 40 41 public String getName() { 42 return name; 43 } 44 45 public void setName(String name) { 46 this.name = name; 47 } 48 49 /** 50 * 實現接口中定義的sing方法 51 */ 52 @Override 53 public void sing() { 54 System.out.println("student is singing"); 55 } 56 57 /** 58 * 實現接口中定義的sleep方法 59 */ 60 @Override 61 public void sleep() { 62 System.out.println("student is sleeping"); 63 } 64 65 public void study() { 66 System.out.println("Studying..."); 67 } 68 69 } 70 71 /** 72 * Teacher這個類實現了兩個接口:Singer和Painter。 這裏Teacher這個類經過實現兩個不相關的接口而實現了多重繼承。 73 * 74 * @author gacl 75 * 76 */ 77 class Teacher implements Singer, Painter { 78 79 private String name; 80 81 public Teacher(String name) { 82 this.name = name; 83 } 84 85 /** 86 * 在Teacher類裏面重寫了這兩個接口裏面的抽象方法, 87 * 經過重寫抽象方法實現了這兩個接口裏面的抽象方法。 88 */ 89 @Override 90 public void eat() { 91 System.out.println("teacher is eating"); 92 } 93 94 public String getName() { 95 return name; 96 } 97 98 @Override 99 public void paint() { 100 System.out.println("teacher is painting"); 101 } 102 103 public void setName(String name) { 104 this.name = name; 105 } 106 107 @Override 108 public void sing() { 109 System.out.println("teacher is singing"); 110 } 111 112 @Override 113 public void sleep() { 114 System.out.println("teacher is sleeping"); 115 } 116 117 public void teach() { 118 System.out.println("teaching..."); 119 } 120 } 121 122 public class TestInterfaces { 123 124 public static void main(String[] args) { 125 /** 126 * 這裏定義了一個接口類型的變量s1 127 */ 128 Singer s1 = new Student("le"); 129 s1.sing(); 130 s1.sleep(); 131 Singer s2 = new Teacher("steven"); 132 s2.sing(); 133 s2.sleep(); 134 Painter p1 = (Painter)s2; 135 p1.paint(); 136 p1.eat(); 137 } 138 }
這裏驗證了兩個規則,「一個類能夠實現多個無關的接口」,Teacher類既實現了Singer接口,同時也實現了Painter接口,而Singer接口和Painter接口是無關係的兩個接口。「多個無關的類能夠實現同一接口」,Student類和Teacher類都實現了Singer接口,而Student類和Teacher類並非關係很密切的兩個類,能夠說是無關的兩個類。this
運行結果:lua
首先分析main方法的第一句話spa
Singer s1 = new Student(「le」); 指針
這裏首先定義了一個接口類型的變量s1,接口Singer是Student類實現的,即至關於Student類從Singer接口繼承,Singer接口的本質是一個特殊的抽象類,因此這裏Singer接口就是Student類的父類,所以s1就是父類對象的一個引用,即這裏這句話執行完後就是一個父類對象s1的引用指向子類對象Student。因此內存裏面的佈局應該是這樣:棧空間裏面有一個父類對象的引用s1,堆空間裏面new出了一個Student對象,創造這個Student對象的時候調用了Student類的構造方法Student(String name),其定義以下:code
Student(String name){對象
this.name = name;
}
經過調用構造方法使得這個Student對象有了一個本身的名字「le」,所以堆內存裏面的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(「steven」);
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()方法,所以可以訪問到的也只有這兩個方法。因此接口對於咱們實際當中的對象來講,每個接口暴露了咱們這個實際對象的一部分方法。你使用什麼樣的接口,就只能訪問這個接口裏面定義的方法,別的接口定義的方法就沒辦法訪問獲得。
接口能夠幫助咱們實現多重繼承這種邏輯,接口和它的實現類之間存在多態性。
1 package javastudy.summary; 2 3 /** 4 * 把「值錢的東西」這個類定義成一個接口Valuable。在接口裏面定義了一個抽象方法getMoney() 5 * @author gacl 6 * 7 */ 8 interface Valuable { 9 public double getMoney(); 10 } 11 12 /** 13 * 把「應該受到保護的東西」這個類定義成一個接口Protectable。 14 * 在接口裏面定義了一個抽象方法beProtected(); 15 * @author gacl 16 * 17 */ 18 interface Protectable { 19 public void beProteced(); 20 } 21 22 /** 23 * 這裏是接口與接口之間的繼承,接口A繼承了接口Protectable, 24 * 所以天然而然地繼承了接口Protectable裏面的抽象方法beProtected()。 25 * 所以某一類去實現接口A時,除了要實現接口A裏面定義的抽象方法m()之外, 26 * 還要實現接口A從它的父接口繼承下來的抽象方法beProtected()。 27 * 只有把這兩個抽象方法都實現了纔算是實現了接口A。 28 * @author gacl 29 * 30 */ 31 interface A extends Protectable { 32 void m(); 33 } 34 35 /** 36 * 這裏定義了一個抽象類Animal。 37 * @author gacl 38 * 39 */ 40 abstract class Animal { 41 private String name; 42 /** 43 * 在Animal類裏面聲明瞭一個抽象方法enjoy() 44 */ 45 abstract void enjoy(); 46 } 47 48 /** 49 * 這裏是爲了實現了咱們原來的語義: 50 * 「金絲猴是一種動物」同時「他也是一種值錢的東西」同時「他也是應該受到保護的東西」。而定義的一個類GoldenMonKey。 51 * 爲了實現上面的語義,這裏把「值錢的東西」這個類定義成了一個接口Valuable, 52 * 把「應該受到保護的東西」這個類也定義成了一個接口Protectable。這樣就能夠實現多繼承了。 53 * GoldenMonKey類首先從Animal類繼承,而後GoldenMonKey類再去實現Valuable接口和Protectable接口, 54 * 這樣就能夠實現GoldenMonKey類同時從Animal類,Valuable類,Protectable類繼承了,即實現了多重繼承, 55 * 實現了原來的語義。 56 * @author gacl 57 * 58 */ 59 class GoldenMonKey extends Animal implements Valuable,Protectable { 60 61 /** 62 * 在GoldenMoKey類裏面重寫了接口Protectable裏面的beProtected()這個抽象方法, 63 * 實現了接口Protectable。 64 */ 65 @Override 66 public void beProteced() { 67 System.out.println("live in the Room"); 68 } 69 70 /** 71 * 在GoldenMoKey類裏面重寫了接口Valuable裏面的getMoney()這個抽象方法,實現了接口Valuable。 72 */ 73 @Override 74 public double getMoney() { 75 return 10000; 76 } 77 78 /** 79 * 這裏重寫了從抽象類Animal繼承下來的抽象方法enjoy()。 80 * 實現了這抽象方法,不過這裏是空實現,空實現也是一種實現。 81 */ 82 @Override 83 void enjoy() { 84 85 } 86 87 public static void test() { 88 /** 89 * 實際當中在內存裏面咱們new的是金絲猴,在金絲猴裏面有不少的方法, 90 * 可是接口的引用對象v能看到的就只有在接口Valuable裏面聲明的getMoney()方法, 91 * 所以可使用v.getMoney()來調用方法。而別的方法v都看不到,天然也調用不到了。 92 */ 93 Valuable v = new GoldenMonKey(); 94 System.out.println(v.getMoney()); 95 /** 96 * 把v強制轉換成p,至關於換了一個窗口,經過這個窗口只能看獲得接口Protectable裏面的beProtected()方法 97 */ 98 Protectable p = (Protectable)v; 99 p.beProteced(); 100 } 101 } 102 103 /** 104 * 這裏讓Hen類去實現接口A,接口A又是從接口Protectable繼承而來,接口A本身又定義了一個抽象方法m(), 105 * 因此此時至關於接口A裏面有兩個抽象方法:m()和beProtected()。 106 * 所以Hen類要去實現接口A,就要重寫A裏面的兩個抽象方法,實現了這兩個抽象方法後纔算是實現了接口A。 107 * @author gacl 108 * 109 */ 110 class Hen implements A { 111 112 @Override 113 public void beProteced() { 114 115 } 116 117 @Override 118 public void m() { 119 120 } 121 122 } 123 124 /** 125 * java中定義接口 126 */ 127 public class JavaInterfacesTest { 128 129 public static void main(String[] args) { 130 GoldenMonKey.test(); 131 } 132 }
接口總結:接口和接口之間能夠相互繼承,類和類之間能夠相互繼承,類和接口之間,只能是類來實現接口。