首先為什麼會有抽象類和接口的産生?
當提到這裡時必須先講一講方法覆寫
方法覆寫:定義了方法名稱,傳回值類型,參數的類型及個數完全相同,
被覆寫的方法不能有比子類更為嚴格的通路控制權限。
方法覆寫:多态的關鍵所在。
是以為了強制要求子類必須覆寫父類方法産生了抽象類與接口.
多态:一個類執行個體的相同方法在不同情形下有不同的表現形式。
回顧:
向上轉型:參數統一化
父類 父類引用 = new 子類執行個體();
father per = new Student();
向下轉型:要發生向下轉型必須先發生向上轉型
子類 子類引用 = (子類) 父類執行個體;
Student stu = (Student) per;
是以不管是向上轉型還是向下轉型都發生了方法覆寫。
抽象類說明符abstract:
一個類被聲明為abstract,則表示該類是無具體對象的抽象類,
即該類不能進行執行個體化操作.不能用new關鍵字建立該類的對象,
隻能由其子類的抽象方法的具體實作來完成.
抽象類:
Abstract:
定義:抽象類是普通類的超級,隻是比普通類多了一些抽象方法而已。
首先知道:
抽象方法:使用抽象方法定義的隻有方法聲明沒有方法體的方法.
文法:
public abstract void test();
//隻有方法聲明,沒有方法體的方法
- 抽象類中包含抽象方法,則抽象類也必須使用abstract來定義,表示抽象類
本地方法(Java調用c同名方法):使用native定義的隻有方法聲明沒有方法體的方法。
public native int hashCode();
注意:隻有方法聲明沒有方法體的方法就是抽象方法(×)
因為在Java中還有native(本地方法);
抽象類的使用限制或者使用原則
(抽象類一定不能new)
-
a.所有抽象類必須有子類(final與abstract不能同時出現)
原因:abstract一定有子類,而final一定沒有子類(不能繼承)。 (final可以修飾類)
因為由子類的抽象方法的具體實作來完成。
例子:
//具體定義
abstract class Person{
private String name;
private int age;
public void setName(String name){
this.name = name;
}
//隻聲明而為實作的方法(不能直接使用,隻能由子類的抽象方法來實作完成)
public abstract void getPerson();
}
public class Test{
public static void main(String[] args) {
}
}
- b.抽象類的子類必須覆寫所有抽象方法(或者子類也使用abstract關鍵字定義)
定義抽象類的目的就是想讓類繼承,實作覆寫
private與abstract不能同時出現,抽象方法必須被覆寫而private方法無法被覆寫
- c.抽象類方法無法直接産生執行個體化對象,但可以通過子類向上轉型進行執行個體化。
(解釋:抽象類是“半成品”,不能直接new 自己;由于存在向上轉型,如果子類是一個普通類,可以把他變為抽象類進行使用)
且子類依然遵循對象執行個體化流程,先調用抽象類構造方法而後再調用子類構造。
(解釋:先調用父類構造,産生父類對象,才有子類對象)
(抽象類是普通類的超級,普通類有構造方法,那抽象類一定有構造方法)
- d.抽象類可以沒有抽象方法,但是此時任然不能直接執行個體化對象。
例子:
//具體定義
abstract class Person{
private String name;
private int age;
public void setName(String name){
this.name = name;
}
//隻聲明而為實作的方法(不能直接使用)
public abstract void getPersonInfo();
}
class Student extends Person{
public void getPersonInfo(){
System.out.println("111");
}
}
public class Test{
public static void main(String[] args) {
//通過向上轉型,把Student對象指向了一個父類的引用
Person per = new Student();
//這時調用per.getPersonInfo();執行輸出
per.getPersonInfo();
}
}
筆試題:
//請問輸出是多少?
A.0
B.30
C.100
D.編譯錯誤
abstract class Person {
public Person() {
this.print();
}
public abstract void print();
}
class Student extends Person{
private int num = 100;
public Student(int num){
this.num = num;
}
public void print(){
System.out.println(num);
}
}
public class Test{
public static void main(String[] args) {
new Student(30);
}
}
輸出:A.0
解釋:
一:**從入口開始看**,
第一步調用子類Student這個類的構造方法,将30傳值,但是
在**子類構造中有一個隐藏的super();(因為繼承關系)**,是以先調用
父類構造方法再到子類。故當調用子類這個有參構造時連方法都進不去。
二:是以先在父類(**本質上第一步從父類開始**),Person的無參構造
調用了print方法(this表示本類對象),this在父類中,且用abstract關鍵字修飾,
是以此時print方法調用不了。
三:故去尋找子類中有無print方法,至子類中的print方法中,
子類的print方法覆寫了父類,故從子類的覆寫後的方法進行輸出,
此時的num還未進行this.num = num;這一行的指派。
四:實際上private int num = 100;這一行在編譯的時候在構造方法中執行,
但是在主方法中有new,是以開辟了一塊新空間,所有屬性都有預設值,故輸出結果為0;
擴充:
擴充:
public class Test{
public static void main(String[] args) {
new Student(30).print();
}
}
輸出0,30
此時Student這個構造方法已經全部執行完成,最後才調用print方法,
print方法是通過子類Student類new調用,print方法被覆寫,
是以調用的一定是被覆寫後的方法。(先等于100,在等于30),
故輸出30.
例子:
abstract class Person{
public abstract void print();
//在抽象類内部提供了一個實作好的子類,
//(靜态方法可通過類名稱直接調用)
public static Person getInstance(){
//方法内部類,繼承了抽象類并實作,
//方法傳回了實作好的子類
class Student extends Person{
public void print(){}
}
return new Student();
}
}
public class Test{
public static void main(String[] args) {
Person per = new Student();
per.print();
}
}
- 匿名内部類:等同于方法内部類,就是沒有名字,但是匿名内部類必須繼承一個抽象類或者實作一個接口,除此之外與方法内部類完全一緻
例子:
abstract class Person{
public abstract void print();
//在抽象類内部提供了一個實作好的子類(靜态方法可通過類名稱直接調用)
public static Person getInstance(){
//匿名内部類,隐藏了沒有名字的類
//等同于class 無名 extends Person{}
//直接執行個體化:new Person();
return new Person(){
//内部類
public void print(){}
}
}
}
.