剛接觸JAVA或者其他面向?qū)ο蟮乃季S以及類與對(duì)象
2021-06-08
對(duì)于剛接觸JAVA或其他面向?qū)ο缶幊陶Z言的朋友來說,一開始可能很難理解面向?qū)ο蟮母拍钜约邦惻c對(duì)象的關(guān)系。筆者曾經(jīng)參加過一次短期培訓(xùn)班,講授java基礎(chǔ)知識(shí)。在課程結(jié)束時(shí),許多學(xué)生沒有理解面向?qū)ο蟮乃枷胍约邦惡蛯?duì)象的含義。這幾天有空,想整理一下思路,談?wù)勎覍?duì)面向?qū)ο蠛皖惡蛯?duì)象的理解。
面向?qū)ο?/p>
首先百度會(huì)得到如下定義:
一切都是對(duì)象。通過面向?qū)ο蟮姆椒?,將現(xiàn)實(shí)世界中的事物抽象為對(duì)象,將現(xiàn)實(shí)世界中的關(guān)系抽象為類并繼承,幫助人們實(shí)現(xiàn)對(duì)現(xiàn)實(shí)世界的抽象化和數(shù)字化建模。
我們知道編寫程序的目的是解決現(xiàn)實(shí)生活中的問題,編程中的思維方式也應(yīng)該接近現(xiàn)實(shí)生活中的思維方式。面向?qū)ο缶幊叹褪菫榱诉_(dá)到以上兩個(gè)目的。它使編程工作更加直觀和易于理解。需要注意的是,這里提到的編程不僅包括設(shè)計(jì)過程,還包括面向?qū)ο?/p>
為什么說面向?qū)ο蟾咏F(xiàn)實(shí)生活?
想象一下,當(dāng)我們向別人描述某事時(shí),我們都會(huì)說什么? “它的嘴像鴨子”,“它有四個(gè)退縮”,“它的爪子上有網(wǎng)”,“它是哺乳動(dòng)物,但它是卵生的?!?/p>
HAS A 和 IS A 的這種表達(dá)方式往往可以簡(jiǎn)單有效地描述同一事物。 HAS A描述事物的屬性或行為,IS A描述事物的類別。
當(dāng)我們結(jié)合這一系列屬性時(shí),我們得到鴨嘴獸的類別。同時(shí),哺乳動(dòng)物這個(gè)詞簡(jiǎn)潔明了地表達(dá)了所有哺乳動(dòng)物的特征,而不是一一列舉。這是繼承特性的體現(xiàn)。同時(shí),卵子發(fā)育是多態(tài)性的一種表現(xiàn)。
這是面向?qū)ο笏季S的特點(diǎn),它提取(抽象)有用的屬性和行為(丟棄那些不需要關(guān)系的)并組織(封裝)成一個(gè)類。在這個(gè)過程中,你可能會(huì)發(fā)現(xiàn)很多屬性或者方法和另一個(gè)類是一樣的,那么你可以使用繼承來避免重復(fù)(當(dāng)然這個(gè)過程也可能是你設(shè)計(jì)了每個(gè)類之后,發(fā)現(xiàn)它們有共同點(diǎn),然后提取基類)。更重要的是,繼承不能照原樣復(fù)制。我們可以通過重載來實(shí)現(xiàn)相同行為或?qū)傩缘奈ㄒ粚?shí)現(xiàn)。此功能稱為多態(tài)性。比如同樣的生產(chǎn)行為,實(shí)現(xiàn)方式從胎生變?yōu)槁焉?。?qǐng)大聲朗讀并牢記面向?qū)ο蟮乃膫€(gè)特點(diǎn):
抽象、封裝、繼承、多態(tài),與早期結(jié)構(gòu)化編程相比
早期結(jié)構(gòu)化編程是面向過程的(功能)。換句話說,程序由一組函數(shù)組成數(shù)據(jù)結(jié)構(gòu)用面向?qū)ο蠓椒ㄅcc++描述,調(diào)用者作為函數(shù)的參數(shù)傳入。在面向?qū)ο蟮某绦蛑?,?duì)象是主體,程序由對(duì)象的集合組成。一個(gè)對(duì)象包含一系列符合設(shè)計(jì)的函數(shù),供其他對(duì)象調(diào)用。所以它可能很抽象,
比如我們?cè)谠O(shè)計(jì)五子棋游戲的時(shí)候,流程化的設(shè)計(jì)思路就是先分析問題:1、開始游戲,2、黑子先走,3、畫圖,4、判斷輸贏,5、轉(zhuǎn)白子,6、畫屏,7、判斷勝負(fù),8、返回第2步,9、輸出最終結(jié)果。用單獨(dú)的函數(shù)分別執(zhí)行以上步驟,問題就解決了。
面向?qū)ο蟮脑O(shè)計(jì)是從另一種思維方式解決問題。整個(gè)五子棋可以分為:1、黑白,兩方的行為完全一樣,2、棋盤系統(tǒng),負(fù)責(zé)畫屏,3、rule系統(tǒng),負(fù)責(zé)判斷犯規(guī),獲勝或輸?shù)取5谝活悓?duì)象(玩家對(duì)象)負(fù)責(zé)接受用戶輸入并通知第二類對(duì)象(棋盤對(duì)象)棋子布局的變化。棋盤對(duì)象負(fù)責(zé)在接收到棋子的變化后在屏幕上顯示變化,同時(shí)使用前三種對(duì)象(規(guī)則系統(tǒng))來確定對(duì)局。 (以上例子來自國(guó)內(nèi)某知名問答社區(qū))
隨便寫點(diǎn)代碼,看看就行,別太認(rèn)真...
/** 玩家類 **/ public class Player { String name; //棋手名稱 boolean isFirst; //是否先手 int color_flag; //代表顏色 0-白 1-黑 Table table;//棋盤對(duì)象 public Player(String name,boolean isFirst;int color_flag){ this.name=name; this.isFirst=isFirst; this.color_flag=color_flag; } /** 下棋,x,y為落子坐標(biāo) **/ public void play(int x,int y) throws Exception{ if(this.table==null){ throw new IllegalArgumentException("玩家還未注冊(cè)到棋盤!"); } 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); } /** 注冊(cè)玩家 **/ public void registPlayer(Player player) throws Exception { //檢測(cè)棋盤中的玩家是否已滿,先手玩家和玩家選色是否沖突。 ....... playerList.add(player); player.setTable(this); } /** 落子 **/ public void setNewPieces(int x , int y){ //重新繪制棋盤 ...... //調(diào)用裁判對(duì)象,判斷結(jié)果 if(referee.isEnd){ End(); } } public void End(){ ....... } } /** 裁判類 **/ public class Referee(){ Table table; public Referee(Table table){ this.table=table; } public boolen isEnd(){ //判斷輸贏 .... return false; } }
但是,其實(shí)通過上面的例子代碼,不難發(fā)現(xiàn),即使我們使用面向?qū)ο蟮姆绞?,也已?jīng)實(shí)現(xiàn)了上面例子中提到的幾個(gè)象棋過程,只不過過程是封裝的進(jìn)入類的方法。所以其實(shí)面向?qū)ο蠛兔嫦蜻^程不是編程的區(qū)別(需要實(shí)現(xiàn)的業(yè)務(wù)邏輯量不會(huì)變),而是設(shè)計(jì)的區(qū)別!
類和對(duì)象
類是抽象的,而對(duì)象是具體的
如何理解上面的單詞?例如,鴨嘴獸是一種類型,特定的鴨嘴獸 A 和鴨嘴獸 B 是對(duì)象。在 JAVA 中,對(duì)象是通過 new 關(guān)鍵字聲明的。再比如,《紅色警戒》中的美軍士兵是一種單位,點(diǎn)擊后從軍營(yíng)中射出的那個(gè)就是目標(biāo):
類的定義是一個(gè)模板,它描述了一類對(duì)象的屬性和行為。類通常是抽象的,沒有實(shí)體。哺乳動(dòng)物是屬的概念,是抽象的。實(shí)際上,沒有哺乳動(dòng)物這樣的實(shí)體。只有具體的東西,比如老虎和獅子。在編程工作中應(yīng)用這種思維方式,我們將程序中的實(shí)例抽象為類。例如,如果一個(gè)系統(tǒng)中的用戶有三、李四,我們將它們抽象為類,或者稱它們?yōu)槊麛?shù)據(jù)類型。
對(duì)象是基于它們所屬的類模板創(chuàng)建的真實(shí)事物。在程序中,我把這個(gè)有形的東西稱為實(shí)例。我們給它的屬性賦予特定的值,讓它成為張三或李斯。在內(nèi)存中,對(duì)象代表特定的數(shù)據(jù)。
我上面說的都是概念性的東西,下面說說實(shí)際應(yīng)用過程中的理解。
在數(shù)據(jù)類型方面
以java為例,數(shù)據(jù)類型分為基本數(shù)據(jù)類型和引用數(shù)據(jù)類型。
基本數(shù)據(jù)類型為byte,,,int,long,,char,;其他需要使用new關(guān)鍵字賦值的都是引用數(shù)據(jù)類型。類和對(duì)象指的是被引用數(shù)據(jù)的類型和值(這里指的類不僅包括接口、數(shù)組、枚舉和注解)。引用是指對(duì)內(nèi)存地址的引用,后面講內(nèi)存的時(shí)候會(huì)詳細(xì)討論這個(gè)??聪旅娴拇a:
int a =1; Person b=new Person();
a 和 b 都是無意義的變量名。需要注意的是:a的類型是基本數(shù)據(jù)類型int 為1,b的類型是引用類型,指的是對(duì)象new()。我們常說對(duì)象xx,比如這里的對(duì)象b。但實(shí)際上b只是一個(gè)對(duì)象的引用,真正的對(duì)象是new()!
需要注意的是,它也是一種引用數(shù)據(jù)類型,但是因?yàn)槭褂寐史浅8?,所以可以像JVM的基本數(shù)據(jù)類型一樣使用:a = "abc";等同于 a = new ("abc") ;
簡(jiǎn)而言之,簡(jiǎn)單來說,類是指引用數(shù)據(jù)的類型,對(duì)象是具體的賦值。為了更深入的理解,我們需要在下面解釋這個(gè)引用是如何反映的。
什么是引用(來自記憶)
要深入理解什么是類,什么是對(duì)象,什么是引用,都離不開Java的內(nèi)存使用。
在Java中,內(nèi)存大致分為棧()和堆(heap)(之所以粗略是因?yàn)樗€包括其他幾個(gè)部分,這里就不贅述了)。我不會(huì)在這里詳細(xì)說明什么是堆棧和堆。有時(shí)間我會(huì)整理一篇文章詳細(xì)講解。
這里只講一點(diǎn):在Java中,基本的數(shù)據(jù)類型和對(duì)象引用是存放在棧()中,而對(duì)象是存放在堆(heap)中的,比如下面的代碼:
int a=1; Person p;
內(nèi)存中的狀態(tài)大致如下:
int a = 1 直接在棧中開辟空間,沒有實(shí)例化的p的值為null,因?yàn)闆]有有效的內(nèi)存地址來引用它。而當(dāng)代碼修改如下:
int a =1 ; Person p = new Person();
內(nèi)存中的狀態(tài)大致如下:
p=new();使p的值=即對(duì)象的地址new();在堆中。所以不難理解前面提到的對(duì)象的引用,所謂引用其實(shí)就是對(duì)堆內(nèi)存地址的引用??偨Y(jié)
隨著計(jì)算機(jī)技術(shù)的不斷進(jìn)步,現(xiàn)在計(jì)算機(jī)不僅僅是用來解決計(jì)算問題,而是用來解決越來越接近現(xiàn)實(shí)生活的復(fù)雜問題。面向?qū)ο笳沁@一發(fā)展過程的產(chǎn)物,它使編程工作更貼近人們的思維方式,從而大大提高了編程效率。我們必須明白的是,面向?qū)ο蟛皇且环N編程方式,而是一種編程思維方式,包括分析、設(shè)計(jì)、編碼等。在面向?qū)ο缶幊讨?strong>數(shù)據(jù)結(jié)構(gòu)用面向?qū)ο蠓椒ㄅcc++描述,程序的基本單元是對(duì)象,數(shù)據(jù)被封裝在對(duì)象中。類是一個(gè)對(duì)象模板,它是一個(gè)預(yù)定義的結(jié)構(gòu)。所謂類的實(shí)例化,就是將數(shù)據(jù)填充到模板中。
最后,我的文筆不是很好,需要改進(jìn)。寫文章和博客的最大目的是梳理自己的想法,其次是分享自己的想法。希望大家多多投訴,共同進(jìn)步。