1. 程式人生 > >JAVA中面向物件基礎:抽象類、初始化塊

JAVA中面向物件基礎:抽象類、初始化塊

本文轉載連線為: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();
	}
}

編譯和執行程式,我們看看結果:

image

這個例子裡,我們有這麼幾點需要留意:

  1. 一個編譯單元裡是可以寫多個頂級類的,只要public修飾的頂級類只有一個就行了。
  2. 用 abstract 修飾的類是抽象類
  3. 用 abstract 修飾的方法是抽象方法,抽象方法沒有方法體,也就是說不能寫大括號。
  4. 抽象類實際上是定義了一個標準和規範,等著他的子類們去實現,譬如動物這個抽象類裡定義了一個發出聲音的抽象方法,它就定義了一個規則,那就是誰要是動物類的子類,誰就要去實現這個抽象方法。
  5. 狗和獅子的類繼承了動物這個抽象類,實現了發出聲音的方法。
  6. 一個物件除了被看成自身的類的例項,也可以被看成它的超類的例項。我們把一個物件看做超類物件的做法叫做向上轉型。譬如 Animal a1 = new Dog();
  7. 雖然都是動物型別,但是方法在執行時是按照它本身的實際型別來執行操作的。因此 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();
	}
}
執行程式,檢視結果:

image

我們把焦點放在第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();
	}
}
編譯並執行程式,檢視結果:

image

從上面的例子中我們需要留意如下幾點:

  1. 初始化塊的語法相當簡單,它沒有名稱,沒有引數,也沒有返回值,只有一個大括號。用 static 修飾的初始化塊就要靜態初始化塊,相對應的,沒有static修飾的初始化塊就叫例項初始化塊。
  2. 靜態初始化塊在首次載入類時會執行一次。
  3. 例項初始化塊在每次建立物件時會執行一次。
  4. 例項初始化塊在建構函式的super()呼叫之後執行。
  5. 初始化塊之間的執行順序取決於他們在類檔案中出現的順序,出現在前面的先執行。
  6. 初始化塊從書寫慣例上應該寫在靠近類檔案的頂部,建構函式附近的某個位置。