談談我對物件導向以及類與物件的理解

Java團長_發表於2018-11-24

640

來源:http://justin-x.cn/


對於剛接觸JAVA或者其他物件導向程式語言的朋友們來說,可能一開始都很難理解物件導向的概念以及物件的關係。筆者曾經帶過一個短期培訓班教授java入門基礎,在最後結束課程的時候,還有很多同學不太理解物件導向的思維以及物件的意義。這幾天有空,就想著整理整理自己的思路,談談自己對物件導向以及類與物件的理解。

物件導向

首先,一言不和先百度,得到如下定義:

一切事物皆物件,通過物件導向的方式,將現實世界的事物抽象成物件,現實世界中的關係抽象成類、繼承,幫助人們實現對現實世界的抽象與數字建模。

我們知道,編寫程式的目的是為了解決現實生活中的問題,程式設計的思維方式也應該貼近現實生活的思維方式。物件導向的程式設計方式就是為了實現上述目的二出現的。它使得程式設計工作更直觀,更易理解。需要注意的是這裡說的程式設計不光是coding還包括了設計的過程也是物件導向的。

為什麼說物件導向更貼近實際生活

想象一下,當我們向別人描述一樣事物時,我們都是怎麼說的?”它有像鴨子一樣的嘴巴”,”它有4條退”,”爪子裡還有蹼”,”它是哺乳動物但卻是卵生”。


這種HAS A 和 IS A的表達方式往往可以簡單而高效的描述一樣事物。HAS A描述事物的屬性或行為,IS A 則說明了事物的類屬。


當我們把這一系列的屬性組合起來便得到的鴨嘴獸這一,同時哺乳動物一詞簡單精煉的表面了所有哺乳動物的特性而不用一一列出,這是繼承特性的體現,同時卵生又是多型的體現。


這就是物件導向的思維特點,抽取(抽象)有用的屬性和行為(拋棄哪些無需關係的)組織(封裝)成一個類。這個過程中你也許會發現很多屬性或方法是和另一個類相同的,那麼你就可以採用繼承的方式避免重複(當然這個過程也有可能是,當你設計完一個個類後,才發現他們有共同點,然後再抽取出基類)。更重要的是,繼承是可以不原樣照搬的,我們可以通過過載實現相同行為或屬性的特有實現方式,這種特點稱之為多型,例如同樣的生產行為,實現方式由胎生變為卵生。請大聲念出,並牢牢記住物件導向的四個特徵:


  • 抽象

  • 封裝

  • 繼承

  • 多型

與早期結構化程式設計相比

早期結構化程式設計是程式導向的(功能),換句話說程式是由功能的集合組成,而呼叫者是作為功能的引數傳入的。而在物件導向的程式中,物件是主體,程式是由物件的集合組成。一個物件中包含一系列符合設計的功能供其他物件呼叫。這麼說可能還是比較抽象~
640例如:當我們設計一個五子棋遊戲時


程式導向的設計思路就是首先分析問題的步驟:

1、開始遊戲,2、黑子先走,3、繪製畫面,4、判斷輸贏,5、輪到白子,6、繪製畫面,7、判斷輸贏,8、返回步驟2,9、輸出最後結果。


把上面每個步驟用分別的函式來實現,問題就解決了。


而物件導向的設計則是從另外的思路來解決問題。


整個五子棋可以分為:

1、黑白雙方,這兩方的行為是一模一樣的,2、棋盤系統,負責繪製畫面,3、規則系統,負責判定諸如犯規、輸贏等。


第一類物件(玩家物件)負責接受使用者輸入,並告知第二類物件(棋盤物件)棋子佈局的變化,棋盤物件接收到了棋子的變化就要負責在螢幕上面顯示出這種變化,同時利用第三類物件(規則系統)來對棋局進行判定。(以上例子來自國內著名問答社群)



隨便寫點程式碼,大家看看就好,不要太認真…


/**
玩家類
**/

public class Player {
   String name;       //棋手名稱
   boolean isFirst;  //是否先手
   int color_flag;  //代表顏色  0-白 1-黑
   Table table;//棋盤物件
   
 public Player(String name,boolean isFirst;int color_flag){
          this.name=name;
          this.isFirst=isFirst;
          this.color_flag=color_flag;
   }
 
 /**
 下棋,x,y為落子座標
 **/

 public void play(int x,int y) throws Exception{
     if(this.table==null){
        throw new IllegalArgumentException("玩家還未註冊到棋盤!");
     }
   table.setNewPieces(x,y);
 }
   
   public void setTable(Table table){
      this.table=table;
   }
}
/**
棋盤類
**/

public class Table{
 List<Player> playerList=new ArrayList<Player>();
 Referee referee ;
 public Table(){
  referee =new Referee(this);
 }
 /**
   註冊玩家
 **/

 public void registPlayer(Player player) throws Exception {
     //檢測棋盤中的玩家是否已滿,先手玩家和玩家選色是否衝突。
     .......
    playerList.add(player);
    player.setTable(this);
 }

  /**
   落子
  **/


  public void setNewPieces(int x , int y){
         //重新繪製棋盤
         ......
        //呼叫裁判物件,判斷結果
     if(referee.isEnd){
         End();
       }
  }

  public void End(){
     .......
 }
}
/**
裁判類
**/

public class Referee(){
 Table table;
 public Referee(Table table){
   this.table=table;
}

  public boolen isEnd(){
     //判斷輸贏
     ....
   return false;
  }
}


然而事實上,通過上述示例程式碼,我們不難發現,即使我們使用物件導向的方式,上面例子裡程式導向中提到的幾個下棋過程我們還是都實現了的,只不過程被封裝到了類的方法中。所以說其實物件導向和麵向過程並不是程式設計的區別(需要實現的業務邏輯的量不會產生變化),而是設計的區別

類與物件

類是抽象的,而物件是具體的

如何理解上面的話呢? 例如鴨嘴獸是型別,具體的鴨嘴獸A、鴨嘴獸B就是物件了。在JAVA中物件是通過new關鍵字宣告的。 再例如,《紅色警戒》中美國大兵是一兵種,點選製造後從兵營裡出來的那個會開槍的傢伙就是物件了:

640

的定義就是一個模板,它描述的一類物件的屬性與行為。類往往是抽象的、沒有實體的。哺乳動物是的概念,是抽象的,現實中沒有哺乳動物這一實體,只有具體的如老虎,獅子等。程式設計工作中套用這一思維模式,我們將程式中的例項抽象為類,例如一個系統中的使用者有張三、李四我們會把他們抽象為Person類,或者稱之為一個名為Person的資料型別。


物件則是根據所屬模板創造出來的實實在在的事物。在程式中我將這個實實在在的事物稱之為例項,我們為它的屬性賦上特定的值,讓它成為張三或者李四。在記憶體裡來說,物件是表示的就是具體資料。


前面說的都是概念性的東西,下面我們說說實際的運用過程中的理解。

從資料型別來說

以java為例,資料型別分為基本資料型別引用資料型別


基本資料型別就是byte,short,int,long,double,char,boolean;其它的,需要用到new關鍵字來賦值的都是引用資料型別。 類與物件指的便是引用資料的型別與其值(這裡指的類不光是class,還包括介面、陣列、列舉、註解)。 而引用指的是記憶體地址的引用,關於這點在後面說的記憶體時會細說。

看下面的程式碼:


int a =1; 
Person b=new Person();


a 和 b 都是本身無意義的變數名。需要關注的是:a的型別是基本資料型別int值為1,而b的型別是Person屬於引用型別,其引用的是new Person()這個物件。我們往往會說物件xx,比如這裡的物件b。但實際上b只是物件的引用,真正的物件是後面的new Person()

需要注意的是String也是引用資料型別,只不過因為使用率非常高,所以對於String,jvm支援其可 以像基本資料型別一樣使用:String a = “abc”; 同等於 String a = new String(“abc”);

總之呢,簡單來說指的的引用資料的型別,物件是具體賦的值。為了更深入理解,我們下面需要解釋下這個引用是如何體現的。

什麼是引用(從記憶體來說)

要深入理解什麼是類,什麼是物件,什麼又是引用,就離不開說說java的記憶體使用方式。


在java中記憶體被大致劃分為棧(stack)堆(heap) (之所以是大致,是因為還包括其它幾部分就不在這細說)。


關於什麼是棧與堆在這就不細說,有空我再整理一篇文章詳細說明。


在這裡我們只說一點:java中,基本資料型別以及物件的引用都儲存在棧(stack),而物件則儲存在堆(heap)中,例如當如下程式碼:


int a=1;
Person p;


記憶體中的狀態大致如下:
640

int a = 1 是直接在棧中開闢空間,而對於未進行例項化的Person p因為沒有有效的記憶體地址引用它的值是null。而當程式碼進行如下修改時:


int a =1 ;
Person p = new Person();


記憶體中的狀態大致如下:
640Person p=new Person();使得p的值=0x8da23也就是物件new Person();中的地址。所以,到這裡後就不難理解之前說的物件的引用了,所謂引用其實就是堆記憶體地址的引用。

總結

隨著計算機技術的不斷提高,現在計算機不單單是用來解決運算問題,而是被用於解決越來越貼近現實生活的複雜問題。物件導向就是這一發展程式的產物,它使得程式設計工作更貼近人的思維方式,從而大大提升程式設計效率。


我們必須明白的是物件導向並不是一種程式設計方式,而是一種程式設計思維方式,這種思維方式涵蓋了分析,設計,編碼等。在物件導向程式設計中,程式的基本單元是物件,資料封裝在物件中。是物件模板,是預定義好的結構,所謂的例項化一個類,所指的就是將資料填入模板。


最後,本人文筆不是很好,有待提高。寫文章和部落格的最大目的是梳理自己的思路,其二是分享自己的想法,望大家多多吐槽,願共同提高。


PS:如果覺得我的分享不錯,歡迎大家隨手點贊、轉發。

(完)

640?

Java團長

專注於Java乾貨分享

640

掃描上方二維碼獲取更多Java乾貨

相關文章