一、Java記憶體劃分
分為五個部分,可以參考這篇筆記簡單認識一下:
https://www.cnblogs.com/unleashed/p/13268027.html
棧 | 堆 | 方法區 | 本地方法棧 | 暫存器 |
---|
二、從陣列的記憶體說起
一個 陣列的記憶體圖
首先,我們有這樣一組程式碼:
1 public class HelloWorld{
2 public static void main(String[] args){
3 int[] array = new int[2];
4 System.out.println(array);
5 System.out.println(array[0]);
6 System.out.println(array[1]);
7 array[0]= 1;
8 array[1]= 2;
9 System.out.println(array);
10 System.out.println(array[0]);
11 System.out.println(array[1]);
}
}
看這張圖:
“.class”檔案裡面主要儲存的就是main方法, 而圖中過程就是 “進棧”的過程,並且為main方法開闢了一個新的空間。 繼續來看 |
---|
“int[] array” 左邊其實是堆當中陣列的地址值 所以“array”這個變數儲存的其實是陣列的地址值 然後根據地址進行尋找陣列 |
---|
輸出的時候,會自動找到陣列所有相關資訊 |
---|
當程式執行到賦值語句時 根據陣列的地址值找到陣列 並且找到索引位置進行修改數值 然後列印輸出的時候,又會重複此前的步驟,進行定址,取值 |
---|
兩個陣列的記憶體圖
1、新建陣列的情況
如果在剛才的main方法中新增一個這樣的語句
int[] array2 = new int[10];
此時,需要我們記住只要 new 了,它就會在堆當中 開闢出一個新的空間 ,也可以說是 新的記憶體空間
2、傳遞地址
如果新增的是這種語句呢?
int[] array2 = array;
此時,堆當中還是隻有那一個陣列,只是將 array 的地址值傳遞給 array2 ,,因為它們的地址值相等,當給 array2 賦值時,更改的內容就是原來 array 裡面的內容,也就是 : 兩個引用指向同一個陣列的情況
三、來看物件的記憶體
一個物件的記憶體圖
首先,還是得有一段程式碼
public class student{
String name;
String ssex;
int age;
public void study(){
System.out.println("正在學習。。。。");
}
public void eat(){
System.out.println("正在吃飯。。。。");
}
}
既然是物件,那就還得有一段程式碼,來使用這個student類
public class TestStudent{
public static void main(String[] args){
Student stu = new Student();
System.out.println(stu.name);
System.out.println(stu.ssex);
System.out.println(stu.age);
stu.name = "小杜";
stu.age = 20;
stu.ssex = "男";
System.out.println(stu.name);
System.out.println(stu.ssex);
System.out.println(stu.age);
stu.study();
stu.eat();
}
}
那就從圖看起來
java中執行程式,首先是從main方法開始執行的 所以它必須第一個進棧 |
---|
此處要 注意 !!! 當new Student()時 Student.class中的成員方法地址值會儲存在堆當中 所以要記住,對於引用型別,都是地址在傳遞 |
---|
所謂的stu.name 就是在呼叫成員變數,所以通過地址值來進行定址 找到之後就進行更改 比如後面的語句 stu.name = "小杜" ; |
---|
物件.成員方法 通過地址值找到所要找的內容 然後開始進棧 當方法執行完畢後就會出現 “彈棧” 然後執行下一條語句 在我們這段程式碼中,下一條語句還是呼叫成員方法 所以study方法執行玩之後就會被彈出 進行下一條指令 呼叫eat方法 |
---|
兩個物件使用同一個方法時的記憶體圖
比如:
Student stu = new Student();
Student stu2 = new Student();
……
都是指向方法區的同一塊方法的同一塊地址空間
兩個引用指向同一個物件的記憶體圖
Student stu = new Student();
Student stu2 = stu;
……
尋找stu的地址值,然後根據stu的地址值進行呼叫方法
使用物件型別作為方法的引數
比如程式碼中有這麼兩三行
public static void method(Student stu){
System.out.println(stu.eat);
……
}
當一個物件作為方法的引數時,傳遞到方法中時,實際傳遞進去的是物件的地址值
使用物件型別作為方法的返回值
public static Student eat(){
Student stu =new Student();
stu.study();
stu.name = "小杜";
return stu;
}
當使用一個物件型別作為方法的返回值時
返回值其實就是物件的地址值
四、字串常量池
字串常量池:程式當中直接寫上的雙引號字串,就在字串常量池當中
所以:
對於基本型別來說,== 比較的是數值
對於引用型別來說 ,== 比較的是地址值
因為內容不可變性,所以可以共享的
而且字串的效果相當於char[]陣列,但是底層原理是byte[]陣列,所以它會在儲存的過程中自動轉換成byte[]陣列
借用一張網上的圖
順便提一下static關鍵字
根據類名稱訪問靜態成員變數的時候,全程和物件是沒有關係的,只和類有關係。
五、繼承中的記憶體圖
圖片來源網路:
也就是父類空間優先於子類物件的產生,在每次建立子類物件時,先初始化父類空間,再建立子類物件本身。