談談我對面向?qū)ο笠约邦惻c對象的理解
文章最初發(fā)表于我的個人博客非典型性程序猿
對于剛接觸JAVA或者其他面向?qū)ο缶幊陶Z言的朋友們來說,可能一開始都很難理解面向?qū)ο蟮母拍钜约邦惡蛯ο蟮年P系。筆者曾經(jīng)帶過一個短期培訓班教授java入門基礎,在最后結束課程的時候,還有很多同學不太理解面向?qū)ο蟮乃季S以及類與對象的意義。這幾天有空,就想著整理整理自己的思路,談談自己對面向?qū)ο笠约邦惻c對象的理解。
面向?qū)ο?/strong>
首先,一言不和先百度,得到如下定義:
一切事物皆對象,通過面向?qū)ο蟮姆绞?,將現(xiàn)實世界的事物抽象成對象,現(xiàn)實世界中的關系抽象成類、繼承,幫助人們實現(xiàn)對現(xiàn)實世界的抽象與數(shù)字建模。
我們知道,編寫程序的目的是為了解決現(xiàn)實生活中的問題,編程的思維方式也應該貼近現(xiàn)實生活的思維方式。面向?qū)ο蟮木幊谭绞骄褪菫榱藢崿F(xiàn)上述目的二出現(xiàn)的。它使得編程工作更直觀,更易理解。需要注意的是這里說的編程不光是coding還包括了設計的過程也是面向?qū)ο蟮?/p>
為什么說面向?qū)ο蟾N近實際生活
想象一下,當我們向別人描述一樣事物時,我們都是怎么說的?"它有像鴨子一樣的嘴巴","它有4條退","爪子里還有蹼","它是哺乳動物但卻是卵生"。
這種HAS A 和 IS A的表達方式往往可以簡單而高效的描述一樣事物。HAS A描述事物的屬性或行為,IS A 則說明了事物的類屬。
當我們把這一系列的屬性組合起來便得到的鴨嘴獸這一類,同時哺乳動物一詞簡單精煉的表面了所有哺乳動物的特性而不用一一列出,這是繼承特性的體現(xiàn),同時卵生又是多態(tài)的體現(xiàn)。
這就是面向?qū)ο蟮乃季S特點,抽取(抽象)有用的屬性和行為(拋棄哪些無需關系的)組織(封裝)成一個類。這個過程中你也許會發(fā)現(xiàn)很多屬性或方法是和另一個類相同的,那么你就可以采用繼承的方式避免重復(當然這個過程也有可能是,當你設計完一個個類后,才發(fā)現(xiàn)他們有共同點,然后再抽取出基類)。更重要的是,繼承是可以不原樣照搬的,我們可以通過重載實現(xiàn)相同行為或?qū)傩缘奶赜袑崿F(xiàn)方式,這種特點稱之為多態(tài),例如同樣的生產(chǎn)行為,實現(xiàn)方式由胎生變?yōu)槁焉?。請大聲念出,并牢牢記住面向?qū)ο蟮乃膫€特征:
- 抽象
- 封裝
- 繼承
- 多態(tài)
與早期結構化編程相比
早期結構化編程是面向過程的(功能),換句話說程序是由功能的集合組成,而調(diào)用者是作為功能的參數(shù)傳入的。而在面向?qū)ο蟮某绦蛑?,對象是主體,程序是由對象的集合組成。一個對象中包含一系列符合設計的功能供其他對象調(diào)用。這么說可能還是比較抽象,
例如當我們設計一個五子棋游戲時,面向過程的設計思路就是首先分析問題的步驟:
1、開始游戲,2、黑子先走,3、繪制畫面,4、判斷輸贏,5、輪到白子,6、繪制畫面,7、判斷輸贏,8、返回步驟2,9、輸出最后結果。
把上面每個步驟用分別的函數(shù)來實現(xiàn),問題就解決了。
而面向?qū)ο蟮脑O計則是從另外的思路來解決問題。整個五子棋可以分為:
1、黑白雙方,這兩方的行為是一模一樣的,2、棋盤系統(tǒng),負責繪制畫面,3、規(guī)則系統(tǒng),負責判定諸如犯規(guī)、輸贏等。
第一類對象(玩家對象)負責接受用戶輸入,并告知第二類對象(棋盤對象)棋子布局的變化,棋盤對象接收到了棋子的變化就要負責在屏幕上面顯示出這種變化,同時利用第三類對象(規(guī)則系統(tǒng))來對棋局進行判定。
(以上例子來自國內(nèi)著名問答社區(qū))
隨便寫點代碼,大家看看就好,不要太認真....
- /**
- 玩家類
- **/
- 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){
- //重新繪制棋盤
- ......
- //調(diào)用裁判對象,判斷結果
- if(referee.isEnd){
- End();
- }
- }
- public void End(){
- .......
- }
- }
- /**
- 裁判類
- **/
- public class Referee(){
- Table table;
- public Referee(Table table){
- this.table=table;
- }
- public boolen isEnd(){
- //判斷輸贏
- ....
- return false;
- }
- }
然而事實上,通過上述示例代碼,我們不難發(fā)現(xiàn),即使我們使用面向?qū)ο蟮姆绞剑厦胬永锩嫦蜻^程中提到的幾個下棋過程我們還是都實現(xiàn)了的,只不過程被封裝到了類的方法中。所以說其實面向?qū)ο蠛兔嫦蜻^程并不是編程的區(qū)別(需要實現(xiàn)的業(yè)務邏輯的量不會產(chǎn)生變化),而是設計的區(qū)別!
類與對象
類是抽象的,而對象是具體的
如何理解上面的話呢? 例如鴨嘴獸是類型,具體的鴨嘴獸A、鴨嘴獸B就是對象了。在JAVA中對象是通過new關鍵字聲明的。 再例如,《紅色警戒》中美國大兵是一類兵種,點擊制造后從兵營里出來的那個會開槍的家伙就是對象了:
類的定義就是一個模板,它描述的一類對象的屬性與行為。類往往是抽象的、沒有實體的。哺乳動物是類的概念,是抽象的,現(xiàn)實中沒有哺乳動物這一實體,只有具體的如老虎,獅子等。編程工作中套用這一思維模式,我們將程序中的實例抽象為類,例如一個系統(tǒng)中的用戶有張三、李四我們會把他們抽象為Person類,或者稱之為一個名為Person的數(shù)據(jù)類型。
對象則是根據(jù)所屬類模板創(chuàng)造出來的實實在在的事物。在程序中我將這個實實在在的事物稱之為實例,我們?yōu)樗膶傩再x上特定的值,讓它成為張三或者李四。在內(nèi)存里來說,對象是表示的就是具體數(shù)據(jù)。
前面說的都是概念性的東西,下面我們說說實際的運用過程中的理解。
從數(shù)據(jù)類型來說
以java為例,數(shù)據(jù)類型分為基本數(shù)據(jù)類型和引用數(shù)據(jù)類型。
基本數(shù)據(jù)類型就是byte,short,int,long,double,char,boolean;其它的,需要用到new關鍵字來賦值的都是引用數(shù)據(jù)類型。 類與對象指的便是引用數(shù)據(jù)的類型與其值(這里指的類不光是class,還包括接口、數(shù)組、枚舉、注解)。 而引用指的是內(nèi)存地址的引用,關于這點在后面說的內(nèi)存時會細說。
看下面的代碼:
- int a =1;
- Person b=new Person();
a 和 b 都是本身無意義的變量名。需要關注的是:a的類型是基本數(shù)據(jù)類型int值為1,而b的類型是Person屬于引用類型,其引用的是new Person()這個對象。我們往往會說對象xx,比如這里的對象b。但實際上b只是對象的引用,真正的對象是后面的new Person()!
需要注意的是String也是引用數(shù)據(jù)類型,只不過因為使用率非常高,所以對于String,jvm支持其可 以像基本數(shù)據(jù)類型一樣使用:String a = "abc"; 同等于 String a = new String("abc");
總之呢,簡單來說類指的的引用數(shù)據(jù)的類型,對象是具體賦的值。為了更深入理解,我們下面需要解釋下這個引用是如何體現(xiàn)的。
什么是引用(從內(nèi)存來說)
要深入理解什么是類,什么是對象,什么又是引用,就離不開說說java的內(nèi)存使用方式。
在java中內(nèi)存被大致劃分為棧(stack)與堆(heap) (之所以是大致,是因為還包括其它幾部分就不在這細說)。
關于什么是棧與堆在這就不細說,有空我再整理一篇文章詳細說明。
在這里我們只說一點:java中,基本數(shù)據(jù)類型以及對象的引用都保存在棧(stack),而對象則保存在堆(heap)中,例如當如下代碼:
- int a=1;
- Person p;
內(nèi)存中的狀態(tài)大致如下:
int a = 1 是直接在棧中開辟空間,而對于未進行實例化的Person p因為沒有有效的內(nèi)存地址引用它的值是null。而當代碼進行如下修改時:
- int a =1 ;
- Person p = new Person();
內(nèi)存中的狀態(tài)大致如下:
Person p=new Person();使得p的值=0x8da23也就是對象new Person();在堆中的地址。所以,到這里后就不難理解之前說的對象的引用了,所謂引用其實就是堆內(nèi)存地址的引用。
總結
隨著計算機技術的不斷提高,現(xiàn)在計算機不單單是用來解決運算問題,而是被用于解決越來越貼近現(xiàn)實生活的復雜問題。面向?qū)ο缶褪沁@一發(fā)展進程的產(chǎn)物,它使得編程工作更貼近人的思維方式,從而大大提升編程效率。我們必須明白的是面向?qū)ο蟛⒉皇且环N編程方式,而是一種編程思維方式,這種思維方式涵蓋了分析,設計,編碼等。在面向?qū)ο缶幊讨校绦虻幕締卧菍ο?,?shù)據(jù)封裝在對象中。類是對象模板,是預定義好的結構,所謂的實例化一個類,所指的就是將數(shù)據(jù)填入模板。
最后,本人文筆不是很好,有待提高。寫文章和博客的最大目的是梳理自己的思路,其二是分享自己的想法,望大家多多吐槽,愿共同提高。