該系列博文會告訴你如何從入門到進階,一步步地學習Java基礎知識,並上手進行實戰,接着瞭解每一個Java知識點背後的實現原理,更完整地瞭解整個Java技術體系,造成本身的知識框架。java
當編寫一個類時,經常會爲該類定義一些方法,這些方法用以描述該類的行爲方式,那麼這些方法都有具體的方法體。但在某些狀況下,某個父類只是知道其子類應該包含怎樣的方法,但沒法準確地知道這些子類如何實現這些方法。例如定義了一個Shape類,這個類應該提供一個計算周長的方法calPerimeter(),但不一樣Shape子類對周長的計算方法是不同的,即Shape類沒法準確地知道其子類計算周長的方法。設計模式
可能有讀者會提出,既然Shape類不知道如何實現calPerimeter()方法,那就乾脆不要管它了!這不是一個好思路:假設有一個Shape引用變量,該變量實際上引用到Shape子類的實例,那麼這個Shape變量就沒法調用calPerimeter()方法,必須將其強制類型轉換爲其子類類型,纔可調用calPerimeter0方法,這就下降了程序的靈活性。框架
如何既能讓Shape類裏包含calPerimeter()方法,又無須提供其方法實現呢?使用抽象方法便可知足該要求:抽象方法是隻有方法簽名,沒有方法實現的方法。ide
抽象方法和抽象類必須使用abstract修飾符來定義,有抽象方法的類只能被定義成抽象類,抽象類裏面能夠沒有抽象方法。學習
抽象方法和抽象類的規則以下:測試
說了一大堆概念,聽得有點糊塗了,下面咱們來看一段代碼:this
下面定義一個Shape抽象類:設計
public abstract class Shape { { System.out.println("執行Shape的初始化塊"); } private String color; //定義一個計算周長的抽象方法 public abstract double calPerimeter(); //定義一個返回形狀的抽象方法 public abstract String getType(); //定義Shape的構造器,該構造器並非用於建立對象,而是被子類調用 public Shape() {} public Shape(String color) { System.out.println("執行Shape的構造器"); this.color=color; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } }
上面的Shape類裏包含了兩個抽象方法:calPerimeter()和 getType(),因此這個Shape類只能被定義成抽象類。Shape類裏既包含了初始化塊,也包含了構造器,這些都不是在建立 Shape對象時被調用的,而是在建立其子類的實例時被調用。對象
抽象類不能用做建立實例,只能當作父類被其餘子類繼承。blog
下面定義一個三角形類,三角形類被定義成普通類,繼承Shape抽象類,所以必須實現Shape類中的抽象方法
public class Triangle extends Shape { //定義三角形的三邊 private double a; private double b; private double c; public Triangle(String color,double a,double b,double c) { super(color); setSize(a, b, c); } public void setSize(double a,double b,double c) { if(a+b<=c||a+c<=b||b+c<=a) { System.out.println("三角形兩邊之和必須大於第三邊"); return; } this.a=a; this.b=b; this.c=c; } //重寫Shape類計算周長的方法 @Override public double calPerimeter() { return a+b+c; } //重寫Shape類返回形狀的方法 @Override public String getType() { // TODO Auto-generated method stub return "三角形"; } }
上面的Triangle類繼承了Shape抽象類,並實現了Shape類中兩個抽象方法,是一個普通類,所以能夠建立 Triangle類的實例,可讓一個Shape類型的引用變量指向Triangle對象。
下面編寫測試代碼:
public class TestShape { public static void main(String[] args) { Shape s1=new Triangle("黑色", 3, 4, 5); System.out.println(s1.getColor()); System.out.println(s1.getType()); } }
輸出結果:
執行Shape的初始化塊 執行Shape的構造器 黑色 三角形
利用抽象類和抽象方法的優點,能夠更好的發揮多態的優點,使得程序更加靈活。
使用抽象類有如下幾點須要注意:
一、當使用abstract修飾類時,代表這個類時抽象類,不能實例化,只能被繼承;當使用abstract修飾方法時,代表這個方法必須由子類去實現。
二、final修飾的類不能被繼承,final修飾的方法不能被重寫,所以final和abstract不能同時出現。
三、abstract不能用於修飾成員變量,不能用於修飾局部變量,即沒有抽象變量、沒有抽象成員變量等說法;abstract也不能用於修飾構造器,沒有抽象構造器,抽象類裏定義的構造器只能是普通構造器。
四、當使用static修飾一個方法時,代表這個方法屬於該類自己,即經過類就可調用該方法,但若是該方法被定義成抽象方法,則將致使經過該類來調用該方法時出現錯誤(調用了一個沒有方法體的方法確定會引發錯誤)。所以static和abstract不能同時修飾某個方法,即沒有所謂的類抽象方法。
五、abstract修飾的方法沒有方法體,必須被之類重寫纔有意義,因此抽象方法不能用private修飾,也就是private和abstract不能同時使用。
抽象類是從多個類中抽象出來的模板,若是將這種抽象進行得更完全,則能夠提煉出一種更加特殊的「抽象類」——接口(interface)。Java9對接口進行了改進,容許在接口中定義默認方法和類方法,默認方法和類方法均可以提供方法實現,Java9爲接口增長了一種私有方法,私有方法也可提供方法實
和類定義的不一樣,定義接口再也不使用class關鍵字,而是使用interface關鍵字。接口的基本語法以下:
[修飾符] interface 接口名稱 extends 父接口1 父接口2 .... { 零到多個常量定義... 零到多個抽象方法定義... 零到多個內部類,接口,枚舉定義... 零到多個私有方法,默認方法或者類方法定義... }
下面來看一個具體的接口:
public interface Output { //接口中定義的成員變量只能是常量 int MAX_CACHE_LINE=50; //接口中定義的普通方法只能是public abstract抽象方法 void out(); //在接口中定義默認方法,須要用default修飾 default void print(String...msg) { for (String str : msg) { System.out.println(str); } } //在接口中定義類方法,須要使用static修飾 static String staticTest() { return "接口中的類方法"; } //定義私有方法 private void foo() { System.out.println("接口中的私有方法"); } //定義私有靜態方法 private static void bar() { System.out.println("bar私有靜態方法"); } }
接口的繼承和類繼承不同,接口徹底支持多繼承,即一個接口能夠有多個直接父接口。和類繼承類似,子接口擴展某個父接口,將會得到父接口裏定義的全部抽象方法、常量。
一個接口繼承多個父接口時,多個父接口排在extends關鍵字以後,多個父接口之間以英文逗號(,)隔開。下面程序定義了三個接口,第三個接口繼承了前面兩個接口。
interface InterfaceA{ int PROP_A=5; void testA(); } interface InterfaceB{ int PROP_B=6; void testB(); } interface InterfaceC extends InterfaceA,InterfaceB{ int PROP_C=7; void testC(); } public class InterfaceExtendsTest { public static void main(String[] args) { System.out.println(InterfaceC.PROP_A); System.out.println(InterfaceC.PROP_B); System.out.println(InterfaceC.PROP_C); } }
接口不能用於建立實例,但接口能夠用於聲明引用類型變量。當使用接口來聲明引用類型變量時,這個引用類型變量必須引用到其實現類的對象。除此以外,接口的主要用途就是被實現類實現。概括起來,接口主要有以下用途。
一個類但是實現多個接口,用關鍵字implements實現,類實現接口的語法格式以下:
[修飾符] class 類名 extends 父類 implements 接口1,接口2...{
類體部分
}
注意:類實現接口時必需要實現接口中全部的抽象方法
interface interfaceA{ void printA(); } interface interfaceB{ void printB(); } public class ImplmentsTest implements interfaceA,interfaceB { @Override public void printB() { System.out.println("printB"); } @Override public void printA() { System.out.println("printA"); } }
上述代碼的ImplmentsTest實現了兩個接口,並重寫了其中的抽象方法
相同點:
不一樣點: