來源:http://justin-x.cn/2016/09/22/my-opion-of-OOP/


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

面向對象

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

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

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

爲什麼說面向對象更貼近實際生活

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

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

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

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

  • 抽象
  • 封裝
  • 繼承
  • 多態

與早期結構化編程相比

早期結構化編程是面向過程的(功能),換句話說程序是由功能的集合組成,而調用者是作爲功能的參數傳入的。而在面向對象的程序中,對象是主體,程序是由對象的集合組成。一個對象中包含一系列符合設計的功能供其他對象調用。這麼說可能還是比較抽象~

談談我對面向對象以及類與對象的理解

例如:當我們設計一個五子棋遊戲時

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

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 playerList=new ArrayList();
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關鍵字聲明的。 再例如,《紅色警戒》中美國大兵是一類兵種,點擊製造後從兵營裏出來的那個會開槍的傢伙就是對象了:

談談我對面向對象以及類與對象的理解


類的定義就是一個模板,它描述的一類對象的屬性與行爲。類往往是抽象的、沒有實體的。哺乳動物是類的概念,是抽象的,現實中沒有哺乳動物這一實體,只有具體的如老虎,獅子等。編程工作中套用這一思維模式,我們將程序中的實例抽象爲類,例如一個系統中的用戶有張三、李四我們會把他們抽象爲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;


內存中的狀態大致如下:

談談我對面向對象以及類與對象的理解


int a = 1 是直接在棧中開闢空間,而對於未進行實例化的Person p因爲沒有有效的內存地址引用它的值是null。而當代碼進行如下修改時:

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


內存中的狀態大致如下:

談談我對面向對象以及類與對象的理解

Person p=new Person();使得p的值=0x8da23也就是對象new Person();在堆中的地址。所以,到這裏後就不難理解之前說的對象的引用了,所謂引用其實就是堆內存地址的引用。

總結

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

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

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

相关文章