所謂的「物件導向程式設計」(OOP, Object-Oriented Programming),包括了「物件導向」與「程式設計」兩種技術的整合,在西元1980年代,物件導向技術影響了很多資訊科學的領域,產生的結果多半是正面的,其實物件導向的觀念起源得更早,只是到近年來才逐漸地被理論化和系統化,並導入各種領域中加以運用。程式設計是很多人都曾有過的經驗,目的是要指揮電腦來解決問題,不同的程式語言往往提供了不同方法來解決問題,而「物件導向程式語言」(Object-Oriented Programming Language)所提供的方法就是建立在物件導向的基礎上。Java算是一種命令式的物件導向程式語言,本章透過Java語言來了解物件導向的程式設計。
5-1-1
5-1-2
-
分類可以將複雜的事物釐清。
-
「類別」(Class)是用來將物件分類的。
-
屬於同一類別的物件具有很多相同的特徵。
-
也可以說類別是鑄造物件的模子,物件則是類別的案例(Instance)。
5-2-1 物件導向程式設計的基本觀念
-
物件導向程式設計的精髓在於類別(class)與物件(object)的運用。
-
通常程式設計所產生的是對於現實事物的一種描述。
-
電腦系統在目前的科技下就是得接受「抽象描述」(abstraction)。
-
Java是一種程式語言,支援抽象描述的能力,所用的就是類別與物件。
5-2-2 從程式執行詮釋物件的觀念
基本觀念: 物件是類別的案例 (instance)。 類別像是建立物件的架構或模子。 在 Java 程式執行的過程中擔當大任的主要是物件。 在使用前,物件必須先被生產出來。
物件建立的步驟: 參考變數 (reference variable)的宣告, (declaration) 產生物件
參考變數(reference variable)的宣告: 物件的內容可多可少,當我們指名物件時,無法以其內容來分辨。 Java 採用物件參考值 (object reference value) 來指名某一個物件,有點像 C/C++ 裡頭指標和位址的觀念。在建立物件以前,我們得用物件參考變數來宣告物件在程式裡頭的名稱。
產生物件: 利用建構子(constructor)來產生類別案例,也就是物件。 關鍵字 new會指示系統傳回一個指定類別所屬物件的參考值,當做參考變數的值。 由於建構子是一個程序,物件產生過程中物件本身的一些成員是利用建構子的執行而建立的。 宣告和案例化兩個步驟在語法上也可以合併在一起。 由於物件移到記憶體之後占用了空間,最好能回收。
5-2-3 物件的成員與類別的成員
基本觀念: 物件是類別的案例,擁有了類別中定義的屬性與方法,這些都是物件的成員( 可稱之為 instance member)。 物件的屬性成員具有指定的值,方法成員則是同一類別的所有物件都一樣的。
基本觀念: 物件的屬性成員決定了物件的狀態(state),每個物件的狀態決定於其屬性成員所具有的「值 (value)」。 類別可以具有不屬於任何物件的屬性成員,我們把它叫做靜態成員 (static member)。 靜態成員的處理可以透過類別名稱或是該類別的物件的名稱來進行。
與物件成員和靜態成員相關的名詞
「句點表示法」(dot notation): 程式中對於各種成員的利用,必須遵循語法與規則。 通常我們可以透過方法的呼叫 (method invocation)來觀察物件所表現的行為。 所謂的「句點表示法」(dot notation)就是常用的呼叫方法的語法。
5-3-1 Java類別的基本語法
基本觀念: 用物件導向程式語言來寫程式通常都要從撰寫類別的定義開始。 了解如何從類別的定義來產生物件。 把應用系統的邏輯放到物件的各種互動關係中。
一個簡單的Car 類別定義
// 一個簡單的Car類別定義
class Car { //Car是類別名稱
String model; //model, year, color均為屬性
int year;
String color;
void EstimatePrice() { //void EstimatePrice是Car類別的方法成員
if (year > 1985) {
System.out.println("This car costs more than 250000 dollars.\n");
}
else {
System.out.println("This car costs less than 250000 dollars.\n");
}
}
}
「存取修飾元」(Access Modifier): 預設的(Default):在同一個Package中的類別,都能引用預設存取型態的類別成員。 公用的(Public):成員可被任何的類別與物件存取,沒有限制。 保護的(Protected):只有原類別或其子類別的方法能使用該成員。 私有的(Private):僅讓成員給原類別使用。
其他的修飾元: static:假如成員前多了static關鍵字,代表該成員一旦有了數值,在類別所有的物件中,都指定成該數值。 final:final修飾元用來指定成員屬性的值為常數,或是指定成員方法在子類別中不被變更(Overridden)。
synchronized:synchronized 修飾元強制系統在任一時刻,僅有一個程序或執行緒(Thread) 在執行被修飾的方法,由於Java 支援多執行緒同時執行,有可能在同一時刻,好幾個執行緒都執行同一個類別方法,假如不希望這種情況發生,就可以在方法前加上synchronized 的修飾元。 native:native 修飾元則是告訴系統某個方法的實際執行碼是用C語言撰寫的,例如:
native int compute y2k( );
定義子類別的方式
// 定義子類別的方式
class MyCar extends Car { //Car是父類別,MyCar是子類別,繼承Car的屬性
String LicensePlate;
String Dealer;
}
5-3-2「重訂」(Override) 與 「重載」(Overloading)
基本觀念: 子類別也可以變更繼承自父類別之方法的功能,我們把這種現象稱做「重訂」(Override)。 所謂的「重載」(Overloading)是指同樣的類別中含有多個名稱相同的方法,卻具有不同的功能,系統可從這些方法的呼叫參數來辨別其差異。
重訂與重載的方式
// 子類別變更繼承自父類別之方法的功能
class overRideCar extends Car {
String LicensePlate;
String Dealer;
void EstimatePrice() {
if (year > 1985) {
System.out.println("This car costs more than 550000 dollars.\n");
}
else {
System.out.println("This car costs less than 550000 dollars.\n");
}
}
}
// 重載的方式
class overloadCar extends Car {
String LicensePlate;
String Dealer;
void accelerate() {
System.out.println("Just accelerate!\n");
}
void accelerate(int a) {
System.out.println("Accelerate to 100 mph in "+a+" seconds!\n");
}
}
測試「重訂」的觀念
class Test2 {
public static void main(String args[ ]) {
overRideCar ford = new overRideCar();
ford.year=1999;
ford.model="Modeo";
ford.LicensePlate="CO-1211";
ford.EstimatePrice();
}
}
This car costs more than 550000 dollars.
測試「重載」的觀念
class Test3 {
public static void main(String args[]) {
overloadCar ford = new overloadCar();
ford.accelerate();
ford.accelerate(5);
}
}
Just accelerate!
Accelerate to 100 mph in 5 seconds!
5-3-3 抽象類別 (Abstract class) 的觀念
抽象類別代表僅含定義不含實際執行程式碼的類別。 通常抽象類別可用來萃取多種類別的共通性,在設計上比較方便。 抽象的方法 (Abstract method) 是不含執行程式碼的方法。
抽象類別與抽象方法的例子
abstract class job{
abstract void cjob();
}
抽象方法在使用上的限制: 建構子不能是抽象方法。 抽象方法不能和 static 或private用在一起。 Java支援所謂的「介面」(Interface)定義,可看成是類別定義的樣版,和抽象類別的概念很類似,但是「介面」定義中的方法成員完全不含程式碼,屬性成員僅限於常數 (Constant)。
Interface 的例子
interface job {
abstract public void Major_jobs( );
}
class FireFighter implements job {
int JobCode; public void Major_jobs( ) {
System.out.println(“Fight fire!\n”);
}
}
5-3-4 類別的繼承關係
我們可以利用現有類別來建立新的類別,新的類別除了繼承現有類別的屬性和方法之外,還可自行定義新的。
通化generalization: 在系統分析與設計的過程當中,不見得先有父類別,很可能先有子類別,萃取出共有的特性來定義父類別,這個過程稱作「通化」。
特化specialization: 若是先有父類別,再一一地定義出子類別,則叫做「特化」。
5-3-5 類別的繼承關係和物件的衍生關係
類別的繼承關係: 父類別的定義含有各種子類別的通化特性,也就是子類別們的共同點。 子類別的定義除了繼承父類別中的定義之外,還可以依個別的特性加入其他的定義。
簡易的類別架構
物件產生的兩個階段
宣告物件的資料型態:說明物件是由那種類別衍 生出來的。
建立物件:利用 new 關鍵字將物件建立起來。
類別的定義
// 類別r的定義
class r {
int a;
void f( ) {System.out.println("from r");}
}
// 類別s的定義
class s extends r {
int b;
void g( ) {System.out.println("from s");}
物件的衍生
class w {
public static void main(String args[ ]) {
r oR; s oS;
oR=new r( ); oS=new s( );
oR.f( ); oS.g( );
oR.a= 23; oS.b= 34; oS.a=233;
System.out.println("oR.a= " + oR.a);
System.out.println("oS.b= " + oS.b);
System.out.println("oS.a= " + oS.a);
}
}
5-3-6 Java中的各種類別
基本觀念: 類別和物件所描述的都是應用系統裡的實體或是抽象的觀念,其屬性可由基本值 (literal)與其他的物件參考所組成。 類別與物件和基本資料型態 (primitive date type)是不同的,通常都具有比較複雜的結構與功能, 所以對於應用系統的描述能力自然就較為優越。
Java完成的軟體系統會包含那些東西呢? 就是一群類別的集合。 連主程式也是某個類別中的一個方法,我們可以把整個系統看成是類別的組合。是大多數的系統都會善用現有的類別。類別之間的引用關係: 類別之間可能存在的引用關係。 除了利用繼承與聚集來產生新類別之外,也可以用 import將其他類別組成的包裹 (package)引入程式中,直接使用包裹中的類別。 這種類別再用的方式,再加上物件導向觀念的應用,使 Java 在程式設計的方法上提供了相當好的支援。
Java 中的各種類別
5-4-1 This 的用法
基本觀念: 定義類別時,除了要取名之外,還得訂出該類別的屬性與方法。 完成定義之後,類別就可以當做一種資料型態,用來宣告變數,這些變數會指向未來由類別所衍生的物件。 物件繼承了類別的屬性和方法,我們可以在程式中引用並執行物件所繼承的方法。 這些方法通常也叫做案例方法(instance method),和靜態方法(static method)是不一樣的。
為什麼需要this關鍵字? 在定義類別中的案例方法時.可能會發生一種情況,就是方法中的程式行引用到物件本身,這時候我們並不知道未來物件的名稱是什麼。 Java語法提供了this 關鍵字,讓我們能從案例方法的程式行中引用到物件本身。
This 的用法
class Cat {
// 案例變數 (instance variables)
int Weight;
String Name;
String Color;
// 建構子 (constructor)
public Cat(int Weight, String Name, String EyeColor) {
String Color;
this.Weight=Weight;
Name=Name;
Color=EyeColor;
this.PrintCat();
PrintCat();
}
void PrintCat ( ) { System.out.println(this); }
}
區別參數名稱與屬性名稱
5-4-2 徹底了解物件與類別的特性
類別 a 的定義
class a {
private int x;
public int y;
private void hi( ) {
System.out.println("hi");
}
public static void h( ){
System.out.println("h");
a d = new a( ); // 可以reference hi( )
d.hi( ); // 但需要在a的定義裡頭
}
}
類別 b 的定義
class b {
public static void main (String args[ ]){
a s = new a();
//s.hi( );
// no method matching hi() found in class a
s.h();
a.h();
// cant make static reference to method void h( )
// if declared as : public void h( )
}
}
類別 c 的定義
class c extends a {
// void h( ) { };
// it is illegal to override a static method
void g( ) { System.out.println(y);
// System.out.println(x); // undefined variable x
};
}
// file saved as a.java, 用java b才有main執行
多層次的建構子 (multi-level constructors)
class x {
x( ) {System.out.println("from x !"); }
}
class y extends x {
y( ) {System.out.println("from y !"); }
}
class z extends y {
z( ) {System.out.println("from z !"); }
}
public class test {
public static void main(String [ ] args) {
z oz=new z();
}
}
5-4-3 類別的多重繼承
5-4-4 抽象類別 (Abstract class) 的觀念
基本觀念: 物件建立的時候會占用系統的資源,我們必須明確地定義出物件的內容,讓系統知道如何建立物件,可以透過類別中建構子 (constructor) 的定義來達成。 當物件不必再用到的時候,也要把資源還給系統,這一部分的處理可以在類別中解構子(destructor) 的定義裡描述。 不再被任何物件變數引用的物件,系統有垃圾回收 (garbage collection) 的機制。
MyFamily 類別的定義(I)
class MyFamily {
int NumOfChildren;
String NameOfSon;
String NameOfDaughter;
int CostPerChild;
MyFamily() {
this(2,"M","F");
}
MyFamily(String Son) {
this(2, Son,"F");
}
………
MyFamily 類別的定義(II)
………
MyFamily(int noc, String Son, String Daughter) {
NumOfChildren = noc;
NameOfSon = Son;
NameOfDaughter = Daughter;
CostPerChild = 5000;
}
public staticvoid main(String args[]) {
MyFamily m = new MyFamily();
System.out.println(m.NameOfSon);
MyFamily m1 = new MyFamily("Marty");
System.out.println(m8-NameOfSon);
System.out.println(m8-CostPerChild);
}
}
重點整理:
1. 類別視為模子,利用模子鑄造出物件,這即是類別與物件的關係
2. 物件導向是一種觀念,也是一種方法, Java即是利用物件導向的概念實行的方法