上幾篇文章我們討論了類、成員變量、成員方法、構(gòu)造方法,對(duì)于成員變量來(lái)說(shuō),它還分為實(shí)例變量和靜態(tài)變量,成員方法也分普通方法和靜態(tài)方法。聲明方法的格式里有個(gè)東西叫“訪問(wèn)修飾符”,當(dāng)時(shí)沒(méi)細(xì)講,這篇我們就詳細(xì)聊聊它。
之前我們寫的程序都有這么一個(gè)特點(diǎn):變量前什么都不寫,方法前寫個(gè)public。那這個(gè)public是什么意思呢?從字面上理解,public英文是“公有的”,是一種訪問(wèn)修飾符。其實(shí)在java里,訪問(wèn)修飾符除了公有public, 還有另外三類,分別是受保護(hù),英文protected; 默認(rèn),不寫任何修飾符就代表默認(rèn); 以及私有,英文private。怎么理解這些東西呢?大家都知道,每個(gè)國(guó)家都有機(jī)密文件,這些機(jī)密文件被妥善的保護(hù),看這些文件的人同樣得是身份很特別的人,甭說(shuō)外國(guó)人了,本國(guó)人都不能隨意瀏覽。而且文件上會(huì)貼有類似“絕密”“無(wú)保密”“內(nèi)部”等標(biāo)簽。訪問(wèn)修飾符就可以想象成這種給予文件權(quán)限的標(biāo)簽:
下面我就帶著大家一個(gè)一個(gè)總結(jié)它們的特點(diǎn)。先說(shuō)public。創(chuàng)建一個(gè)叫Test.java的文件,然后復(fù)制粘貼以下代碼:
假設(shè)我現(xiàn)在有一個(gè)國(guó)家機(jī)密文件,里面寫著國(guó)家目標(biāo)和國(guó)家計(jì)劃。我創(chuàng)建了一個(gè)叫Test的類代表該文件,目標(biāo)用變量countryGoal表示,計(jì)劃用countryPlan方法表示。修飾符其實(shí)不光可以修飾方法,還可以修飾變量,甚至是類。我現(xiàn)在只給變量和方法前面都加上了public,對(duì)類暫時(shí)用不著,因?yàn)橹挥袔讉€(gè)類不在同一文件里時(shí)類修飾符才有用。然后我在main函數(shù)里聲明出了一個(gè)對(duì)象p1,雖然它只是Test的一個(gè)對(duì)象,但為了便于理解大家可以把它暫時(shí)當(dāng)成一個(gè)人。p1嘗試訪問(wèn)國(guó)家目標(biāo)和國(guó)家計(jì)劃,看他/她是否能成功:
最后一行原來(lái)是“100年內(nèi)我大天朝牛b哄哄”改成了“100年內(nèi)我大中華四夷賓服”,因?yàn)橛X(jué)得把b這個(gè)字形容自己的祖國(guó)再怎么樣聽(tīng)起來(lái)也不雅,后面的截圖我懶得改了,大家知道就行了。沒(méi)問(wèn)題,這個(gè)人既可以訪問(wèn)countryGoal,又可以訪問(wèn)countryPlan。所以,我們第一個(gè)結(jié)論就是:當(dāng)成員變量或成員方法是public的時(shí)候,對(duì)象又和它們?cè)谕愔?,是可以隨意被訪問(wèn)的。就好像是權(quán)限很低的文件,可以隨便看。那不同類中呢?
我把代表文件的那部分代碼挪到了Document類中,在Test里聲明出了一個(gè)Document對(duì)象p1。這次p1再次嘗試訪問(wèn)國(guó)家目標(biāo)和國(guó)家計(jì)劃,看是否能成功:
沒(méi)問(wèn)題,p1還可以訪問(wèn)。如果這兩個(gè)類在不同文件里呢?之前我們?cè)陬?、成員變量、成員方法(3)那篇里曾演示過(guò)多個(gè)類在不同文件中的執(zhí)行,我也說(shuō)過(guò)好多時(shí)候其實(shí)都是一個(gè)類一個(gè)文件,只不過(guò)為了方便演示,咱們都放到了一起,僅僅如此?,F(xiàn)在咱們就演示一下,如果放在不同的文件中,pulic修飾符會(huì)有什么效果。演示之前升級(jí)一下我們的開(kāi)發(fā)工具。由于我們的例子比較簡(jiǎn)單,只需要兩個(gè)文件就夠了,一個(gè)放Document類,另一個(gè)放Test類。可如果這個(gè)項(xiàng)目很大呢?開(kāi)發(fā)目錄下是不是會(huì)產(chǎn)生好多個(gè).java文件?用記事本就不好管理了。所以從現(xiàn)在開(kāi)始,我們不再用記事本了,開(kāi)始用咱們第一篇最后說(shuō)到的那個(gè)工具-Eclipse。有時(shí)候我們聽(tīng)別人說(shuō)IDE這個(gè)詞,IDE就是integrated development environment,翻譯過(guò)來(lái)就是集成開(kāi)發(fā)工具。Eclipse既幫助我們寫代碼,也幫助我們維護(hù)代碼,功能很強(qiáng)大。而且這個(gè)工具是開(kāi)源的,咱們可以直接下載使用。當(dāng)然,市面上還有別的類似軟件,大家可以自行下載試試。
去官網(wǎng)下載最新版eclipse,安裝過(guò)程比較簡(jiǎn)單,我就不細(xì)說(shuō)了。雙擊執(zhí)行文件打開(kāi),一開(kāi)始需要你填workspace,也就是工作區(qū),意思就是你以后要把項(xiàng)目存到哪個(gè)文件夾下。如果一個(gè)工作區(qū)里還沒(méi)有任何項(xiàng)目,它會(huì)提示個(gè)Eclipse的歡迎界面,叉掉之后就是你的工作臺(tái):
默認(rèn)情況下左邊是你的項(xiàng)目列表,右邊是代碼區(qū),下面是運(yùn)行結(jié)果。當(dāng)然,你如果不喜歡這種布局可以修改。現(xiàn)在我們先新建個(gè)項(xiàng)目試試。因?yàn)槲沂怯⑽陌?,就按英文版的指令走:點(diǎn)擊左上角File -> New -> Project打開(kāi)項(xiàng)目對(duì)話框,選擇Java Project后點(diǎn)擊下一步:
項(xiàng)目名稱就輸入Documents吧,國(guó)家密檔嘛。
然后一直下一步到最后,項(xiàng)目就建完了,在這個(gè)過(guò)程中你能看到Eclipse會(huì)把jdk和jre加進(jìn)去。新建的項(xiàng)目比較空,只有src文件夾和jre library。
現(xiàn)在把Test和Document這兩個(gè)類加進(jìn)去,每個(gè)類單獨(dú)占一個(gè)文件,所以我們要新建兩個(gè).java文件。右鍵點(diǎn)擊src -> new ->class打開(kāi)類對(duì)話框,類名是Document:
看見(jiàn)上圖名稱下面還有個(gè)修飾符那一欄嗎?默認(rèn)它是public的,點(diǎn)擊確定會(huì)創(chuàng)建一個(gè)叫Document.java的文件,并且類名為Document,修飾符是public:
剛才說(shuō)了,如果幾個(gè)類都在同一個(gè)文件里那沒(méi)必要用類修飾符。但現(xiàn)在Document和Test不在同一個(gè)文件里了,那么它也要受到修飾符的控制。同樣步驟創(chuàng)建Test.java,不過(guò)勾選上main函數(shù),代表它是主類:
現(xiàn)在的項(xiàng)目是這個(gè)樣子:
我們發(fā)現(xiàn)在項(xiàng)目和java文件中間多了一層,上面寫著(default package)這一層是什么呢?我并沒(méi)有建這么個(gè)東西呀?這就引來(lái)了我們這篇文章的第二個(gè)概念–包,英文叫package。在實(shí)際的自動(dòng)化測(cè)試項(xiàng)目里,有些文件是為了儲(chǔ)存網(wǎng)頁(yè)上的控件,有些文件是為了處理邏輯需求,有些文件是為了連接數(shù)據(jù)庫(kù),功能各不相同,所以即便你把各種類區(qū)分開(kāi)寫了很多文件,那也得需要根據(jù)不同的職責(zé)來(lái)管理這些文件。咱們的操作系統(tǒng)里都用文件夾來(lái)管理文件,而包就相當(dāng)于java里的文件夾。所以,java使用包的概念來(lái)管理類、文件、模塊或功能。之前說(shuō)java其中一個(gè)重要部分叫做類庫(kù),本質(zhì)上就是包的一種應(yīng)用。
如果在創(chuàng)建文件前沒(méi)創(chuàng)建包,eclipse會(huì)自動(dòng)補(bǔ)上一個(gè)默認(rèn)的包,名字就叫default。包也有自己的命名規(guī)則,一般是按照“所在公司的互聯(lián)網(wǎng)域名.公司名.項(xiàng)目名.模塊名”這種格式,每個(gè)字段首字母為小寫,字段之間用點(diǎn)隔開(kāi)。比如我要為阿里巴巴的淘寶做項(xiàng)目,我可以把負(fù)責(zé)測(cè)試的模塊寫到一個(gè)包里,包名就是com.alibaba.taobao.tests。
和類名首字母大寫或是變量方法首字母小寫一樣,包的命名規(guī)則也是個(gè)約定俗成的東西,你說(shuō)我偏不遵守,也沒(méi)什么,只不過(guò)顯得有點(diǎn)不專業(yè),但程序執(zhí)行上沒(méi)問(wèn)題。關(guān)于包還有些東西要說(shuō),下篇文章我會(huì)用字符串舉例說(shuō)說(shuō)類庫(kù),到時(shí)大家更能體會(huì)到包的作用。
來(lái),咱們把包名改一下,我也不想寫得特別復(fù)雜,就用com.test吧,意思到了就行,最后做項(xiàng)目實(shí)戰(zhàn)時(shí)我會(huì)寫得規(guī)范點(diǎn)。右鍵點(diǎn)擊src -> New -> Package打開(kāi)包對(duì)話框,輸入com.test,點(diǎn)擊確定。
之后我們會(huì)發(fā)現(xiàn)com.test并沒(méi)有把(default package)取代,沒(méi)關(guān)系,手動(dòng)把Document.java和Test.java拖拽進(jìn)去。拖完會(huì)驚奇地發(fā)現(xiàn)(default package)不見(jiàn)了:
現(xiàn)在把剛才用記事本寫的程序復(fù)制粘貼進(jìn)去:
這兩個(gè)文件都在com.tests包下,現(xiàn)在右鍵點(diǎn)擊Test.java -> Run As -> Java Application執(zhí)行程序,注意一定要在主類上執(zhí)行,否則找不到main函數(shù):
運(yùn)行結(jié)果顯示正確,沒(méi)有報(bào)錯(cuò),證明同包下public修飾的變量和方法也可以被訪問(wèn)。那現(xiàn)在就能得出一個(gè)結(jié)論了:不管在同類還是同包不同類,public修飾的變量和方法都是可以被訪問(wèn)的。
第二個(gè)說(shuō)protected?,F(xiàn)在把Document.java里的countryGoal變量和countryPlan方法都改成protected,Test.java不變,然后運(yùn)行程序,發(fā)現(xiàn)還是通過(guò)的:
既然在不同文件都可以通過(guò),那相同文件相同類肯定也沒(méi)問(wèn)題。所以我又得出一個(gè)結(jié)論:不管在同類,還是同包不同類,protected修飾的變量和方法也都是可以被訪問(wèn)的。暫時(shí)還沒(méi)看出來(lái)public和protected的區(qū)別,不過(guò)別著急,咱們繼續(xù)看。
Protected試完咱們?cè)囋嚹J(rèn)的。把Document.java里的protected移走,什么都不加,就是默認(rèn)的。Test.java保持原樣。運(yùn)行,還是通過(guò):
這結(jié)論不用我說(shuō)也知道吧?不管在同類,還是同包不同類,默認(rèn)修飾的變量和方法也都是可以被訪問(wèn)的。有人開(kāi)始不耐煩了,你tm這是在逗我吧?息怒,一會(huì)兒就不同了。
最后用private:
看到了么?Test.java已經(jīng)提示錯(cuò)誤了: countryPlan方法和countryGoal變量都是invisible。什么意思?主函數(shù)看不到它們!用對(duì)象調(diào)用的時(shí)候雙雙不可訪問(wèn),這就是private私有的意思,屏蔽了,修飾符終于開(kāi)始發(fā)威了。順便說(shuō)一句,即使報(bào)錯(cuò)也是Eclipse勝過(guò)記事本的地方。記事本不會(huì)有什么紅波浪線提示代碼有誤,所以一段復(fù)雜的程序出了問(wèn)題用記事本有時(shí)很難排錯(cuò)。
這是同包不同類的情況。那我回退一步,試試同類。咱不嫌麻煩,我?guī)е蠹乙粋€(gè)一個(gè)地驗(yàn)證,通過(guò)程序的變化大家體驗(yàn)不同修飾符的區(qū)別。把Document.java文件里內(nèi)容再挪到Test.java里,現(xiàn)在Test.java就是這個(gè)樣子:
這回不報(bào)錯(cuò)了,而且執(zhí)行通過(guò)。看來(lái)如果在同類中,這個(gè)private也沒(méi)什么威力了。那咱們第四個(gè)結(jié)論就出來(lái)了,用private的時(shí)候,同類可訪問(wèn),同包但不同類的情況下不能訪問(wèn)。
剛才演示的都是同類或同包不同類,那如果不同包呢?不同包時(shí)public, private, protected和默認(rèn)會(huì)怎樣處理呢?還是從public開(kāi)始。我們?cè)賱?chuàng)建一個(gè)包叫com.document,然后把Document.java拖拽進(jìn)去。此時(shí)兩個(gè)文件的內(nèi)容修改成下面的樣子:
主類Test.java: 創(chuàng)建Document對(duì)象用來(lái)訪問(wèn)變量和方法
第二個(gè)類Document.java: 聲明變量和方法用于被訪問(wèn)
這時(shí)Eclipse又提示Test.java出錯(cuò)了,問(wèn)咱們是不是需要import document:
Import的意思是導(dǎo)入,這里面是導(dǎo)入另一個(gè)包的信息。為什么需要導(dǎo)入呢?這就是這篇文章的第三個(gè)概念:如果java程序分散在不同包中,一個(gè)包中的類想要訪問(wèn)另一個(gè)包中類的內(nèi)容則必須導(dǎo)入。導(dǎo)入其實(shí)就是把別的包的程序?qū)У轿疫@里來(lái),導(dǎo)進(jìn)來(lái)了不就相當(dāng)于在同一個(gè)包里了嘛。選擇導(dǎo)入,這時(shí)會(huì)在Test.java文件頭部多加了import com.document.Document這么一行,同時(shí)錯(cuò)誤信息也不見(jiàn)了,表明導(dǎo)入成功。現(xiàn)在再運(yùn)行一下,看看public修飾符還允許不允許通過(guò)。告訴大家吧,還是通過(guò),public等級(jí)非常低。所以對(duì)于public來(lái)說(shuō),同類,同包不同類,不同包,都可以訪問(wèn)。其實(shí)這也是public本身的涵義。公有嘛,誰(shuí)都可以訪問(wèn)。
現(xiàn)在把public改成protected。我們發(fā)現(xiàn)這次不行了,即便包都倒進(jìn)去了還是提示不可見(jiàn)。試試默認(rèn)呢?也不行,封得死死的。我們感覺(jué)訪問(wèn)修飾符在不同包的情況下威力非常大,各種不可見(jiàn):
至于private,其實(shí)壓根不用試,肯定不行,同包不同類都不行,不同包肯定更不行了。所以,再把不同包的情況包括進(jìn)去,咱們的結(jié)論就變成這張表:
變量和方法都遵循這個(gè)表上的規(guī)則,那類本身呢?也遵守,大家可以課后去實(shí)踐一下。這個(gè)結(jié)論不用死記,忘了的話就用我這個(gè)方法驗(yàn)證一下就行了。
剛才的所有例子中countryGoal和countryPlan都是同時(shí)擁有相同的修飾符的,要么都是public,要么都是private。如果我讓它們不一樣呢?比如變量countryGoal是private的,countryPlan方法是public的。很明顯前者在不同包情況下不可見(jiàn),后者各種可見(jiàn)。如果我非要在不同包的情況下訪問(wèn)變量countryGoal呢?有沒(méi)有辦法?現(xiàn)在我把這兩個(gè)文件寫成下面這樣:
我把countryPlan方法里面加了個(gè)字符串返回值,而且返回的正是countryGoal變量。現(xiàn)在執(zhí)行程序,我們驚奇地發(fā)現(xiàn)用private修飾的countryGoal的結(jié)果被打印出來(lái)了,它竟然可以被訪問(wèn):
等等,仔細(xì)再看看程序,countryGoal現(xiàn)在是在哪兒被訪問(wèn)的?是不是countryPlan方法里邊?這么做意味著它不再被Test.java中的Document對(duì)象直接訪問(wèn)了,而是間接通過(guò)countryPlan方法。countryPlan方法和countryGoal變量在同一個(gè)類中,因?yàn)橥愔衟rivate是可見(jiàn)的,所以程序沒(méi)問(wèn)題。“方法公有但變量私有,然后通過(guò)方法訪問(wèn)變量”這種做法非常普遍,咱們一定要記住。不過(guò)現(xiàn)在記不住也沒(méi)關(guān)系,到時(shí)候我教大家寫項(xiàng)目的時(shí)候會(huì)經(jīng)常用這種寫法。所以,對(duì)于訪問(wèn)修飾符,我們一定要看這個(gè)變量或是方法到底在哪兒被訪問(wèn)的,是同類?還是同包?還是不同包?
那有人說(shuō)你講這個(gè)訪問(wèn)修飾符是干什么的?有什么意義?這就引出了我們這篇文章最后一個(gè)知識(shí)點(diǎn) – 封裝。前幾篇我說(shuō)得最多的詞就是類,對(duì)象,還是對(duì)象。不管用王思聰咪蒙舉例也好,還是自動(dòng)化的信息配置也好,它們都是對(duì)象,對(duì)不對(duì)?所以,java是一種面向?qū)ο蟮恼Z(yǔ)言,一切都是圍繞對(duì)象展開(kāi)。以前我學(xué)java的時(shí)候老師剛講類就直接把面向?qū)ο筮@個(gè)概念拋出來(lái),又加上是英文授課,一開(kāi)始一頭霧水,后來(lái)因?yàn)槔蠋煯吘顾礁撸也怕愣?。所以我講的時(shí)候就先帶大家寫幾個(gè)程序,由淺入深慢慢理解對(duì)象這個(gè)概念,這時(shí)再拋出面向?qū)ο蟠蠹依斫馄饋?lái)就應(yīng)該容易多了。我不知道你們學(xué)沒(méi)學(xué)過(guò)別的語(yǔ)言,尤其是C語(yǔ)言,它被稱為面向過(guò)程語(yǔ)言,沒(méi)有類和對(duì)象這個(gè)概念的,沒(méi)有java方便,這里咱們就不討論了。
面向?qū)ο笳Z(yǔ)言有三大特征,而封裝就是第一個(gè)特征。你看,這幾種訪問(wèn)修飾符,是不是感覺(jué)像塑料袋一樣把里面的內(nèi)容封住,或是像墻一樣把訪問(wèn)者拒之門外?拒之門外的目的就是為了保證代碼的安全性,因?yàn)樽鲰?xiàng)目的時(shí)候不是所有人都可以對(duì)一些特定文件里的類,方法,或是變量隨意進(jìn)行訪問(wèn)。封裝就是通過(guò)不同的訪問(wèn)修飾符來(lái)控制類,變量,方法的可見(jiàn)性,以達(dá)到保護(hù)代碼的目的。
這篇文章的源代碼是有10個(gè)小項(xiàng)目,分別在DocumentsForDefaultDiffPkgs,DocumentsForDefaultSamePkgDiffClasses,DocumentsForPrivateSameClass,DocumentsForPrivateSamePkgDiffClasses,DocumentsForPrivateValPublicMeth,DocumentsForProtectedDiffPkgs,DocumentsForProtectedSamePkgDiffClasses,DocumentsForPublicDiffPkgs,DocumentsForPublicSameClass,DocumentsForPublicSamePkgDiffClasses)
本篇知識(shí)點(diǎn)及注意事項(xiàng):
1. 訪問(wèn)修飾符結(jié)論: 1)不管在同類、同包不同類、不同包,public修飾的變量和方法都是可以被訪問(wèn)的;2)protected和default只可以在同類以及同包不同類之間訪問(wèn),兩者區(qū)別講繼承時(shí)說(shuō);3)private只可以在同類間訪問(wèn)。
2. java使用包的概念來(lái)管理類、文件、模塊或功能。
3. 如果java程序分散在不同包中,一個(gè)包中的類想要訪問(wèn)另一個(gè)包中類的內(nèi)容則必須導(dǎo)入,關(guān)鍵字是import。
4. “方法公有但變量私有,然后通過(guò)方法訪問(wèn)變量”這種做法非常普遍,對(duì)于訪問(wèn)修飾符,我們一定要看這個(gè)變量或是方法到底在哪兒被訪問(wèn)的,是同類?還是同包?還是不同包?