Java基礎(chǔ)之面向?qū)ο?/span>第1章:面向?qū)ο蟾拍?/span>下面就進(jìn)入java的核心部分:面向?qū)ο蟆?/span> java是一門面向?qū)ο蟮恼Z(yǔ)言。 什么是面向?qū)ο竽兀?/span> 面向?qū)ο笫窍鄬?duì)于面向過(guò)程而言的。 1.1 理解面向?qū)ο?/span>下面我們來(lái)舉個(gè)例子, 用手機(jī)發(fā)短信 1:開(kāi)機(jī) 2:發(fā)送短信 如果使用面向過(guò)程來(lái)實(shí)現(xiàn)這個(gè)過(guò)程的話就是第一步,打開(kāi)手機(jī),第二步發(fā)送短信,相當(dāng)于我們要針對(duì)手機(jī)定義兩個(gè)功能,一個(gè)是打開(kāi)手機(jī),一個(gè)是發(fā)送短信,這里強(qiáng)調(diào)的是執(zhí)行的過(guò)程。 如果使用面向?qū)ο蟮乃枷雭?lái)實(shí)現(xiàn)這個(gè)功能的話,就是我找到手機(jī),(手機(jī)本身是有開(kāi)機(jī)和發(fā)送短信的功能的),然后調(diào)用手機(jī)的開(kāi)機(jī)和發(fā)送短信功能即可,不需要我自己定義這兩個(gè)功能,強(qiáng)調(diào)的是具體事物,也稱為對(duì)象。 如圖1.1中的對(duì)比情況。 圖1.1 面向?qū)ο蠓治?/span> 也就是說(shuō)如果使用面向過(guò)程來(lái)實(shí)現(xiàn)這個(gè)功能,我們需要實(shí)現(xiàn)兩個(gè)功能,直接調(diào)用這兩個(gè)功能, 而使用面向?qū)ο笏枷氲脑?,我們就需要定義一個(gè)對(duì)象,把這兩個(gè)功能封裝到對(duì)象里面,后續(xù)使用的的話就只需要找到這個(gè)對(duì)象就行了,這個(gè)對(duì)象就有這些功能。 側(cè)重點(diǎn)是不一樣的。 面向過(guò)程的話我們需要關(guān)注這兩個(gè)過(guò)程具體是如何實(shí)現(xiàn)的,而面向?qū)ο蟮脑捨也恍枰芄δ軐?shí)現(xiàn)的細(xì)節(jié)就可以了,只需要會(huì)使用手機(jī)就行了。 面向?qū)ο笫窍雽?duì)于面向過(guò)程而言的,無(wú)論過(guò)程還是對(duì)象都是生活中的一種思考方式(思想)。 總結(jié): 1. 面向?qū)ο笫且环N符合現(xiàn)在人們思考習(xí)慣的一種思想。 2. 面向?qū)ο蟮某霈F(xiàn)讓角色發(fā)生了變化,從執(zhí)行者轉(zhuǎn)變成了指揮者。 3. 面向?qū)ο髮?fù)雜的事情簡(jiǎn)單化。 不在強(qiáng)調(diào)過(guò)程,而是強(qiáng)調(diào)對(duì)象,找到了對(duì)象,其實(shí)就找到了這些功能。 面向?qū)ο笃鋵?shí)就是基于面向過(guò)程而來(lái)的。 假設(shè)我想做幾道菜,但是我自己還不會(huì)做, 按照面向過(guò)程的思想,那么就需要我去學(xué)習(xí)這幾道菜的做法。 如果按照面向?qū)ο蟮乃枷?,我只需要找一個(gè)廚子就可以解決了。 或者我直接找一個(gè)會(huì)做飯的女朋友不是更好,什么洗衣服做飯都搞定了^^。 針對(duì)我們之前寫的數(shù)組排序的代碼來(lái)說(shuō),是我們自己實(shí)現(xiàn)的,按照我們剛才說(shuō)的,java是一門面向?qū)ο蟮恼Z(yǔ)言,那么他有沒(méi)有實(shí)現(xiàn)這個(gè)方法,并把這個(gè)方法封裝到一個(gè)對(duì)象中呢? 有!前面我們?cè)谥v數(shù)組排序的時(shí)候也講到過(guò)Arrays這個(gè)工具類,它里面有一個(gè)封裝好的sort排序方法,這其實(shí)就是面向?qū)ο笏枷氲囊环N具體體現(xiàn)。 1.2 面向?qū)ο蟮奶卣?/span>面向?qū)ο蟮娜筇卣鳎?/span> l 封裝(encapsulation) l 繼承(inheritance) l 多態(tài)(polymorphism) 后面會(huì)詳細(xì)分析面向?qū)ο蟮倪@三大特征。 第2章:類與對(duì)象之間的關(guān)系之前我們寫過(guò)一些類,還沒(méi)有定義過(guò)對(duì)象,那類與對(duì)象具體有什么關(guān)系呢? 2.1 類與對(duì)象的關(guān)系計(jì)算機(jī)語(yǔ)言開(kāi)發(fā),其實(shí)就是不斷用計(jì)算機(jī)語(yǔ)言來(lái)描述現(xiàn)實(shí)生活中的事務(wù)。 對(duì)于java語(yǔ)言,它描述事物是通過(guò)類來(lái)體現(xiàn)的。 類:就是某一個(gè)具體事物的抽象,概念上的定義 對(duì)象:就是該類事物實(shí)實(shí)在在存在的個(gè)體 描述任意一類事物,其實(shí)就是在描述該類事物的屬性和行為。 我們把一批對(duì)象中共性的屬性和功能抽取出來(lái),定義為一個(gè)類,類就是這一組對(duì)象的抽象,如圖2.1所示。 圖 2.1 類與對(duì)象的關(guān)系 2.2 類與對(duì)象案例分析現(xiàn)在有一個(gè)需求,使用計(jì)算機(jī)語(yǔ)言來(lái)描述汽車這一類事物。 代碼實(shí)例如圖2.2所示。 圖2.2 car代碼 如何創(chuàng)建該類的對(duì)象呢? java中通過(guò)new關(guān)鍵字來(lái)創(chuàng)建實(shí)體,實(shí)體其實(shí)就是用來(lái)封裝具體數(shù)據(jù)用的,數(shù)組是這樣,對(duì)象也是。 Car c = new Car();//通過(guò)Car類創(chuàng)建了一個(gè)具體對(duì)象。該對(duì)象如何使用呢?為了方便使用該對(duì)象,就給對(duì)象起個(gè)名字。 等號(hào)左邊:定義了一個(gè)變量,是Car類型的。而Car是一個(gè)類,所以c是類類型變量,類類型變量一定指向?qū)ο?。因?yàn)樗硪粋€(gè)實(shí)體。 這樣我們就完成了一個(gè)對(duì)象的建立。 下面我們就想讓這個(gè)車運(yùn)行一下,車運(yùn)行的方法我們已經(jīng)封裝好了。直接調(diào)用run方法即可。 調(diào)用格式:對(duì)象.對(duì)象成員 c.run();//指揮車進(jìn)行運(yùn)行。 詳細(xì)代碼如圖2.3所示。 圖2.3 運(yùn)行car對(duì)象 最終輸出的color是null,num是0,因?yàn)檫@里我們還沒(méi)有給這些變量賦值,所以輸出的是默認(rèn)值。 從現(xiàn)實(shí)生活過(guò)渡到計(jì)算機(jī)中, Car:對(duì)應(yīng)的是生活中的汽車圖紙 new Car():對(duì)應(yīng)的是生活中具體的汽車 2.3 對(duì)象的內(nèi)存圖下面來(lái)看一下這段代碼在內(nèi)存中的到底是什么樣子的呢,如圖2.4所示。 Car c = new Car(); 首先有棧和堆,在這我們先不說(shuō)方法,后面再說(shuō) 用new建立的都是實(shí)體,實(shí)體都是存在堆內(nèi)存中的,堆內(nèi)存會(huì)給這個(gè)實(shí)體分配一個(gè)內(nèi)存空間,分配內(nèi)存首地址值。 每個(gè)實(shí)體都有color和num屬性,如果沒(méi)有給這兩個(gè)屬性賦值,那這兩個(gè)屬性有沒(méi)有默認(rèn)值呢? 注意:堆內(nèi)存中的所有變量都有默認(rèn)初始化值,字符串默認(rèn)值為null,數(shù)字默認(rèn)值為0。 把內(nèi)存首地址賦給變量c,變量c就指向這個(gè)對(duì)象了。 c.color=”red”; c.num=4; c.run(); 執(zhí)行代碼,就會(huì)打印紅色和四個(gè)輪胎。 圖2.4 對(duì)象內(nèi)存圖 第二個(gè)案例如圖2.5所示。 圖2.5 對(duì)象內(nèi)存圖 2.4 成員變量和局部變量定義在類中的變量稱為成員變量,定義在函數(shù)中的變量稱為局部變量。 成員變量和局部變量的區(qū)別? 1. 作用范圍 成員變量:定義在類中,在整個(gè)類中都可以被訪問(wèn) 局部變量:只定義在局部范圍內(nèi),如:函數(shù)內(nèi),語(yǔ)句內(nèi)等,只在它所屬的局部大括號(hào)中有效 2. 存儲(chǔ)情況 成員變量:成員變量隨著對(duì)象的建立而建立,存在于對(duì)象所在的堆內(nèi)存中,當(dāng)對(duì)象編程垃圾被回收時(shí),該對(duì)象中的成員變量才會(huì)消失,所以,成員變量也稱為實(shí)例(對(duì)象)變量。 局部變量:局部變量存在于棧內(nèi)存中,當(dāng)方法或者語(yǔ)句被執(zhí)行的時(shí)候才存在,當(dāng)方法或者語(yǔ)句執(zhí)行結(jié)束時(shí),該局部變量會(huì)被自動(dòng)釋放 3. 初始化情況 成員變量:在堆內(nèi)存中有默認(rèn)初始化值 局部變量:沒(méi)有默認(rèn)初始化值,需要手工初始化后才可以使用 2.5 匿名對(duì)象這塊內(nèi)容了解就可以。 下面是正常的代碼,如圖2.6所示。 圖2.6 代碼 注意:下面的代碼是完全不同的,第一個(gè)寫法只創(chuàng)建了一個(gè)對(duì)象,第二種寫法創(chuàng)建了三個(gè)對(duì)象,這種寫法就是匿名對(duì)象的寫法,如圖2.7所示。 圖2.7 匿名對(duì)象 為什么使用匿名對(duì)象的時(shí)候可以調(diào)用方法,但是不建議調(diào)用屬性呢? 因?yàn)槟涿麑?duì)象調(diào)用屬性沒(méi)意義。 如圖2.8所示。 默認(rèn)car里面有兩個(gè)屬性,顏色為紅色,num為4. 左邊的代碼會(huì)產(chǎn)生一個(gè)對(duì)象。并修改顏色和num 右邊的代碼會(huì)產(chǎn)生三個(gè)對(duì)象,但是在棧里面沒(méi)有任何東西,因?yàn)?,就沒(méi)有變量產(chǎn)生,直接產(chǎn)生對(duì)象,當(dāng)產(chǎn)生一個(gè)對(duì)象,并修改對(duì)象中的一個(gè)屬性之后,這個(gè)對(duì)象就變成垃圾了,這個(gè)對(duì)象什么事也沒(méi)干。最后執(zhí)行run的時(shí)候打印出來(lái)的還是默認(rèn)屬性的值。 圖2.8 匿名對(duì)象的調(diào)用
如果new多個(gè)car,都修改統(tǒng)一的屬性,調(diào)用同樣的方法,這樣代碼重復(fù)度太高,可以提取一個(gè)方法,把car對(duì)象作為一個(gè)參數(shù)進(jìn)行傳遞,如圖2.9所示。 圖2.9 匿名對(duì)象的第二種用法 匿名對(duì)象:沒(méi)有名字的對(duì)象,是定義對(duì)象的一種簡(jiǎn)化形式。 使用注意: l 當(dāng)對(duì)象對(duì)方法只進(jìn)行一次調(diào)用的時(shí)候,可以使用匿名對(duì)象對(duì)代碼進(jìn)行簡(jiǎn)化。 l 如果對(duì)象要對(duì)成員進(jìn)行多次調(diào)用,必須給對(duì)象起個(gè)名字,不能再使用匿名對(duì)象 第3章:封裝下面我們來(lái)學(xué)習(xí)一下面向?qū)ο蟮牡谝粋€(gè)特征:封裝。 3.1 封裝介紹封裝:是指隱藏對(duì)象的屬性和實(shí)現(xiàn)細(xì)節(jié),僅對(duì)外提供公共訪問(wèn)方式。 舉個(gè)例子,人開(kāi)車這個(gè)例子, 我們想要開(kāi)車的話,沒(méi)必要知道具體車是怎么運(yùn)行的,我們只要用鑰匙把車啟動(dòng)就可以了。 拿我們計(jì)算機(jī)來(lái)說(shuō),我們的筆記本。里面都有主板,內(nèi)存,硬盤,具體這些東西是怎么運(yùn)行的不需要我們關(guān)心,這些細(xì)節(jié)都被封裝到計(jì)算機(jī)里面了,計(jì)算機(jī)給我們提供了一個(gè)開(kāi)機(jī)按鈕,我們只需要按一下開(kāi)機(jī)按鈕就可以使用這些東西了。 封裝的好處: l 將變化隔離。 具體計(jì)算機(jī)內(nèi)部有什么變化我們不需要知道,只要能正常開(kāi)機(jī)使用即可。 l 便于使用。 我們不需要知道計(jì)算機(jī)內(nèi)部是怎么拼裝的,只要能使用就可以。 l 提高重用性。 即使后期筆記本的主板或者內(nèi)存發(fā)生變化,筆記本還是可以繼續(xù)使用的。 l 提高安全性。 如果把主板,內(nèi)存,硬盤,全部暴露在外面的話,是很不安全的 封裝原則: l 將不需要對(duì)外提供的內(nèi)容都隱藏起來(lái)。 l 把屬性都隱藏,提供公共方法對(duì)其訪問(wèn)。 3.2 private(私有)如圖3.1所示: 執(zhí)行main函數(shù),會(huì)打印出人的年齡,如果把年齡定義為20,則沒(méi)有問(wèn)題,如果定義為-20,則就有問(wèn)題了,雖然代碼可以運(yùn)行,但是不合常理,人的年齡不可能有負(fù)數(shù),但是在java中int類型是可以接受負(fù)數(shù)的。所以說(shuō)直接把a(bǔ)ge這個(gè)變量暴露出去,讓其他人操作是比較危險(xiǎn)的,這個(gè)時(shí)候就需要把這個(gè)age這個(gè)變量隱藏起來(lái),不想讓其他人直接訪問(wèn)進(jìn)行賦值。 圖3.1 代碼 在這為了不讓其他人訪問(wèn),我們需要介紹一個(gè)權(quán)限修飾符:private private是私有的意思。 當(dāng)我們?cè)赼ge 前面加一個(gè)private之后,再編譯PersonDemo類的代碼,會(huì)報(bào)錯(cuò),提示age這個(gè)屬性只能在person類中訪問(wèn)。 這是因?yàn)槲覀儼阉揎棡樗接械闹螅荒茉谒鶎賹?duì)象中使用,權(quán)限降低了。其他程序就不能訪問(wèn)了。這樣就保證了安全性了。 但是外界就無(wú)法操作了,該如何賦值呢? 那么我們可以在所屬對(duì)象中提供一個(gè)方法,在這先隨便起個(gè)名字haha(),我在這個(gè)類的方法中是可以操作這個(gè)屬性的,你只需要訪問(wèn)我這個(gè)方法就可以了。 這個(gè)時(shí)候如果你傳遞的值為負(fù)數(shù)的話,我就可以在函數(shù)中使用if進(jìn)行判斷了。這樣我們就可以通過(guò)對(duì)外提供公有方法的形式對(duì)外提供訪問(wèn),并對(duì)訪問(wèn)進(jìn)行控制,如圖3.2所示。 圖3.2 代碼 通常將成員變量私有化,并通過(guò)對(duì)外提供set 和 get方法對(duì)其進(jìn)行設(shè)置和獲取。 方法名稱的定義格式,第一個(gè)單詞首字母小寫,后面的單詞首字母大寫 一般情況下,一個(gè)類中只要有屬性,這個(gè)屬性都會(huì)私有,并且對(duì)外提供兩個(gè)方法,一個(gè)set和一個(gè)get,這都是固定的格式。 set方法是對(duì)私有的變量進(jìn)行賦值操作的 get方法是獲取私有變量的值的。 set方法的返回值一般都是void類型,他只負(fù)責(zé)賦值。 get方法的返回值類型一般都是私有變量的類型。 代碼實(shí)現(xiàn)如圖3.3所示。 圖3.3 代碼 也就是說(shuō)私有就是封裝的一種體現(xiàn)形式。 這里我們是對(duì)成員變量進(jìn)行私有化,那么成員方法需不需要私有化呢? 這個(gè)就要看需求了,如果確實(shí)不需要對(duì)外提供訪問(wèn),那么就設(shè)置為私有的。 Eclipse 中提供了一個(gè)根據(jù)成員變量快速生成 get/set 方法的工具。主菜單【Source】→【Generate Getters Setters】。如圖3.4所示。 圖3.4 快速生成get和set方法 第4章:構(gòu)造函數(shù)下面我們來(lái)學(xué)習(xí)一下類里面的構(gòu)造函數(shù) 這個(gè)函數(shù)和我們之前學(xué)過(guò)的函數(shù)不一樣,我們之前學(xué)過(guò)兩種,主函數(shù)和一般函數(shù) 4.1 構(gòu)造函數(shù)介紹構(gòu)造函數(shù)的特點(diǎn): l 函數(shù)名與類名相同 l 不用定義返回值類型 l 不需要寫return語(yǔ)句(沒(méi)有具體的返回值) l 在對(duì)象建立的時(shí)候被直接調(diào)用 一般的函數(shù),我們想要使用的時(shí)候,必須指定才能調(diào)用,而構(gòu)造函數(shù)則比較特殊,不需要指定。當(dāng)類初始化的時(shí)候就會(huì)自動(dòng)被調(diào)用,如圖4.1所示。 圖4.1 構(gòu)造函數(shù)代碼 注意: 構(gòu)造函數(shù)再對(duì)象建立時(shí)就被直接調(diào)用 構(gòu)造函數(shù)的作用: 用于給對(duì)象進(jìn)行初始化。 前面我們?cè)趧?chuàng)建類的時(shí)候沒(méi)有創(chuàng)建構(gòu)造函數(shù)。那對(duì)象有沒(méi)有被初始化呢? 被初始化了,如果我們沒(méi)有顯式創(chuàng)建構(gòu)造函數(shù),那么系統(tǒng)會(huì)給這個(gè)類創(chuàng)建一個(gè)默認(rèn)的構(gòu)造函數(shù),例如前面創(chuàng)建的Person類,如圖4.2所示。 圖4.2 默認(rèn)構(gòu)造函數(shù) 注意: 當(dāng)類中沒(méi)有定義構(gòu)造函數(shù)時(shí),系統(tǒng)會(huì)自動(dòng)給該類加上一個(gè)空參數(shù)的構(gòu)造函數(shù),這個(gè)是類中默認(rèn)的構(gòu)造函數(shù)。 當(dāng)類中自定義了構(gòu)造函數(shù),這時(shí)默認(rèn)的構(gòu)造函數(shù)就沒(méi)有了。 在一個(gè)類中可以定義多個(gè)構(gòu)造函數(shù),以進(jìn)行不同的初始化,多個(gè)構(gòu)造函數(shù)存在于類中,是以重載的形式體現(xiàn)的,因?yàn)闃?gòu)造函數(shù)的名稱都相同。 如圖4.3中的案例所示。 這兩個(gè)構(gòu)造函數(shù)都可以使用,初始化方式是不一樣的。 默認(rèn) new person2();是調(diào)用person2()這個(gè)構(gòu)造函數(shù) 有些人一出生就帶著姓名new person2(“zhangsan”);這時(shí)就會(huì)使用person2(String n)這個(gè)構(gòu)造函數(shù)進(jìn)行初始化。 圖 4.3 多個(gè)構(gòu)造函數(shù) 4.2 構(gòu)造函數(shù)和一般函數(shù)的區(qū)別構(gòu)造函數(shù)和一般函數(shù)的區(qū)別: l 區(qū)別之一 一般函數(shù)是用于定義對(duì)象應(yīng)該具備的功能 構(gòu)造函數(shù)定義的是對(duì)象在建立時(shí)需要具備的一些內(nèi)容,也就是對(duì)象的初始化內(nèi)容 l 區(qū)別之二 一般函數(shù)在對(duì)象建立后,當(dāng)對(duì)象調(diào)用該功能時(shí)才會(huì)執(zhí)行。 構(gòu)造函數(shù)是在對(duì)象建立時(shí)被調(diào)用,為了給對(duì)象進(jìn)行初始化。 4.3 構(gòu)造函數(shù)練習(xí)題需求: 創(chuàng)建一個(gè)Person類,Person有姓名、年齡(年齡在1~100之間)屬性,還有一個(gè)說(shuō)話的功能,在說(shuō)話的時(shí)候會(huì)把自己的姓名和年齡說(shuō)出來(lái)。 在創(chuàng)建Person對(duì)象的時(shí)候要支持直接傳遞姓名和年齡參數(shù)。 代碼實(shí)現(xiàn)如圖4.4所示。 圖4.4 練習(xí)題 第5章:this關(guān)鍵字5.1 this關(guān)鍵字介紹我們先寫一個(gè)demo,正常情況下可以這樣寫,如圖5.1所示。 這個(gè)代碼雖然沒(méi)問(wèn)題,但是參數(shù)的名稱定義的不太友好,不容易識(shí)別。我們想要定義一個(gè)有意義的名稱,這樣別人在調(diào)用這個(gè)構(gòu)造函數(shù)的時(shí)候就知道該傳遞什么內(nèi)容。 圖5.1 代碼 這樣定義行不行呢?如圖5.2所示。結(jié)果發(fā)現(xiàn)打印的結(jié)果為空 圖5.2 代碼 注意:當(dāng)你的局部變量和成員變量同名的時(shí)候,因?yàn)楹瘮?shù)內(nèi)部已經(jīng)有這個(gè)局部變量了,那么就不會(huì)再到外面找了,所以在這就是局部變量name把值賦給了自己。所以成員變量name的值還是null。 如果想把這個(gè)局部變量賦給成員變量怎么辦呢? 在這可以使用this關(guān)鍵字 當(dāng)局部變量和成員變量重名時(shí),可以使用this這個(gè)關(guān)鍵字進(jìn)行區(qū)分,如圖5.3所示。 圖5.3 代碼 原理是什么呢? this:代表所在函數(shù)所屬對(duì)象的引用。 因?yàn)閠his是在person4這個(gè)構(gòu)造函數(shù)中,這個(gè)函數(shù)是屬于p這個(gè)對(duì)象的,所以,this就代表p的引用。 也就是說(shuō),this代表當(dāng)前對(duì)象。 5.2 this關(guān)鍵字在構(gòu)造函數(shù)間的調(diào)用如果想要在初始化一個(gè)對(duì)象的時(shí)候,還進(jìn)行其他初始化,那就需要用到構(gòu)造函數(shù)之間的調(diào)用了 假設(shè)在初始化姓名之后,還想初始化年齡,不能new兩個(gè)Person,這樣就是兩個(gè)對(duì)象了,所以就需要在初始化名字的構(gòu)造函數(shù)中調(diào)用初始化年齡的方法。 注意:不能在初始化姓名的構(gòu)造函數(shù)中使用this.Person(20),這樣是不行的,這個(gè)Person不是一個(gè)普通的方法。 應(yīng)該直接使用this(20),這樣就表示初始化過(guò)姓名的時(shí)候,先調(diào)用初始化年齡的構(gòu)造函數(shù)進(jìn)行初始化,再執(zhí)行this.name=name進(jìn)行姓名初始化,如圖5.4所示。 圖5.4 構(gòu)造函數(shù)的調(diào)用 其實(shí)可以在這簡(jiǎn)單記住一句話,想要實(shí)現(xiàn)構(gòu)造函數(shù)之間的調(diào)用,需要使用this關(guān)鍵字。 注意:this語(yǔ)句只能定義在構(gòu)造函數(shù)的第一行,因?yàn)槌跏蓟瘎?dòng)作必須先完成,如果this語(yǔ)句不放在構(gòu)造函數(shù)的第一行,是會(huì)報(bào)錯(cuò)的。 下面這種寫法是錯(cuò)誤的,如圖5.5所示。 圖5.5構(gòu)造函數(shù)的調(diào)用 也就是說(shuō)在構(gòu)造函數(shù)中是不可以寫兩個(gè)this語(yǔ)句的,因?yàn)榈诙€(gè)this語(yǔ)句就在第二行了,如圖5.6所示。 圖5.6 構(gòu)造函數(shù)的調(diào)用 下面這種寫法也不行,因?yàn)榉痔?hào)就表示第一個(gè)語(yǔ)句的結(jié)束,如圖5.7所示。 圖5.7 構(gòu)造函數(shù)的調(diào)用 第6章:static關(guān)鍵字6.1 static關(guān)鍵字介紹static這個(gè)關(guān)鍵字我們已經(jīng)用了很長(zhǎng)時(shí)間了。 static關(guān)鍵字:主要用于修飾成員(成員變量和成員函數(shù)) 看圖6.1所示的例子。 如果我new多個(gè)person,在內(nèi)存中就會(huì)存在多個(gè)對(duì)象,并且這些對(duì)象還有一些特點(diǎn),country都一樣,如果都是中國(guó)人的話,那么所有的country字段的值都為cn,所有的對(duì)象中都需要存儲(chǔ)這個(gè)相同的值,這樣是會(huì)造成重復(fù)數(shù)據(jù),浪費(fèi)內(nèi)存空間。 圖6.1 代碼 此時(shí)我可以把這個(gè)country定義到外面,對(duì)象如果想要使用的話都從外面取就行了,這樣就需要在country前面加上一個(gè)修飾符,static,這樣,加上靜態(tài)之后,這個(gè)country就不會(huì)在堆內(nèi)存的每個(gè)對(duì)象內(nèi)存在了,而是在堆內(nèi)存以外的位置存在,被所有對(duì)象所共享,如圖6.2所示。 這就是靜態(tài)的特點(diǎn)。 圖6.3 靜態(tài) 注意:靜態(tài)中的數(shù)據(jù)不存放在堆內(nèi)存中。 靜態(tài)成員多了一種調(diào)用方式,可以直接被類名所調(diào)用。 那是不是可以對(duì)類中所有的屬性都使用靜態(tài)這種功能呢? 可以是可以,語(yǔ)法沒(méi)有問(wèn)題,但是會(huì)有其它問(wèn)題, name本身是對(duì)象當(dāng)中的數(shù)據(jù),每個(gè)人的姓名是不一樣的,如果使用static關(guān)鍵字定義的話,只需要定義一次值,所有對(duì)象的name都是一個(gè)值,這樣就不合理了。 針對(duì)對(duì)象共有的數(shù)據(jù),可以使用static關(guān)鍵字定義,針對(duì)對(duì)象特有的數(shù)據(jù),就不能使用static了,這就是static關(guān)鍵字應(yīng)用的場(chǎng)景。 將共享的數(shù)據(jù)提取出來(lái),靜態(tài)化,將非共享的數(shù)據(jù)存儲(chǔ)到對(duì)象中。 舉個(gè)例子 在公司里面,飲水機(jī)一般只有一個(gè),所有人都到這接水喝,如果每個(gè)人都分配一臺(tái)飲水機(jī)可以嗎?也是可以的,但是這樣就太浪費(fèi)空間了,所以就把飲水機(jī)提取出來(lái),所有人都使用這一個(gè)就可以了,這就是共享數(shù)據(jù),但是接水的杯子是每個(gè)人都有一個(gè)的。 被靜態(tài)修飾后的成員具備以下特點(diǎn): l 隨著類的加載而加載 l 優(yōu)先于對(duì)象存在 l 被所有對(duì)象所共享 l 可以直接被類名調(diào)用(也可以被對(duì)象調(diào)用。) 看下面圖6.4所示代碼。 圖6.4 代碼 靜態(tài)方法只能訪問(wèn)靜態(tài)成員,如圖6.5中,這樣編譯是會(huì)報(bào)錯(cuò)的 因?yàn)殪o態(tài)優(yōu)先于對(duì)象存在,當(dāng)show方法存在的時(shí)候,name還不在呢,所以調(diào)用是會(huì)報(bào)錯(cuò)的。 圖6.5 靜態(tài)調(diào)用 主函數(shù)也是靜態(tài)的,這個(gè)具體在后面進(jìn)行分析。 static(靜態(tài))使用注意事項(xiàng): l 靜態(tài)方法只能訪問(wèn)靜態(tài)成員 l 靜態(tài)方法中不可以寫this,super關(guān)鍵字 l 主函數(shù)是靜態(tài)的 l 靜態(tài)關(guān)鍵字不能修飾局部變量 靜態(tài)的好處:靜態(tài)成員多了一種調(diào)用方式,可以直接被類名調(diào)用。也可以被對(duì)象調(diào)用 靜態(tài)的弊端:靜態(tài)方法只能訪問(wèn)靜態(tài)成員,出現(xiàn)了訪問(wèn)局限性 6.2靜態(tài)變量和實(shí)例變量的區(qū)別靜態(tài)變量和實(shí)例變量的區(qū)別 1: 靜態(tài)變量也稱為類變量,可以直接被類名調(diào)用,所屬于類。 非靜態(tài)變量稱為成員變量,或者實(shí)例變量,是被對(duì)象調(diào)用,所屬于對(duì)象 2: 靜態(tài)變量隨著類的加載而加載,也意味著隨著類的消失而消失,生命周期最長(zhǎng)。 實(shí)例變量,隨著對(duì)象的創(chuàng)建而加載,隨著對(duì)象的消失而消失,按照對(duì)象的生命周期而存在。 3: 靜態(tài)變量存儲(chǔ)在方法區(qū)的靜態(tài)區(qū)中。 實(shí)例變量存在于對(duì)象所屬的堆內(nèi)存中 4: 靜態(tài)變量數(shù)據(jù),被所有對(duì)象所共享。 實(shí)例變量是對(duì)象中的特有數(shù)據(jù)。 6.3 靜態(tài)的使用場(chǎng)景什么時(shí)候使用static呢? l 成員變量: 當(dāng)該成員變量所存儲(chǔ)的數(shù)據(jù),每一個(gè)對(duì)象都是一樣的,這時(shí)就沒(méi)有必要吧該數(shù)據(jù)存儲(chǔ)到每一個(gè)對(duì)象中,只要讓所有對(duì)象共享該數(shù)據(jù)即可,這時(shí)成員就需要被static修飾。 l 成員函數(shù) 當(dāng)成員函數(shù)內(nèi)并沒(méi)有訪問(wèn)對(duì)象中的特有數(shù)據(jù)時(shí),就可以將該方法定義成靜態(tài)的。 簡(jiǎn)單說(shuō):該函數(shù)如果訪問(wèn)了成員變量,該函數(shù)就是非靜態(tài)的。 該函數(shù)沒(méi)有訪問(wèn)過(guò)成員變量,或者訪問(wèn)過(guò)靜態(tài)的成員變量,那么為了程序的嚴(yán)謹(jǐn)性,將該方法定義成靜態(tài)的,因?yàn)樵摲椒ú恍枰獙?duì)象存在就可以使用。 6.4 工具類的應(yīng)用假設(shè)要做一個(gè)數(shù)組排序,并打印,代碼實(shí)現(xiàn)如圖6.6所示。 圖6.6 數(shù)組排序打印 前面實(shí)現(xiàn)的功能代碼沒(méi)有問(wèn)題,但是所有代碼都放在主函數(shù)內(nèi)導(dǎo)致代碼很臃腫,并且后期如果還想使用排序功能的話也不能實(shí)現(xiàn)代碼的復(fù)用。 所以需要對(duì)代碼進(jìn)行重構(gòu),抽取兩個(gè)方法,排序方法和數(shù)組轉(zhuǎn)化為string的方法,代碼實(shí)現(xiàn)如圖6.7所示。 圖6.7 抽取方法 剛才我們提取的功能現(xiàn)在只能在這個(gè)類中才能使用,我們希望以后能隨時(shí)使用這個(gè)功能,所以我們現(xiàn)在還需要把這些功能封裝到一個(gè)數(shù)組工具類中,以后想對(duì)數(shù)組操作的時(shí)候,只要找到這個(gè)對(duì)象,就可以對(duì)數(shù)組進(jìn)行操作了。 把剛才提取的兩個(gè)方法,重新封裝到一個(gè)ArrayTool類中,這個(gè)里面是不需要主函數(shù)的,因?yàn)閷懼骱瘮?shù)主要是為了讓當(dāng)前類執(zhí)行。 注意:先把這兩個(gè)方法上的static關(guān)鍵字去掉。 這個(gè)類定義好了之后,任何人都可以使用里面的功能了,如圖6.8所示。 圖6.8 提取公共方法 這樣再進(jìn)行排序的時(shí)候就可以這樣使用了,如圖6.9所示。 圖6.9 使用工具類 下面分析下這個(gè)Arraytool類 這個(gè)類如果我們多個(gè)地方使用的時(shí)候是需要new多次的,會(huì)生成多個(gè)對(duì)象,然后分析這個(gè)類里面的方法,發(fā)現(xiàn)這里面的方法并沒(méi)有訪問(wèn)這個(gè)類的特有成員變量,那么這個(gè)時(shí)候,這個(gè)對(duì)象的建立是沒(méi)有意義的。 其實(shí)對(duì)象存在的根本原因是為了封裝他的特有數(shù)據(jù),在這個(gè)類里面沒(méi)有特有數(shù)據(jù),所以這幾個(gè)方法都可以使用static進(jìn)行修飾。 下面把這個(gè)工具類里面的所有方法都加上static關(guān)鍵字,那么再使用這個(gè)工具類的時(shí)候就非常簡(jiǎn)單了,使用的時(shí)候直接使用類名調(diào)用即可,如圖6.10所示。 圖6.10 工具類的使用 這個(gè)類中的功能都被靜態(tài)化后,只需要使用類名調(diào)用即可,但是其他程序在使用該類時(shí),是可以建立該類對(duì)象的(因?yàn)樵擃愔杏幸粋€(gè)默認(rèn)的構(gòu)造函數(shù))。 但是該類并不需要對(duì)象就可以使用類中的成員,所以針對(duì)這個(gè)類建立對(duì)象是沒(méi)有意義的,為了提高程序的嚴(yán)謹(jǐn)性,應(yīng)該強(qiáng)制不讓其他程序建立該類對(duì)象。 如何才能讓其他程序訪問(wèn)不到我的構(gòu)造函數(shù)呢(也就是說(shuō)如何才能強(qiáng)制不讓其他程序建立該類對(duì)象)? 私有化構(gòu)造函數(shù)就可以,如圖6.11所示。 圖6.11 私有化構(gòu)造函數(shù) 下面再給這個(gè)類加一些注釋,不然把這個(gè)工具類給其他人的時(shí)候他們不知道該如何使用。 完整工具類代碼示例如圖6.12所示。 圖6.12 工具類案例 6.5 main函數(shù)上一節(jié)我們?cè)趯W(xué)習(xí)靜態(tài)的時(shí)候說(shuō),主函數(shù)是靜態(tài)的,為什么把它單獨(dú)列出來(lái)呢? 因?yàn)橹骱瘮?shù)比較特殊,他是被虛擬機(jī)所調(diào)用的。 這個(gè)例子編譯會(huì)報(bào)錯(cuò),因?yàn)閟how不是靜態(tài)的,在main中無(wú)法直接調(diào)用,如圖6.13所示。 圖6.13 代碼 那這個(gè)加上靜態(tài)之后還是不行,因?yàn)閤不是靜態(tài)的,如圖6.14所示, 圖6.14 代碼 如果想讓代碼編譯不報(bào)錯(cuò),還需要把x定義為靜態(tài),但是這樣就導(dǎo)致方法和變量的生命周期變長(zhǎng),這樣是沒(méi)有意義的,我只是為了簡(jiǎn)單的打印一個(gè)x而已。 所以不能這樣做,把static取消掉,show這個(gè)時(shí)候是非靜態(tài)的,只能被對(duì)象調(diào)用。 那在main函數(shù)中能不能使用this來(lái)調(diào)用show方法? 注意了,靜態(tài)里面是不能使用this的。 所以只能使用new創(chuàng)建一個(gè)對(duì)象來(lái)調(diào)用show方法。 這個(gè)類存在的目的就是去建立其他類的對(duì)象,并調(diào)用其他類中的功能。 可以驗(yàn)證其他類中的功能是否可用。(也包含當(dāng)前類。) 下面來(lái)解釋下主函數(shù)的定義: 注意:主函數(shù)沒(méi)有返回值,假設(shè)有返回值,虛擬機(jī)也不知道該怎么用。 public static void main(String[] args) public:jvm調(diào)用的函數(shù)必須權(quán)限足夠大,所以被public修飾 static:主函數(shù)隨著類的加載而加載,jvm不需要?jiǎng)?chuàng)建該類對(duì)象就可以完成對(duì)該函數(shù)的調(diào)用,所以是靜態(tài)的 void:主函數(shù)沒(méi)有返回值 main:函數(shù)名,是固定的,jvm可以識(shí)別 (String[] args):主函數(shù)的參數(shù)列表。參數(shù)類型是:一個(gè)字符串?dāng)?shù)組類型的參數(shù)。 args:參數(shù)名稱,這個(gè)名稱可以隨便改,全稱是arguments 主函數(shù)有參數(shù),那么jvm調(diào)用的時(shí)候到底傳入了什么? 先打印這個(gè)args參數(shù),發(fā)現(xiàn)打印的結(jié)果是一個(gè)字符串?dāng)?shù)組。 再打印一下數(shù)數(shù)組的長(zhǎng)度,發(fā)現(xiàn)是0。 這也就說(shuō)明默認(rèn)情況下jvm在調(diào)用main方法的時(shí)候什么參數(shù)都沒(méi)有傳遞。 如圖6.15所示。 圖6.15 args參數(shù) 如何給main方法傳遞參數(shù)呢? 在執(zhí)行這個(gè)類的時(shí)候 ,在后面指定參數(shù)即可,這些參數(shù)會(huì)被jvm封裝到一個(gè)字符串?dāng)?shù)組傳遞到類的主函數(shù)中。 這樣就可以讓用戶在運(yùn)行程序的時(shí)候,動(dòng)態(tài)傳入指定的參數(shù),在eclipse中可以這樣來(lái)實(shí)現(xiàn)。 代碼如圖6.16所示,這個(gè)代碼默認(rèn)情況下直接運(yùn)行是會(huì)報(bào)錯(cuò)的,數(shù)組角標(biāo)越界異常。 eclipse中執(zhí)行流程如圖6.17所示。 圖6.16 代碼 圖6.17 執(zhí)行流程 運(yùn)行結(jié)果如圖6.18所示。 圖6.18 結(jié)果 注意:如果想要傳遞多個(gè)參數(shù),則在這里寫多個(gè)參數(shù)即可,多個(gè)參數(shù)之間用空格隔開(kāi),如圖6.19所示。 圖6.19 傳遞多個(gè)參數(shù)。 使用for循環(huán)把a(bǔ)rgs中的參數(shù)都打印一下,如圖6.20所示。 圖6.20 打印main接收到的參數(shù) 6.6 靜態(tài)代碼塊接下來(lái)學(xué)習(xí)一下靜態(tài)中的靜態(tài)代碼塊。 靜態(tài)代碼塊的格式: static{ code...; } 例子: 如果一個(gè)類里面的所有成員都是靜態(tài)的,也把構(gòu)造函數(shù)私有化了,不能創(chuàng)建對(duì)象了,這個(gè)時(shí)候還想給這個(gè)類進(jìn)行一些初始化動(dòng)作。 在這我們就可以使用靜態(tài)代碼塊來(lái)實(shí)現(xiàn)。 靜態(tài)代碼塊的作用: 當(dāng)類在加載進(jìn)內(nèi)存時(shí),如果需要進(jìn)行一些內(nèi)容的執(zhí)行,完成一個(gè)類的初始化,就需要使用靜態(tài)代碼塊來(lái)完成 靜態(tài)代碼塊的特點(diǎn): 當(dāng)類被加載時(shí),靜態(tài)代碼就執(zhí)行了,而且只執(zhí)行一次,優(yōu)先于main函數(shù)執(zhí)行。 看下這個(gè)代碼的執(zhí)行結(jié)果,如圖6.21所示。 從這個(gè)代碼的輸出結(jié)果中可以看出來(lái),靜態(tài)代碼只會(huì)執(zhí)行一次,并且會(huì)優(yōu)先于main函數(shù)執(zhí)行。 圖6.21 靜態(tài)代碼塊的執(zhí)行順序 第7章:?jiǎn)卫O(shè)計(jì)模式下面使用靜態(tài)代碼塊的知識(shí)來(lái)學(xué)習(xí)一下設(shè)計(jì)模式 設(shè)計(jì)模式:可以理解為是一個(gè)固定的流程,就像是工廠里面的流水線,假設(shè)要做一個(gè)手機(jī),都是有步驟的,先做什么,再做什么。都按照這個(gè)流程來(lái)走,就可以提高工作的效率。 設(shè)計(jì)模式:就是解決某類問(wèn)題最行之有效的解決方案,設(shè)計(jì)模式是一種思想。 Java里面也有設(shè)計(jì)模式,一共總結(jié)出了23種設(shè)計(jì)模式 在這我們先學(xué)習(xí)其中的一種模式:?jiǎn)卫O(shè)計(jì)模式 7.1 單例設(shè)計(jì)模式介紹單例設(shè)計(jì)模式 解決的問(wèn)題:保證一個(gè)類在內(nèi)存中只有一個(gè)對(duì)象 簡(jiǎn)單說(shuō)就是保證一個(gè)類在內(nèi)存中的對(duì)象唯一性 應(yīng)用場(chǎng)景: 比如配置文件,A程序要使用配置文件對(duì)象,B程序也要使用配置文件對(duì)象,希望A程序?qū)ε渲梦募?shù)據(jù)修改后,B程序可以直接使用,那么就需要A程序和B程序使用同一個(gè)對(duì)象 看下面這個(gè)圖7.1。假設(shè)我有一個(gè)類,這個(gè)類里面封裝了很多配置信息, 右邊有兩個(gè)程序A和B 都要使用這個(gè)配置里面的信息,首先是A程序,使用這個(gè)類的時(shí)候需要使用new創(chuàng)建這個(gè)對(duì)象,后續(xù)可能會(huì)修改這個(gè)配置中某一些屬性的值。 后面B程序也要使用這個(gè)配置類,也需要重新new,可能也會(huì)修改配置類中屬性的值。 這個(gè)時(shí)候兩個(gè)程序中的修改操作都是修改自己創(chuàng)建的config配置對(duì)象,這是兩個(gè)單獨(dú)的對(duì)象,這樣就不能實(shí)現(xiàn)使用共同的配置了。 這個(gè)時(shí)候我們希望A程序和B程序修改的都是同一個(gè)對(duì)象。 那我們把這個(gè)類中的屬性都定義成靜態(tài)不是就可以嗎 注意:定義靜態(tài)理論上是可以的,但是這個(gè)配置類里面有很多配置信息,如果都定義成靜態(tài)的話,造成生命周期過(guò)長(zhǎng)。但是我們還是希望A程序和B程序使用到的是同一個(gè)對(duì)象,那么就需要保證這個(gè)類在內(nèi)存中的對(duì)象唯一性。 圖7.1 配置信息 如何保證一個(gè)類在內(nèi)存中的對(duì)象唯一性呢? 1:在本類中自定義一個(gè)本類對(duì)象 2:對(duì)外提供獲取該對(duì)象的方式即可 代碼具體體現(xiàn) 1:私有化構(gòu)造函數(shù) 2:創(chuàng)建一個(gè)私有并且靜態(tài)的本類的對(duì)象 3:創(chuàng)建一個(gè)公共的static方法返回該對(duì)象 7.2 單例設(shè)計(jì)模式的兩種實(shí)現(xiàn)第一種實(shí)現(xiàn): 代碼實(shí)現(xiàn)如圖7.2所示。 圖7.2 單例 驗(yàn)證一下,如果結(jié)果為true,就說(shuō)明是一個(gè)對(duì)象,如圖7.3所示。 圖7.3 單例 單例模式第二種實(shí)現(xiàn)形式 這種方式是對(duì)象的延遲加載方式 前面寫的直接new的方式是類一加載,這個(gè)對(duì)象就進(jìn)內(nèi)存了,而第二種實(shí)現(xiàn)方式只有在調(diào)用getInstance的時(shí)候才會(huì)創(chuàng)建對(duì)象。也就是什么時(shí)候使用,什么時(shí)候創(chuàng)建。 這一種對(duì)內(nèi)存空間的利用還是比較好的。 不過(guò)一般我們使用單例的時(shí)候,肯定是要使用這個(gè)對(duì)象,晚一點(diǎn)加載對(duì)我們來(lái)說(shuō),其實(shí)意義不是很大。 所以在工作中還是第一種用的比較多。 第二種雖然看起來(lái)比較好,但是會(huì)存在一些問(wèn)題,如果在多線程并發(fā)訪問(wèn)的時(shí)候會(huì)有一些安全隱患。這個(gè)等后面講到多線程的時(shí)候再詳細(xì)解釋。 開(kāi)發(fā)用第一種,面試用第二種。 第二種實(shí)現(xiàn)如圖7.4所示。 圖7.4 第二種單例實(shí)現(xiàn) 為了區(qū)分這兩種方式。 前面學(xué)習(xí)的第一種方式是餓漢式。//這個(gè)家伙比較餓,一加載就創(chuàng)建,吃掉。 第二種稱為懶漢式。//這家伙比較懶,什么時(shí)候用,什么時(shí)候再創(chuàng)建。 一般面試的時(shí)候會(huì)這樣問(wèn) 請(qǐng)寫一個(gè)單例延遲加載的例子,這個(gè)就需要用到懶漢式。 |
|
來(lái)自: 大數(shù)據(jù)徐葳 > 《java》