Java零基礎(chǔ)快速入門|方法(下)

本篇文章主要內(nèi)容:
方法重載/overload
方法遞歸
難點(diǎn)解惑
方法重載/overload
關(guān)于方法重載是什么,以及怎么進(jìn)行重載,這些我們目前先不去研究,先來(lái)看看以下代碼不使用方法重載機(jī)制,存在哪些缺點(diǎn)?

運(yùn)行結(jié)果如下圖所示:

我們可以看到以上三個(gè)方法功能“相似”,都是求和,只不過(guò)參與求和的數(shù)據(jù)類型不同, 因此定義了三個(gè)方法,分別起了三個(gè)不同的方法名。這種方式會(huì)增加程序員編程的壓力,因?yàn)?程序員起碼要記憶三個(gè)方法名,另外代碼也不是很美觀。怎么解決呢?我們來(lái)看看使用方法重 載機(jī)制之后會(huì)是怎樣,請(qǐng)看以下代碼以及運(yùn)行結(jié)果:

那么,什么是方法重載呢?
方法重載(overload)是指在一個(gè)類中定義多個(gè)同名的方法, 但要求每個(gè)方法具有不同的參數(shù)的類型或參數(shù)的個(gè)數(shù)。調(diào)用重載方法時(shí),Java?編譯器能通過(guò)檢查調(diào)用的方法的參數(shù)類型和個(gè)數(shù)選擇一個(gè)恰當(dāng)?shù)姆椒?。方法重載通常用于創(chuàng)建完成一組任務(wù)相似但參數(shù)的類型或參數(shù)的個(gè)數(shù)不同的方法。調(diào)用方法時(shí)通過(guò)傳遞給它們的不同個(gè)數(shù)和類型的實(shí)參來(lái)決定具體使用哪個(gè)方法。
什么情況下我們考慮使用方法重載呢?
在同一個(gè)類當(dāng)中,如果多個(gè)功能是相似的,可以考 慮將它們的方法名定義的一致,使用方法重載機(jī)制,這樣便于程序員的調(diào)用,以及代碼美觀, 但相反,如果兩個(gè)方法所完成的功能完全不同,那么方法名也一定要不一樣,這樣才是合理的。
代碼滿足什么條件的時(shí)候構(gòu)成方法重載呢?滿足以下三個(gè)條件:
在同一個(gè)類當(dāng)中。
方法名相同。
參數(shù)列表不同:個(gè)數(shù)不同算不同,順序不同算不同,類型不同也算不同。
接下來(lái)我們來(lái)看看以下程序哪些方法構(gòu)成了方法重載,哪些沒(méi)有:

編譯結(jié)果如下圖所示:

通過(guò)觀察以上代碼以及測(cè)試結(jié)果我們得知,方法5和方法4是一樣的,這不是方法重載,?這叫“方法重復(fù)(哈哈)”,因?yàn)橹拔覀兙驼f(shuō)過(guò)方法形參中起決定性作用的是參數(shù)的數(shù)據(jù)類型,參數(shù)的名字隨意,因?yàn)槊恳粋€(gè)形參都是局部變量,變量名自然是隨意的。其中方法6和方法1相同,顯然方法的重載和方法的返回值類型沒(méi)有關(guān)系,這也是合理的,因?yàn)橹拔覀兲徇^(guò),方法執(zhí)行結(jié)束之后的返回值我們可以接收也可以不接收。另外方法7和方法1也無(wú)法構(gòu)成重載, 顯然方法重載和修飾符無(wú)關(guān)。
總之,方法1和方法2要想構(gòu)成方法重載,首先它們?cè)谕粋€(gè)類當(dāng)中,方法名一樣,參數(shù)列表不同(類型、個(gè)數(shù)、順序),這樣 java 虛擬機(jī)在運(yùn)行的時(shí)候就可以分清楚去調(diào)用哪個(gè)方法了。其實(shí),最終要調(diào)用哪個(gè)方法,還是取決于調(diào)用的時(shí)候傳遞的實(shí)際參數(shù)列表。所以在 java 編程中要區(qū)分兩個(gè)方法,首先看方法名,如果方法名一致,則繼續(xù)看它們的形式參數(shù)列表。
接下來(lái)我們來(lái)看一下方法重載在實(shí)際開發(fā)中的應(yīng)用,你有沒(méi)有覺(jué)得每一次輸出的時(shí)候“System.out.println();”這行代碼很麻煩,我們來(lái)封裝一個(gè)工具類,請(qǐng)看以下代碼:



運(yùn)行結(jié)果如下圖所示:

看到以上的代碼,你是不是感覺(jué)以后要打印數(shù)據(jù)到控制臺(tái)就很方便了,代碼再也不需要寫這么多“System.out.println();”,你只需要“U.p();”,當(dāng)然,你需要把U.java 文件編譯生成的U.class?文件拷貝到你的硬盤當(dāng)中,一直攜帶著,什么時(shí)候需要的話,把U.class 文件放到classpath 當(dāng)中就可以使用了。
?
方法遞歸
什么是方法遞歸?我們先來(lái)看一段代碼:

以上代碼的執(zhí)行結(jié)果如下圖所示:

我們可以看到以上代碼的執(zhí)行過(guò)程中,一直輸出“m?begin”,“m?over”一次也沒(méi)有輸出, 直到最終發(fā)生了錯(cuò)誤:java.lang.StackOverflowError,這個(gè)錯(cuò)誤是棧內(nèi)存溢出錯(cuò)誤,錯(cuò)誤發(fā)生后, JVM?退出了,程序結(jié)束了。
實(shí)際上以上代碼在 m()方法執(zhí)行過(guò)程中又調(diào)用了 m()方法,方法自身調(diào)用自身,這就是方法遞歸調(diào)用。以上程序?qū)嶋H上等同于以下的偽代碼(說(shuō)明問(wèn)題,但是無(wú)法執(zhí)行的代碼):

通過(guò)偽代碼我們可以看出,m()方法一直在被調(diào)用(方法中的代碼必須遵循自上而下的順 序依次逐行執(zhí)行,不能跳行執(zhí)行),對(duì)于棧內(nèi)存來(lái)說(shuō)一直在進(jìn)行壓棧操作,m()方法從未結(jié)束 過(guò),所以沒(méi)有彈棧操作,即使棧內(nèi)存足夠大(也是有限的內(nèi)存),總有一天棧內(nèi)存會(huì)不夠用的, 這個(gè)時(shí)候就會(huì)出現(xiàn)棧內(nèi)存溢出錯(cuò)誤。通過(guò)以上研究得出遞歸必須要有合法的結(jié)束條件,沒(méi)有結(jié) 束條件就一定會(huì)發(fā)生StackOverflowError。我們?cè)賮?lái)看看有結(jié)束條件的遞歸,例如以下代碼:

綜上所述,遞歸其實(shí)就是方法在執(zhí)行的過(guò)程中調(diào)用了另一個(gè)方法,而另一個(gè)方法則是自己本身。在代碼角度來(lái)看就是在a()方法中調(diào)用a()方法,使用遞歸須謹(jǐn)慎,因?yàn)檫f歸在使用的時(shí)候必須有結(jié)束條件,沒(méi)有結(jié)束條件就會(huì)導(dǎo)致無(wú)終止的壓棧,棧內(nèi)存最終必然會(huì)溢出,程序因錯(cuò)誤的發(fā)生而終止。
大家再來(lái)思考一個(gè)問(wèn)題,一個(gè)遞歸程序有合法有效的結(jié)束條件就一定不會(huì)發(fā)生棧內(nèi)存溢出錯(cuò)誤嗎?在實(shí)際開發(fā)中遇到這個(gè)錯(cuò)誤應(yīng)該怎么辦?
一個(gè)遞歸程序有的時(shí)候存在合法有效的終止條件,但由于遞歸的太深,在還沒(méi)有等到條件 成立的時(shí)候,棧內(nèi)存已經(jīng)發(fā)生了溢出,這種情況也是存在的,所以實(shí)際開發(fā)中我們盡可能使用 循環(huán)來(lái)代替遞歸算法,原則是:能不用遞歸盡量不用,能用循環(huán)代替的盡可能使用循環(huán)。當(dāng)然, 如果在開發(fā)中遇到了由于使用遞歸導(dǎo)致棧內(nèi)存溢出錯(cuò)誤的發(fā)生,首先,我們要檢查遞歸的終止 條件是否合法,如果是合法的還是發(fā)生棧內(nèi)存溢出錯(cuò)誤,那么我們可以嘗試調(diào)整堆??臻g的大 小。怎么調(diào)整堆棧大小呢,大家可以研究一下下圖中的一些參數(shù),這里就不再講解內(nèi)存大小的 調(diào)整了,這不是初級(jí)程序員應(yīng)該掌握的。

接下來(lái)我們來(lái)研究一下在不使用遞歸的前提下,完成 1~N 的求和,這個(gè)應(yīng)該很簡(jiǎn)單,請(qǐng)看下面代碼:

運(yùn)行結(jié)果如下圖所示:

那么,使用遞歸應(yīng)該怎么寫呢?請(qǐng)看以下代碼:

運(yùn)行結(jié)果如下圖所示:

我們來(lái)使用偽代碼對(duì)以上代碼的執(zhí)行過(guò)程進(jìn)行分析,請(qǐng)看以下偽代碼:


?以上程序的內(nèi)存變化是這樣的,請(qǐng)看下圖:

為了加強(qiáng)大家對(duì)遞歸算法的理解,我們?cè)賮?lái)看一張圖:

其實(shí)大家把上圖逆時(shí)針旋轉(zhuǎn) 90 度,你會(huì)看到一個(gè)棧數(shù)據(jù)結(jié)構(gòu)對(duì)嗎?
?
難點(diǎn)解惑
對(duì)于本章節(jié)內(nèi)容來(lái)說(shuō),要求理解的內(nèi)容較多,其中包括兩個(gè)難點(diǎn):
方法執(zhí)行過(guò)程中內(nèi)存的變化;
遞歸算法
其實(shí)只要對(duì)方法執(zhí)行過(guò)程中內(nèi)存的變化有很深入的理解,所有方法執(zhí)行過(guò)程中內(nèi)存變化都能畫出來(lái),遞歸算法也就理解了。
對(duì)于畫內(nèi)存圖在這里囑咐大家?guī)讉€(gè)關(guān)鍵點(diǎn),以幫助你掌握方法的內(nèi)存變化以及對(duì)遞歸的理解:
方法體當(dāng)中的代碼必須遵循自上而下的順序依次逐行執(zhí)行,當(dāng)前行代碼不結(jié)束,下一行代碼 是絕對(duì)不會(huì)執(zhí)行的;
一定要理解棧數(shù)據(jù)結(jié)構(gòu)的原理是遵循先進(jìn)后出、后進(jìn)先出 原則,每當(dāng)方法調(diào)用時(shí)分配空間,此時(shí)“進(jìn)”棧,每當(dāng)方法執(zhí)行結(jié)束時(shí)釋放空間,此時(shí)“出” 棧。永遠(yuǎn)都是最后執(zhí)行的方法最先結(jié)束,最先執(zhí)行的方法最后結(jié)束。
小結(jié)
本章節(jié)內(nèi)容主要講解了?Java?中方法相關(guān)的語(yǔ)法機(jī)制,其實(shí)這個(gè)在?C?語(yǔ)言中被稱為函數(shù),?一般在開發(fā)中我們都會(huì)把獨(dú)立的功能單獨(dú)定義成一個(gè)方法,在需要的時(shí)候調(diào)用就行了,這樣可以讓寫過(guò)的代碼得到重復(fù)利用。
本章節(jié)中對(duì)于方法的作用是需要大家理解的,對(duì)于方法的定義與調(diào)用是需要大家必須掌握?的,另外要求大家對(duì)方法的形參和返回值存在的意義有深刻的理解。還有大家要想徹底掌握Java??程序中的方法,還需要對(duì)方法執(zhí)行過(guò)程中內(nèi)存的變化有深刻的理解。另外還要掌握方法的重載機(jī)制,以及方法的遞歸算法。尤其是遞歸時(shí)的內(nèi)存變化有助于你對(duì)遞歸執(zhí)行原理的理解。
最后附Java零基礎(chǔ)視頻教程給大家,配合學(xué)習(xí)效果更佳?。?/p>