Java SE 學習---內存管理&函數參數傳遞

  • Java的內存管理

Java虛擬機管理的存儲空間在邏輯上可以分爲兩個區域:1)棧區域 2)堆區域。在這兩個區域上存儲的數據也不一樣

  1. 棧區用來存儲基本類型的變量以及對象的引用變量,當程序的邏輯超出了存儲在這個區域的變量的作用域之後,Java虛擬機會自動釋放掉這些變量所佔用的存儲空間
  2. 堆區用來存儲由 new 創建的對象和數組由 Java 虛擬機的自動垃圾回收器來管理,在Java中在堆中產生了一個數組或者對象之後,在棧中定義一個特殊的變量,讓棧中的這個變量的取值等於數組或對象在堆內存中的首地址,棧中的這個變量就成了數組或對象的引用變量,以後就可以在程序中使用棧中的引用變量來訪問堆中的數組或者對象,引用變量就相當於是爲數組或者對象起的一個名稱。

具體情況下面舉例說明:

public class Student { 
  String stuId; 
  String stuName; 
  int stuAge; 
} 
public class TestStudent { 
  public static void main(String[] args) { 
    Student zhouxingxing = new Student(); 
    String name = new String("旺旺");  
    int a = 10; 
    char b = 'm'; 
    zhouxingxing.stuId = "9527"; 
    zhouxingxing.stuName = "周星星"; 
    zhouxingxing.stuAge = 25; 
  } 
}
在上面的代碼中
 Student zhouxingxing = new Student();
定義了一個指向Student的引用類型變量zhouxingxing並將它的值設爲new Student()對象在堆中的首地址

String name = new String("旺旺");  
同樣指向String的引用類型變量name的值設爲對象 new String("旺旺")在堆中的首地址

int a = 10;  char b = 'm'; 
a和b爲基本類型的變量他們的存儲空間位於棧區,他們存儲在爲其分配的空間的內容就是他們的值。

下圖是上述例子的內存分佈圖:

引用類型中的數組也封裝了指針,即便是基本數據類型的數組也封裝了指針,數組也是引用類型。比如代碼int[] arr = new int[]{23,2,4,3,1};如下圖所示:

  • Java的函數的參數傳遞

關於Java的函數的參數傳遞的問題,有人認爲Java調用方法時向方法傳遞的是變量的值,有人認爲傳遞的是引用(即指針)。其實我認爲這兩種說法都有理,引用類型的變量存的其實就是一個對象或者數組的地址(本質上也使用幾個字節的空間存儲的數值)這和基本類型的變量存儲的值沒有什麼區別只是表示的含義不一樣而已,因此我們可以認爲Java的參數傳遞方式是“值”傳遞,說是引用傳遞也說得過去,因爲在Java中自定義類型變量的使用頻率遠遠超出了基本類型的變量,只不過基本類型的變量的值是一種特殊的“引用”它並不是指向堆中某一塊存儲區域的地址而就是其要表示的數值本身。

舉一個例子:

class A{
...
}
void method(A param){
...
}

void method(int param){
...
}

A a=new A();
int test=12;
method(a);
method(test);
當調用method(A param)時,A類型的實參a將指向的對象的地址傳遞給形參,在方法method(A param)中對形參的修改(這裏的修改指的是對其指向的堆空間的對象數據的修改)都會改變實參指向的空間的值。而當調用method(int param)時形參(基本類型的變量int test)將自己的值傳遞給形參,形參會在棧中拷貝一份實參的值,在方法內部對形參的修改將不會對實參的值造成任何的影響。

關於Java的參數傳遞有一個特殊的情況,就是傳遞String類型的變量時,形參的改變並不會對實參造成改變

例如:

public class TestString { 
  public static void main(String[] args) { 
     
    String name = "wangwang"; 
    TestString testString = new TestString(); 
     
    System.out.println("方法調用前:" + name); 
    testString.change(name); 
    System.out.println("方法調用後:" + name); 
  } 
   
  void change(String str) { 
    str = "旺旺老師"; 
    System.out.println("方法體內修改值後:" + str); 
  } 
} 

結果:
方法調用前:wangwang 
方法體內修改值後:旺旺老師 
方法調用後:wangwang 

分析:我們看初始情況,即String name = "wangwang";這行代碼運行
完,如下圖:
JAVA內存管理 - 小白 - 小白的博客

 當調用方法時testString.change(name),內存變化爲:

JAVA內存管理 - 小白 - 小白的博客


在方法體內,參數str賦予一個新值,str = "旺旺老師"。由於String是final類型的對象一經創建之後就不允許對其進行修改,當然就不會允許將str(地址爲36DF)的空間的值設置爲“旺旺老師”,在這種情況下系統就會在堆中分配一塊新的內存空間37DF並將37DF這塊內存區域的值設置爲“旺旺老師”,並將str指向這塊內存區域,而name還是指向36DF, 37DF的改變對它已沒影響:

JAVA內存管理 - 小白 - 小白的博客

最後,方法調用結束,str與37DF的內存空間消亡。Name的值依然爲wangwang,並沒有改變。
所以String雖然是引用類型參數,但值依然不變:


關於String類型更多的分析見這篇blog

最後是一個綜合的例子:

public class TestChange { 
  void change(Student stu1, Student stu2) { 
    stu1.stuAge ++; 
    stu2.stuAge ++; 
    Student stu = stu1; 
    stu1 = stu2; 
    stu2 = stu; 
  } 
   
  public static void main(String[] args) { 
     
    Student furong = new Student(); 
    furong.stuName = "芙蓉姐姐"; 
    furong.stuAge = 30; 
     
    Student fengjie = new Student(); 
    fengjie.stuName = "鳳姐"; 
    fengjie.stuAge = 26; 
     
    TestChange testChange = new TestChange(); 
    testChange.change(furong, fengjie); 
     
    System.out.println(furong.stuName); 
    System.out.println(furong.stuAge); 
     
    System.out.println(fengjie.stuName); 
    System.out.println(fengjie.stuAge); 
  } 
 
} 
運行結果:
芙蓉姐姐 
31 
鳳姐 
27 
分析:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章