方法
- 方法產生的機制
- 某個功能代碼只需要寫一遍
- 要使用這個功能只需要給這個功能傳遞具體的數據
- 這個功能完成之後返回一個最終的結果
這樣代碼就可以重複利用,提高代碼複用性
- 在返回值類型是void的方法中使用 return 語句用於終止方法的執行
public static void fun(){
for(int i = 0; i < 6; i++){
if(i == 3)
return ; //此處的 return相當於 break
System.out.println("Hello");
}
}
方法執行內存分析
方法在執行過程當中,在JVM中的內存是如何分配的呢,內存是如何變化的?
- 方法只定義,不調用,是不會執行的,並且在JVM中也不會給該方法分配“運行所屬”的內存空間。只有在調用這個方法的時候,纔會動態地給這個方法分配所屬的內存空間。
- 在JVM內存劃分上有這樣三塊主要的內存空間(當然除了這三塊之外還有其他的內存空間):
方法區內存、堆內存、棧內存 - 數據結構棧的特點?
- 棧幀永遠指向棧頂元素
- 棧頂元素處於活躍狀態,其他元素靜止
- 術語: 壓棧/入棧/push ; 彈棧/出棧/pop
- 棧數據結構存儲數據的特點:先進後出,後進先出
- 方法代碼片段存在哪裏?方法執行的時候執行過程的內存在哪裏分配?
- 方法代碼片段屬於.class字節碼文件的一部分,字節碼文件在類加載的時候將其放到了
方法區內存
當中,所以JVM中的三塊主要的內存空間中方法區內存最先有數據。存放了代碼片段。 - 代碼片段雖然在方法區內存當中只有一份,但是可以被重複利用。每一次調用這個方法的時候,需要給該方法分配獨立的活動場所,在
棧內存
中分配。【棧內存中分配方法運行所屬的內存空間】
- 方法在調用的瞬間,會給該方法分配內存空間,會在棧中發生壓棧動作,方法執行結束之後,給該方法分配的內存空間全部釋放,此時發生彈棧動作。
- 壓棧:給方法分配內存
- 彈棧:釋放該方法的內存空間
- 局部變量在方法體中聲明,局部變量運行階段內存在棧中分配。
【案例1】
分析以下程序運行時的內存空間分配
public class MyTest {
public static void main(String[] args) {
int a = 10;
int b = 20;
int retValue = sumInt(a, b); //等於號先執行完右邊,然後分配retValue的內存空間
System.out.println("retValue = " + retValue);
}
public static int sumInt(int i, int j) {
int result = i + j;
int num = 3;
int retValue = divide(result, num);
return retValue;
}
public static int divide(int x, int y) {
int z = x / y;
return z;
}
}
分析:
- 按照程序自上而下執行的順序,查看哪些字節碼文件會被加載到方法區內存,分別是本類編譯後的字節碼文件,String.class以及System.class,三者依次加載到方法區內存中
- 在執行方法的時候給方法分配棧內存空間,爲局部變量分配內存空間。如main方法中的a和b變量,然後爲sumInt方法分配棧內存空間,依次爲i, j, result, num分配內存空間,然後分配divide的內存空間;繼續依次分配x, y和z的內存空間,return z;語句執行結束後分配sumInt方法中的retValue內存,執行完return retValue;語句後分配main方法中的retValue空間。
【案例2】
分析以下程序的內存空間分配
public class MyTest {
public static void main(String[] args) {
int i = 10;
method(i);
System.out.println("main -->" + i); // i=10
}
public static void method(int i) {
i++;
System.out.println("method -->" + i);// i=11
}
}
方法重載
- 方法重載又被稱爲:overload
- 什麼時候考慮使用方法重載?
- 功能相似的時候,儘可能讓方法名相同。但是:功能不同/不相似的時候,儘可能讓方法名不同。
- 什麼條件滿足之後構成了方法重載?
- 在同一個類中
- 方法名相同
- 參數列表不同:數量不同,順序不同,類型不同
- 方法重載和什麼有關係,和什麼沒有關係?
- 方法重載和方法名+參數列表有關係
- 方法重載和返回值類型無關
- 方法重載和修飾列表無關
方法遞歸
- 什麼是遞歸?
遞歸就是方法自身調用自身 - 遞歸是很耗費
棧內存
的,遞歸算法可以不用時儘量別用 - 棧內存溢出的結果是JVM停止工作
棧內存發生溢出錯誤,錯誤時無法挽回的,只有一個結果,就是JVM停止工作
以下程序會發生棧內存溢出錯誤(java.lang.StackOverflowError)【不是異常,是錯誤】
public static void main(String[] args) {
dosome();
}
public static void dosome() {
System.out.println("dosome begin");
dosome();
System.out.println("dosome end");
}
- 遞歸必須有結束條件,沒有結束條件一定會發生棧內存溢出錯誤
- 遞歸即使有了結束條件,即使結束條件是正確的,也有可能發生棧內存溢出錯誤,因爲遞歸的太深了。
【案例】
用遞歸實現求1+2+…+n
public static void main(String[] args) {
int n = 4;
System.out.println(doSum(n));
}
public static int doSum(int n){
if(n==1)
return 1;
else
return n+doSum(n-1);
}
//思路:
//n+sum(n-1)
//4+sum(3)
//4+3+sum(2)
//4+3+2+sum(1)
//4+3+2+1
上述程序的內存空間分配情況:
用一個更形象的圖來表示遞歸調用過程,就是如下圖所示,將該圖逆時針旋轉90度就是一個棧。