抽象類和抽象方法
定義
- 抽象方法和抽象類都必須被abstract關鍵字修飾。
- 抽象——abstract,抽象類的方法不一定是抽象的,但抽象方法出現的類一定是抽象類。
//抽象方法,沒有方法體(即沒有{}),只有聲明
abstract void f();
- 最重要的是:抽象類,抽象既不是真的,所以,抽象類不可以實例化。但可以通過子類的實例化來使用
/**
* @author yhy
* 用來完成9.2的練習
* 驗證抽象類與抽象方法的使用
*/
public class YanZheng {
public static void main(String[] args) {
// 不能被實例化,抽象類,會報錯
// ChouXiang chouxi = new ChouXiang() ;
// 可以實例child類
// 即通過繼承其子類來實現不能繼承抽象類
Child test = new Child();
}
}
abstract class AbstractChouXiang{
/**
* 構造函數
*/
AbstractChouXiang() {
}
/**
* 定義一個抽象類的抽象方法
*/
abstract void chouxiang();
}
class Child extends AbstractChouXiang{
Child(){
System.out.println("實例時候就打印出來");
}
/**
* 注意這里不是abstract就不要講方法定義為abstract
*/
@Override
void chouxiang(){
System.out.println("繼承抽象類");
}
}
- 子類可以不是抽象類,但要實現抽象父類中的所有抽象方法,否則必須定義為abstract類型。(下面的代碼中,我將其子類的重寫方法注釋掉之后,就會報錯must be declared abstract or implentment abstract method)

與普通類的區別以及注意點:
-
抽象類也是可以與普通類那樣,可以直接extends,區別在於抽象類不能直接實例化,可以通過實例化其子類,通過子類重寫方法實現等——設置抽象方法就是讓子類來實現的,否則毫無意義。
-
與普通方法的區別
抽象方法和空方法體的方法不是同一個概念。例如,public abstract void test();是一個抽象方法,它根本沒方法體,即方法定義后面沒有一對花括號;但public void test(){}方法是一個普通方法,它已經定義了方法體,只是方法體為空,即它的方法體什么也不做,因此這個方法不可使用abstract來修飾。——瘋狂的Java講義
-
abstract不能用於修飾Field,不能用於修飾局部變量,即沒有抽象變量、沒有抽象Field等說法;abstract也不能用於修飾構造器,沒有抽象構造器,抽象類里定義的構造器只能是普通構造器。
抽象類的作用
-
《thinking in java》
-
抽象類是普通的類與接口之間的一種中庸之道。
-
抽象方法、抽象類可以使類的抽象性明確起來,告訴用戶和編譯器怎么使用它們;
-
同時,抽象類是很好的重構工具,在后期的工作中,可以實現重用性。
-
-
體現一種模板的效果,從一群相似的子類提煉出一個抽象類的感覺一樣,提供了一種規范,子類可以在其原來的基礎上進行擴展。
-
抽象父類可以只定義需要使用的某些方法,把不能實現的部分抽象成抽象方法,就是一中留給下一代去實現,一開始沒有能力去實現,那可就給厲害的人去做,留給其子類去實現。
接口
定義
- 特殊的“抽象類”——接口(interface):比抽象類更加抽象的是接口,在接口中所有的方法都是抽象的。就不能像上面的抽象類一樣還可以有普通方法。
//省略public就變為默認級別,只能在當前包所訪問
public interface Figure {
//接口中靜態成員變量
String name = "幾何圖形";//省略public static final
// 繪制幾何圖形方法
void onDraw(); //省略public 這里是抽象方法
}
- Java中可以implements多個接口,多繼承的含義便是接入多個接口(繼承只能單繼承)
- 一個類可以實現一個或多個接口,繼承使用extends關鍵字(但接口只能繼承接口),實現則使用implements關鍵字。
示例
- JieKou.java
import java.text.SimpleDateFormat;
/**
* @author yhy
* 這個是實現接口定義的代碼,在其它地方去調用
* 這里的接口不用public的話,其它的包就訪問不了
*/
public interface JieKou {
// 定義了兩個常量
/**
* 這里定義一個df變量來獲取當前時間
*/
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//設置日期格式
String AUTHOR = "yhycoder";
/**
* 定義一個接口的方法
* 這里的public是多余的,在接口里面是自動為public
*/
/*public*/ void print();
}
- 使用接口的Java代碼
//訪問了其它包的接口,就是下面這個地址
import music.daima.ebook.JieKou;
import java.util.Date;
public class UseInterfaces {
public static void main(String[] args) {
//實例化using類,實現查看代碼的運行情況
Using Shuchu = new Using();
Shuchu.print();
}
}
/**
* 這里是接口繼承接口
*/
interface Jiekou2 extends JieKou{
String num = "接口2";
}
/**
* 這里是Using類實現了JieKou和Jiekou2接口,逗號隔開
*/
class Using implements JieKou,Jiekou2 {
/**
* 重寫了方法,調用接口定義的常量
*/
@Override
public void print() {
System.out.println(AUTHOR+"在新的包里面使用接口的時間:"+df.format(new Date())+" 同時還有"+num);
}
}
注意
- 接口與抽象類一樣都不能被實例化
- 實現接口時接口中原有的抽象方法在實現類中必須實現。默認方法可以根據需要有選擇實現(覆蓋)。靜態方法不需要實現,實現類中不能擁有接口中的靜態方法。(Java 8之后)
//InterfaceA.java文件,定義一個接口
public interface InterfaceA {
//抽象方法
void methodA();
String methodB();
// 默認方法
default int methodC() {
return "6666";
}
// 默認方法
default String methodD() {
return "這是默認方法";
}
// 靜態方法
static double methodE() {
return 0.0;
}
}
實現接口代碼
import xxxx.InterfaceA;
public class ABC implements InterfaceA {
//重寫
@Override
public void methodA() {
}
@Override
public String methodB() {
return "實現methodB方法...";
}
//重寫覆蓋,根據自己的需要來。
@Override
public int methodC() {
return 500;
}
}
//實現類中不能有接口中的靜態方法,最后一行
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());
}
}
作用
- 規范,在分配不同人的任務時,接口就像是總綱一樣,告訴大家去實現哪些功能模塊等。(命名規范都有限制到)
最后:接口與抽象類的異同
不同
-
接口interface,實現接口則使用implements;抽象類abstract
-
抽象類可以有普通方法。Java 8 之前接口中只有抽象方法,而 Java 8 之后接口中也可以聲明具體方法,具體方法通過聲明默認方法實現。
-
接口可以繼承多個,而抽象類不可以。
-
和類繼承相似,子接口擴展某個父接口,將會獲得父接口里定義的所有抽象方法、常量Field、內部類和枚舉類定義。
-
實現父接口的所有:一個類實現了一個或多個接口之后,這個類必須完全實現這些接口里所定義的全部抽象方法(也就是重寫這些抽象方法);否則,該類將保留從父接口那里繼承到的抽象方法,該類也必須定義成抽象類。
-
接口定義的是一種規范,因此接口里不能包含構造器和初始化塊定義。接口里可以包含Field(只能是常量)、方法(只能是抽象實例方法)、內部類(包括內部接口、枚舉)定義。但抽象類與普通類一樣,可以有構造器,初始化模塊等。
-
接口只有常量——接口中不能有實例成員變量,接口所聲明的成員變量全部是靜態常量,即便是變量不加 public static final 修飾符也是靜態常量。抽象類與普通類一樣各種形式的成員變量都可以聲明。
相同
- 都不能直接實例化來使用。
- 接口和抽象類都可以包含抽象方法,實現接口或繼承抽象類的普通子類都必須實現這些抽象方法。
使用場景
-
想要多重繼承的時候——接口(功能性強,規范性)
-
想要底層基礎功能模塊不斷改變——抽象類(模板設計)
感謝
才疏學淺,不對的地方多多指教!