JAVA中面向物件基礎:抽象類、初始化塊
阿新 • • 發佈:2019-01-02
本文轉載連線為:http://android.yaohuiji.com/archives/3241
一、抽象類
用 abstract 修飾的類定義,我們稱之為抽象類,抽象類不能被例項化。
用 abstract 修飾的方法,我們稱之為抽象方法,抽象方法不能有方法體。
面向物件中,所有的物件都是某一個類的例項,但是並不是每個類都可以例項化成一個物件。如果一個類中沒有足夠的資訊來描繪一個具體的物件,那麼這個類就不能被例項化,我們稱之為抽象類。抽象類用來描述一系列看起來不同,但究其本質是相同的物件。譬如把蘋果、橘子、梨等抽象出來一個概念叫水果,我們把狗、老鼠、貓、獅子、大象、豬等抽象出來個概念叫動物。這時候我們把動物抽象成一個Animal類時,就最好不要讓它直接初始化,創建出一個Animal()例項物件的結果似乎難以想象。
抽象類被繼承之外,沒有用途,沒有目的。
下面我們用一個Test.java的例子看一下什麼叫抽象類:
abstract class Animal { abstract void makenoise(); } class Lion extends Animal { @Override void makenoise() { System.out.println("獅子吼!"); } } class Dog extends Animal { @Override void makenoise() { System.out.println("狗叫!"); } } public class Test { public static void main(String[] args){ Animal a1 = new Dog(); Animal a2 = new Lion(); a1.makenoise(); a2.makenoise(); } }
編譯和執行程式,我們看看結果:
這個例子裡,我們有這麼幾點需要留意:
- 一個編譯單元裡是可以寫多個頂級類的,只要public修飾的頂級類只有一個就行了。
- 用 abstract 修飾的類是抽象類
- 用 abstract 修飾的方法是抽象方法,抽象方法沒有方法體,也就是說不能寫大括號。
- 抽象類實際上是定義了一個標準和規範,等著他的子類們去實現,譬如動物這個抽象類裡定義了一個發出聲音的抽象方法,它就定義了一個規則,那就是誰要是動物類的子類,誰就要去實現這個抽象方法。
- 狗和獅子的類繼承了動物這個抽象類,實現了發出聲音的方法。
- 一個物件除了被看成自身的類的例項,也可以被看成它的超類的例項。我們把一個物件看做超類物件的做法叫做向上轉型。譬如 Animal a1 = new Dog();
- 雖然都是動物型別,但是方法在執行時是按照它本身的實際型別來執行操作的。因此 a1.makenoise()執行的是狗叫,a2.makenoise()執行的是獅子吼,我們稱之為執行時多型。
我們再看一下把一個類看做一個超類有什麼樣的損失或者不便,我們把上面的例子稍微改一下:
abstract class Animal {
abstract void makenoise();
}
class Lion extends Animal {
@Override
void makenoise() {
System.out.println("獅子吼!");
}
}
class Dog extends Animal {
@Override
void makenoise() {
System.out.println("狗叫!");
}
void bark(){
System.out.println("汪,汪!");
}
}
public class Test {
public static void main(String[] args){
Animal a1 = new Dog();
Animal a2 = new Lion();
a1.makenoise();
a2.makenoise();
((Dog)a1).bark();
}
}
執行程式,檢視結果:我們把焦點放在第35行,我們再a1前面加了一個(Dog),這個做法的意思是把a1強制轉換為Dog物件,只有轉換為Dog物件後,才能使用bark方法,否則即使你知道他是一個Dog物件也不能呼叫bark方法。這就是子類物件付給超類引用所帶來的不便或者說是損失。
二、初始化塊
我們已經知道在類中有兩個位置可以放置執行操作的程式碼,這兩個位置是方法和建構函式。初始化塊是第三個可以放置執行操作的位置。當首次載入類(靜態初始化塊)或者建立一個例項(例項初始化塊)時,就會執行初始化塊。
我們看一個例子:
class SuperClass{
SuperClass(){
System.out.println("父類SuperClass的建構函式");
}
}
public class Initialize extends SuperClass {
Initialize(int x){
System.out.println("帶引數的建構函式");
}
Initialize(){
System.out.println("不帶引數的建構函式");
}
static {
System.out.println("第一個靜態初始化塊");
}
{ System.out.println("第一個例項初始化塊");}
{ System.out.println("第二個例項初始化塊");}
static {
System.out.println("第二個靜態初始化塊");
}
public static void main(String[] args){
new Initialize(1);
new Initialize();
}
}
編譯並執行程式,檢視結果:從上面的例子中我們需要留意如下幾點:
- 初始化塊的語法相當簡單,它沒有名稱,沒有引數,也沒有返回值,只有一個大括號。用 static 修飾的初始化塊就要靜態初始化塊,相對應的,沒有static修飾的初始化塊就叫例項初始化塊。
- 靜態初始化塊在首次載入類時會執行一次。
- 例項初始化塊在每次建立物件時會執行一次。
- 例項初始化塊在建構函式的super()呼叫之後執行。
- 初始化塊之間的執行順序取決於他們在類檔案中出現的順序,出現在前面的先執行。
- 初始化塊從書寫慣例上應該寫在靠近類檔案的頂部,建構函式附近的某個位置。