《Java從小白到大牛》紙質版已經上架了!!!
html
設計良好的軟件系統應該具有「可複用性」和「可擴展性」,可以知足用戶需求的不斷變動。使用抽象類和接口是實現「可複用性」和「可擴展性」重要的設計手段。java
Java語言提供了兩種類:一種是具體類;另外一種是抽象了。前面章節接觸的類都是具體類。這一節介紹一下抽象類。ide
在13.4.1節介紹多態時候,使用過幾何圖形類示例,其中Figure(幾何圖形)類中有一個onDraw(繪圖)方法,Figure有兩個子類Ellipse(橢圓形)和Triangle(三角形),Ellipse和Triangle覆蓋onDraw方法。學習
做爲父類Figure(幾何圖形)並不知道在實際使用時有多少個子類,目前有橢圓形和三角形,那麼不一樣的用戶需求可能會有矩形或圓形等其餘幾何圖形,而onDraw方法只有肯定是哪個子類後才能具體實現。Figure中的onDraw方法不能具體實現,因此只能是一個抽象方法。在Java中具備抽象方法的類稱爲「抽象類」,Figure是抽象類,其中的onDraw方法是抽象方法。如圖13-1所示類圖中Figure是抽象類,Ellipse和Triangle是Figure子類實現Figure的抽象方法onDraw。
字體
提示 在UML類圖抽象類和抽象方法字體是斜體的,見圖13-1所示中的Figure類和onDraw方法都是斜體的。設計
在Java中抽象類和抽象方法的修飾符是abstract,聲明抽象類Figure示例代碼以下:3d
//Figure.java文件 package com.a51work6; public abstract class Figure { ① // 繪製幾何圖形方法 public abstract void onDraw(); ② }
代碼第①行是聲明抽象類,在類前面加上abstract修飾符。代碼第②行聲明抽象方法,方法前面的修飾符也是abstract,注意抽象方法中只有方法的聲明,沒有方法的實現,即沒有大括號({})部分。code
注意 若是一個方法被聲明爲抽象的,那麼這個類也必須聲明爲抽象的。而一個抽象類中,能夠有0~n個抽象方法,以及0~n具體方法。視頻
設計抽象方法目的就是讓子類來實現的,不然抽象方法就沒有任何意義,實現抽象類示例代碼以下:htm
//Ellipse.java文件 package com.a51work6; //幾何圖形橢圓形 public class Ellipse extends Figure { //繪製幾何圖形方法 @Override public void onDraw() { System.out.println("繪製橢圓形..."); } } //Triangle.java文件 package com.a51work6; //幾何圖形三角形 public class Triangle extends Figure { // 繪製幾何圖形方法 @Override public void onDraw() { System.out.println("繪製三角形..."); } }
上述代碼聲明瞭兩個具體類Ellipse和Triangle,它們實現(覆蓋)了抽象類Figure的抽象方法onDraw。
調用代碼以下:
//HelloWorld.java文件 package com.a51work6; public class HelloWorld { public static void main(String[] args) { // f1變量是父類類型,指向子類實例,發生多態 Figure f1 = new Triangle(); f1.onDraw(); // f2變量是父類類型,指向子類實例,發生多態 Figure f2 = new Ellipse(); f2.onDraw(); } }
上述代碼中實例化兩個具體類Triangle和Ellipse,對象f1和f2是Figure引用類型。
注意 抽象類不能被實例化,只有具體類才能被實例化。
使用接口
比抽象類更加抽象的是接口,在接口中全部的方法都是抽象的。
提示 Java 8以後接口中新增長了默認方法,所以「接口中全部的方法都是抽象的」這個提法在Java 8以後是有待商榷。
其實13.1.1節抽象類Figure能夠更加完全,即Figure接口,接口中全部方法都是抽象的,並且接口能夠有成員變量。將13.1.1節幾何圖形類改爲接口後,類圖如圖13.2所示。
提示 在UML類圖中接口的圖標是「I」,見圖13-2所示中的Figure接口。類的圖標是「C」,見圖13-2所示中的Triangle接口。
在Java中接口的聲明使用的關鍵字是interface,聲明接口Figure示例代碼以下:
//Figure.java文件 package com.a51work6; public interface Figure { ① //接口中靜態成員變量 String name = "幾何圖形";//省略public static final ② // 繪製幾何圖形方法 void onDraw(); //省略public ③ }
代碼第①行是聲明Figure接口,聲明接口使用interface關鍵字,interface前面的修飾符是public或省略。public是公有訪問級別,能夠在任何地方訪問。省略是默認訪問級別,只能在當前包中訪問。
代碼第②行聲明接口中的成員變量,在接口中成員變量都靜態成員變量,即省略了public static final修飾符。代碼第③行是聲明抽象方法,即省略了public關鍵字。
某個類實現接口時,要在聲明時使用implements關鍵字,當實現多個接口之間用逗號(,)分隔。實現接口時要實現接口中聲明的全部方法。
實現接口Figure示例代碼以下:
//Ellipse.java文件 package com.a51work6.imp; import com.a51work6.Figure; //幾何圖形橢圓形 public class Ellipse implements Figure { //繪製幾何圖形方法 @Override public void onDraw() { System.out.println("繪製橢圓形..."); } } //Triangle.java文件 package com.a51work6.imp; import com.a51work6.Figure; //幾何圖形三角形 public class Triangle implements Figure { // 繪製幾何圖形方法 @Override public void onDraw() { System.out.println("繪製三角形..."); } }
上述代碼聲明瞭兩個具體類Ellipse和Triangle,它們實現了接口Figure中的抽象方法onDraw。
調用代碼以下:
//HelloWorld.java文件 import com.a51work6.imp.Ellipse; import com.a51work6.imp.Triangle; public class HelloWorld { public static void main(String[] args) { // f1變量是父類類型,指向子類實例,發生多態 Figure f1 = new Triangle(); f1.onDraw(); System.out.println(f1.name); ① System.out.println(Figure.name); ② // f2變量是父類類型,指向子類實例,發生多態 Figure f2 = new Ellipse(); f2.onDraw(); } }
上述代碼中實例化兩個具體類Triangle和Ellipse,對象f1和f2是Figure接口引用類型。接口Figure中聲明瞭成員變量,它是靜態成員變量,代碼第①行和第②行是訪問name靜態變量。
注意 接口與抽象類同樣都不能被實例化。
在C++語言中一個類能夠繼承多個父類,但這會有潛在的風險,若是兩個父類在有相同的方法,那麼子類如何繼承哪個方法呢?這就是C++多繼承所致使的衝突問題。
在Java中只容許繼承一個類,但可實現多個接口。經過實現多個接口方式知足多繼承的設計需求。若是多個接口中即使有相同方法,它們也都是抽象的,子類實現它們不會有衝突。
圖13-3所示是多繼承類圖,其中的有兩個接口InterfaceA和InterfaceB,從類圖中能夠見兩個接口中都有一個相同的方法void methodB()。AB實現了這兩個接口,繼承了Object父類。
接口InterfaceA和InterfaceB代碼以下:
//InterfaceA.java文件 package com.a51work6; public interface InterfaceA { void methodA(); void methodB(); } //InterfaceB.java文件 package com.a51work6; public interface InterfaceB { void methodB(); void methodC(); }
從代碼中可見兩個接口都有兩個方法,其中方法methodB()徹底相同。
實現接口InterfaceA和InterfaceB的AB類代碼以下:
//AB.java文件 package com.a51work6.imp; import com.a51work6.InterfaceA; import com.a51work6.InterfaceB; public class AB extends Object implements InterfaceA, InterfaceB { ① @Override public void methodC() { } @Override public void methodA() { } @Override public void methodB() { ② } }
在AB類中的代碼第②行實現methodB()方法。注意在AB類聲明時,實現兩個接口,接口之間使用逗號(,)分隔,見代碼第①行。
Java語言中容許接口和接口之間繼承。因爲接口中的方法都是抽象方法,因此繼承以後也不須要作什麼,所以接口之間的繼承要比類之間的繼承簡單的多。如同4-4所示,其中InterfaceB繼承了InterfaceA,在InterfaceB中還覆蓋了InterfaceA中的methodB()方法。ABC是InterfaceB接口的實現類,從圖可見ABC須要實現InterfaceA和InterfaceB接口中的全部方法。
接口InterfaceA和InterfaceB代碼以下:
//InterfaceA.java文件 package com.a51work6; public interface InterfaceA { void methodA(); void methodB(); } //InterfaceB.java文件 package com.a51work6; public interface InterfaceB extends InterfaceA { @Override void methodB(); void methodC(); }
InterfaceB繼承了InterfaceA,聲明時也使用extends關鍵字。InterfaceB 中的methodB()覆蓋了InterfaceA,事實上在接口中覆蓋方法,並無實際意義,由於它們都是抽象的,都是留給子類實現的。
實現接口InterfaceB的ABC類代碼以下:
//ABC.java文件 package com.a51work6.imp; import com.a51work6.InterfaceB; public class ABC implements InterfaceB { @Override public void methodA() { } @Override public void methodB() { } @Override public void methodC() { } }
ABC類實現了接口InterfaceB,事實上是實現InterfaceA和InterfaceB中全部方法,至關於同時實現InterfaceA和InterfaceB接口。
在Java 8以前,儘管Java語言中接口已經很是優秀了,但相比其餘面向對象的語言而言Java接口存在以下不足之處:
針對這些問題,Java 8在接口中提供了聲明默認方法和靜態方法的能力。接口示例代碼以下:
//InterfaceA.java文件 package com.a51work6; public interface InterfaceA { void methodA(); String methodB(); // 默認方法 default int methodC() { return 0; } // 默認方法 default String methodD() { return "這是默認方法..."; } // 靜態方法 static double methodE() { return 0.0; } }
在接口InterfaceA中聲明瞭兩個抽象方法methodA和methodB,兩個默認方法methodC和methodD,還有聲明瞭靜態方法methodE。接口中的默認方法相似於類中具體方法,給出了具體實現,只是方法修飾符是default。接口中靜態方法相似於類中靜態方法。
實現接口示例代碼以下:
//ABC.java文件 package com.a51work6.imp; import com.a51work6.InterfaceA; public class ABC implements InterfaceA { @Override public void methodA() { } @Override public String methodB() { return "實現methodB方法..."; } @Override public int methodC() { return 500; } }
實現接口時接口中原有的抽象方法在實現類中必須實現。默認方法能夠根據須要有選擇實現(覆蓋)。靜態方法不須要實現,實現類中不能擁有接口中的靜態方法。
上述代碼中ABC類實現了InterfaceA接口,InterfaceA接口中的兩個默認方法ABC只是實現(覆蓋)了methodB。
調用代碼以下:
//HelloWorld.java文件 package com.a51work6.imp; import com.a51work6.InterfaceA; public class HelloWorld { public static void main(String[] args) { //聲明接口類型,對象是實現類,發生多態 InterfaceA abc = new ABC(); // 訪問實現類methodB方法 System.out.println(abc.methodB()); // 訪問默認方法methodC System.out.println(abc.methodC()); ① // 訪問默認方法methodD System.out.println(abc.methodD()); ② // 訪問InterfaceA靜態方法methodE System.out.println(InterfaceA.methodE()); ③ } }
運行結果:
實現methodB方法... 500 這是默認方法... 0.0
從運行結果可見,代碼第①行調用默認方法methodC,是調用類AB中的實現。代碼第②行調用默認方法methodD,是調用接口InterfaceA中的實現。代碼第③行調用接口靜態方法,只能經過接口名(InterfaceA)調用,不能經過實現類ABC調用,能夠這樣理解接口中聲明的靜態方法與其餘實現類沒有任何關係。
通過前面的學習,廣大讀者應該對於抽象類和接口所瞭解,可能會有這樣的疑問抽象類和接口有什麼區別?本節就回答這個問題。
概括抽象類與接口區別以下:
提示 學習了接口默認方法後,有些讀者還會有這樣的疑問,Java 8以後接口能夠聲明抽象方法和具體方法,這就至關於抽象類同樣了嗎?在多數狀況下接口不能替代抽象類,例如當須要維護一個對象的信息和狀態時只能使用抽象類,而接口不行,由於維護一個對象的信息和狀態須要存儲在實例成員變量中,而接口中不能聲明實例成員變量。
本章小結
經過對本章的學習,讀者能夠了解抽象類和接口的概念,掌握如何聲明抽象類和接口,如何實現抽象類和接口。瞭解Java 8以後的接口的新變化。熟悉抽象類和接口的區別。
http://edu.51cto.com/topic/1246.html
http://www.zhijieketang.com/group/5