物件導向程式設計,我的思想[下]

javaprogramers發表於2005-04-21
2.3深入探討函式:
  
  2.3.1建構函式、預設建構函式、 預設建構函式
  


  對於上面的例項,它已經能完成絕大部分工作了,但它還是不完善的,還有許許多多的細節等到我們去完善!也許有的同學已經注意到了,當我建立完“jingwei”這個物件時,這個物件的所有的屬性都是空的,也就是說:這個物件的姓名是未定的、年齡是未定的、性別是未定的、薪水是未定的、午餐也是未定的。而我們想把這些屬性都新增上去,就還要用物件呼叫相應的方法,去一個個修改!天啊,這簡直是太麻煩了!有沒有什麼好方法能夠在我們建立物件的同時就完成了對屬性賦值的操作呢?哦不,應該說是對屬性的初始化呢?當然沒問題了,這就需要所謂的建構函式!
  
  建構函式是類中最特殊的函式,它與解構函式的功能正好相反!
  
  從特徵上來說:1.它是程式語言中唯一沒有返回值型別的函式。
  
  2.它的名稱與類的名稱必須要完全相同。
  
  3.它必須被宣告為公共(public)的型別
  
  4,可以對建構函式進行過載。
  
  5.它在建立物件是自動被呼叫。
  
  從功能上來說:1.它是對類中的屬性進行初始化。
  
  其實,對於上面的程式來說我們沒有自己定義建構函式。但是,在這種情況下,系統會自動為我們定義一個“預設建構函式”。他會把數值變數自動賦值為0,把布林行變數賦值為false等等(但在C++中,預設建構函式不初始化其成員)。如果程式設計師定義了建構函式,那麼系統就不會再為你的程式新增一個缺預設造函式了。(在這裡,我們提倡的是自己定義建構函式,而不是用系統的預設建構函式)
  
  還是看個例項吧!這樣比較清楚一些!




   //employee.java
  
  public class employee{
  
  private String name; //員工姓名
  
  private int age; //員工年齡
  
  private char sex; //員工性別
  
  private float emolument; //員工薪水
  
  private boolean lunch; //員工午餐
  
  //……等等
  
  public employee(){ //這個就是“預設”建構函式
  
  name = “jw”; //設定員工姓名
  
  age = 20; //設定員工年齡
  
  sex = “M”; //設定員工性別
  
  emolument = 100; //設定員工薪水
  
  lunch = false; //設定員工午餐
  
  }
  
  public void heater(){ //這個方法是用來加工員工的午餐
  
  lunch = true;
  
  }
  
  //……等等
  
  };
 
 這樣,在我們建立“jingwei”這個物件的同時,它的所有的屬性也被初始化了!顯然,這大大的提高了工作效率,但是,它還是不符合要求。想想看,如果我們現在建立這個型別的第二個物件的時候會發生什麼事情?告訴你,除了物件的“名”(這個名稱不在是物件屬性中的名稱,而是物件本身的名稱)不一樣外,其所有的“屬性值”都一樣!比如:現在我們建立第二個物件flashmagic,然而我會發現這個物件的所有的屬性和jingwei這個物件的所有的屬性完全相同。而我們只能在用物件的方法去改變著寫屬性了!很顯然,這種方法不大好!我們需要一種方法在建立物件的時候為物件的屬性賦予“我們想要的值”。
  
  相信你也看到了,預設建構函式就顯得無能為力了。我們需要的是帶引數的建構函式,在建立物件時,我們把引數傳給建構函式,這樣就能完成了上述的功能!口說無憑,還是來看個例項吧:
  


   //employee.java
  
  public class employee{
  
  private String name; //員工姓名
  
  private int age; //員工年齡
  
  private char sex; //員工性別
  
  private float emolument; //員工薪水
  
  private boolean lunch; //員工午餐
  
  //……等等
  
  public employee(String n,int a,char s,float e,boolean l){ //看這個建構函式
  
  name = n; //設定員工姓名
  
  age = a; //設定員工年齡
  
  sex = s; //設定員工性別
  
  emolument = e; //設定員工薪水
  
  lunch =l; //設定員工午餐
  
  }
  
  public void heater(){ //這個方法是用來加工員工的午餐
  
  lunch = true;
  
  }
  
  //……等等
  
  };
 


  
  這樣一來,在建立物件的同時我們就可以給他賦予我們想要的值,很顯然,這可就方便多了。哦,對了!還沒有告訴你怎麼建立呢!哈哈,往前翻幾頁你會看到這句話:
  
  jingwei = new employee();這是建立一個物件,而我們把它改成
  
  jingwei = new employee("jingwei",20,'M',100,false);這樣一來,所有的工作都完成了,呵呵!(在建立物件的同時賦予了我們想要的“初值”)
  
  2.3.2過載建構函式:
  
  我還是先把概念給你吧,讓你有個認識,隨後我們在進行論述。
  
  在JAVA中:
  
  1. 函式過載是一個類中宣告瞭多個同名的方法,但有不同的引數個數和引數型別。
  
  2. 函式重構是指在子類中宣告與父類同名的方法,從而覆蓋了父類的方法。重構解決了子類與父類的差異問題。(在討論到繼承時我會詳細說明)
  
  在C++中:
  
  1. 數過載的概念一樣。
  
  2. 重構的概念可就不一樣了,C++中功能更為龐大的虛擬函式。更詳細內容這裡就不錯過多介紹了!
其實關於過載的概念你並不陌生,在程式設計中相信你也接觸過。呵呵!讓我們來舉個操作符過載的例子你就會明白了,(JAVA中不支援這個功能)我們定義三個整數變數:
  
  int i1=2, i2=3,i3=0;
  
  i3 = i1 + i2; 
  
  此時i3=5;加號實現了兩個數相加的運算功能。然而我們現在要定義三個字串變數:
  
  String str1=”jing”, str2=”wei”,str3=””;
  
  str3 = str1 + str2;
  
  此時str3 = “jingwei”;加號實現了兩個字串相加的運算功能。同樣是加號,既可以把兩個整型的變數加在一起,也可以把兩個字串型別的變數加在一起。同一個操作符實現了不同的功能------這就是所謂的操作符過載(嘿嘿,我說你一定見過吧:)!不就好像是漢語中的一詞多意一樣!我需要說明一下的是,C++中的操作符過載可沒有這麼簡單。比如,我們可以對兩個自定義型別的物件進行相加的運算,進行賦值的運算。這樣書寫簡潔明瞭,而且非常實用。當然,關於操作符過載的話題太多了,有興趣再看看書吧!
  
  我們把操作符的話題在轉到函式上來,我們一直強調的是“物件調方法”------物件其實調的是方法的“名稱”。而我們現在要對方法進想過載,也就是定義多個相同名稱的函式,這樣計算機在呼叫的時候不會混淆嘛?我想應該不會的,呵呵,因為僅僅是函式名稱相同,而我們在呼叫函式時會把引數傳遞給他的。既是沒有引數也是一種引數傳遞引數的資訊(資訊為無引數)!然而由於引數型別、引數數量、返回值型別不同我們就可以對相同名稱的函式進行區分了!目的只有一個,用簡便的方法實現更多的功能。還是舉個例子吧,過載建構函式!
  

   public class employee{
  
  public employee(String n,int a,char s,float e,boolean l){ //看這個建構函式
  
  name = n; //設定員工姓名
  
  age = a; //設定員工年齡
  
  sex = s; //設定員工性別
  
  emolument = e; //設定員工薪水
  
  lunch =l; //設定員工午餐
  
  }
  
  public employee(){ //請注意這個函式沒有引數
  
  name = “jw”;
  
  age = 20;
  
  sex = ’W’;
  
  emolument = 99;
  
  lunch = true
  
  }
  
  //……等等
  
  };

 

  
  看,在一個類中有兩個名稱相同的函式,可我們在使用的時候系統如何知道我們呼叫的是那個版本的函式呢?呵呵,我剛剛說過了,可以通過函式的引數型別、引數數量、返回值型別來確定。現在我們接著試驗,我們建立兩個物件其中的一個呼叫帶引數的建構函式,第二個物件呼叫預設值的建構函式。我們來看看結果:
  
  jingwei = new employee("jingwei",20,'M',100,false);/*建立這個物件的時候呼叫的是帶引數的建構函式*/
  
  flashmagic = new employee();//建立這個物件是呼叫的是卻省值的建構函式
  
  而得到的結果呢?讓我們一起來看一看!
  
  Jingwei這個物件中: flashmagic這個物件中:
  

   name jingwei name jw
 


   age 20 age 20
  
  sex M sex W
  
  emolument 100 emolument 99
  
  lunch false lunch true
 


  
  看,雖然是兩個名稱完全相同的函式,但完成了不同的工作內容。呵呵!關於函式過載我們就料到這裡吧,我相信你已經有個大印象了,而更詳細的內容仍需要你的努力!
  
  過載普通的函式與過載建構函式大同小異,不過他多了一個this指標!this一般是對當前物件的引用。這麼說吧,如果涉及到兩個以上的物件時就會使用this指標。每個成員函式都有一個this指標,它是一個隱藏的引數,this指標只向呼叫它的物件!我說過方法只有一份,而物件都有自己的屬性,當物件呼叫方法來先是屬性的時候,他怎麼來判斷呼叫的時不是自己的屬性呢?這就需要this指標來大顯神威了。
  
  關於拷貝建構函式、行內函數、虛擬函式、模版等歐就不做過多的討論了,因為JAVA中好像沒有這些了。不過我需要提醒你一下的是,在C++中,類內定義的函式自動轉換為行內函數,而這好像與我前面提到的思想有衝突。因為行內函數的目的是減少函式呼叫的開銷!呵呵!我也沒繞出來呢!還請哪為大蝦指點一二!謝!
  
  2.3.3 初始化與賦值
  
  這裡我卻要提醒你一下的是,初始化與賦值是完全不同的兩個概念。建立一個類的時候會呼叫這個類的建構函式對物件的屬性進行初始化。而如果以後再把這個物件賦給其他同型別的物件時可就沒那麼簡單了。在JAVA中直接賦值就行了,因為JAVA中取消了指標,不存在指標的深拷貝與前拷貝問題。而在C++中就需要拷貝建構函式以及操作符過載了。因為JAVA中不牽扯這些東西,所以偶就不做過多介紹了。詳情請參閱相關書籍吧!
  
  2.3.4析夠函式:
  
  JAVA中不再支援指標了,所以你感覺不到它的重要性,因為系統會自動為你釋放記憶體。而在C++中一切都是手動的。在建構函式中new了一個指標,在析夠函式中就要delete這個指標。
  
  2.3.5靜態:
  
  現在我們再來看一看“靜態”是咋一回事兒!
  
  把一個變數或函式宣告為靜態的需要“static”這個關鍵字。宣告靜態的目的是“為某個類的所有物件的某個屬性或方法分配單一的儲存空間”。靜態的資料是屬於類的,不屬於任何的物件。靜態的資料在宣告的時候系統就為他分配了記憶體空間,而不用等到建立物件時。舉個例子來幫你更好的理解它吧。
    
  還是接著上面的例子。還記得剛剛我說過的員工能用微波爐熱飯的事情吧。現在我們要找一個手套,畢竟想把熱好的飯從微波爐裡拿出來直接下手是不行的。我把手套定義成一個布林型的變數,它有乾淨和髒兩種狀態。想想看手套是屬於誰的?所有物件?不對!因為只有方法才能屬於所有的物件。它是屬於類的,它像微波爐那個方法一樣,在記憶體中只有一份,所有的物件通過方法都能夠修改它。而下一次修改是基於上一次修改的基礎之上的!我的意思是:一個員工把手套弄髒了,下一個員工在使用的時候它還是髒的。而這個員工把手套洗乾淨之後,別人再用的時候它就是乾淨的了!就這麼點事兒,明白了吧!
  
  關於靜態函式我想就沒什麼可多說的了。給我的感覺就是,它也是屬於類的,在定義的時候就分配的記憶體。呼叫是可以使用類名直接呼叫。其他的和普通成員函式沒什麼不同的了不過這裡需要說明的一點是:在JAVA中,靜態的成員函式只能修改靜態的屬性,而靜態的屬性可以被所有的成員函式修改。不過在C++中就沒這麼多事兒了!
  
  2.4繼承
  
  繼承很好理解,它的最大好處就是“程式碼重用”,大大提高了工作效率。舉個例子你就明白了。世界上先有的黑白電視機,它有自己的工作原理。然而人們在他的基礎之上開發出了彩色電視機。彩色電視機繼承了黑白電視機的所有的特性與方法!因為它既能顯示彩色影像也能顯示黑白影像。然而它與黑白電視機又有許多區別,在工作原理上。彩色電視及多了矩陣色電路,把彩色訊號分離出三種顏色(RGB),他就能顯示彩色的影像了。而黑白電視機沒有這塊電路,即使它收到了彩色訊號也顯示不了彩色影像。彩色電視機是從黑白電視機中派生出來的。所以,黑白電視機是父類,彩色電視既是子類,彩色電視繼承了黑白電視機所有的特性與方法。看看再計算機中它是什麼樣子的吧:


   //BWtv.java 父類的定義
  
  public class BWtv{
  
  private int a;
  
  public BWtv(){
  
  a=1;
  
  }
 
 public changeBWtv(int i){
  
  a=i;
  
  }
  
  }
  
  //Ctv.java 子類的定義
  
  class Ctv exntends BWtv{ //注意關鍵字“extends”
  
  private int b;
      
  public Ctv(){
    
  b=2;
  
  }
  
  public changetCv(int x){
  
  b = x;
  
  }
  
  }
 


  
  有了上面的定義,我們來看看他們都有什麼資料。
  
  BWtv的資料包括     Ctv的資料包括
  
  private int a     private int a
  
             private int b
  
  public changeBWtv(); public changeBWtv()
  
  public changeCtv();
  
  你看,子類擁有父類的所有的方法及屬性。注意關鍵字”extends”,它的意思是繼承。在C++中使用的是“:”操作符。意思是一樣的。但是這裡有許多問題,首先是訪問許可權的問題,子類的物件擁有父類的所有的屬性和方法這句話。對嘛?肯定是對的!(不過JAVA的書中可不是這麼說的,他說只繼承非private型別的屬性及方法,我覺得它這句話有錯誤!)可是,子類的物件不能直接訪問父類的私有屬性或方法,它只能通過父類的公有成員函式來訪問。而此時,如果你修改了父類的屬性的值。那就真的修改了。我的意思是:父類的私有屬性的值會隨著子類物件呼叫父類的公有方法進行對相應屬性的修改而發生變化!(這裡面存在一個域的問題,所有的修改都是在子類中進行的,修改的是子類繼承的父類的屬性(在子類這個域中,此時父類以拷貝到子類中了。)。而程式中定義的父類的屬性不會發生任何變化(在父類的域中),)
  
  其次是建構函式,在建立一個子類物件時首先要呼叫的是父類的建構函式,然後再呼叫子類的建構函式,畢竟,子類的建構函式不包括父類的屬性的初始化功能!(從這一點來說我的觀點又是正確的“子類的物件擁有父類的所有的屬性和方法”)當然了,析夠函式的呼叫順序正好相反!
  
  現在讓我們來談談protected這個關鍵字吧,它的意思是:對物件來說,宣告為protected的變數是私有的,而對子類父類來說,宣告為protected的變數是公共的。
  
  現在又出現了這樣的一個問題,如果我們在子類中也定義了一個int 型別的變數a,那我們在建立子類的物件的時候呼叫的是子類定義的還是父類定義的呢?這就涉及到資料的隱藏的問題了,我可以告訴你肯定是呼叫的子類的變數a。因為,子類把父類的這個同名變數給隱藏了。而如果是方法呢?這就涉及到重構的問題了,在上面我提到過“函式重構是指在子類中宣告與父類同名的方法,從而覆蓋了父類的方法。重構解決了子類與父類的差異問題。”這裡必須要宣告一下的是,在JAVA中,子類出現了對父類屬性的隱藏和父類方法的覆蓋後,在子類中,子類物件僅能呼叫子類本身的屬性和方法。要呼叫父類的屬性和方法必須要實用super這個關鍵子。而在C++中就不這樣了。因為它有虛擬函式

 虛擬函式在C++中非常好玩的事。我們可以把需要改寫的函式宣告為虛擬函式,用virtual這個關鍵字來宣告。這樣。假如如果我們CwinApp這麼一個基類,它裡面定義了一個成員(虛)函式為InitInstance()和另一個為(虛)函式InitApplication()。如果我從CWinApp派生一個子類為CMyWinApp並修改了InitInstance()這個成員函式。我們並沒有修改InitApplication()這個成員函式。現在我們建立CMyWinApp這個類的函式theApp,我們並建立一個指標*pApp指向這個物件theApp。此時:
  
  pApp->InitInstance() //指標呼叫的是子類CMyWinApp的虛方法
  
  pApp->InitApplication() //指標呼叫的時父類CwinApp的虛方法
  
  因為子類並沒有修改父類的方法,所以呼叫的是父類的虛方法。這就牽扯到虛你表的問題。礙與本篇文章的定位,這裡就不討論了!
  
    關於父類與子類的物件的型別轉換問題是這樣的,子類物件轉換為父類物件時,不會出現錯誤。因為子類包含父類的所有的屬性及方法,而父類向子類轉換時就難說了,呵呵。這還會牽扯到虛擬表的問題,也不討論了!
  
  JAVA中不再支援多重繼承,也就是一個類從兩個以上的類中繼承而來,但它卻多了介面的概念“interface”。這裡就不做過多介紹了!
  
  關於抽象基類也沒什麼難的!他的一個大概念就是:做為許多類的父類,不定義物件,只做派生用!
  
我能做得也只有這些了,如果你能明白以上的六七成,那就是對我最大的回報了,呵呵!就像剛剛開始我說的,我只是給你一個大概的思想,至於內部的實現細節,仍需要你的繼續努力。關於程式語言的內容還有許多許多,實屬小生個人能力有限而不能全盤照顧到。不過作為一個初學者的你來說,這些東西都是基本的。需要我提醒你一點的是,不要指望在第一、二遍的時候看懂什麼!加油:)
    

相關文章