Java中的變量根據不同的標准可以分為兩類,以其引用的數據類型的不同來划分可分為“原始數據類型變量和引用數據類型變量”,以其作用范圍的不同來區分可分為“局部變量,實例變量和靜態變量”。
根據“Java中的變量與數據類型”中的介紹,“變量是在內存中分配的保留區域的名稱。換句話說,它是一個內存位置的名稱”,也就是說我們通過這個變量名字就可以找到一個指向這個變量所引用的數據的內存指針,根據變量的類型我們可以知道這個指針之后的幾個字節里存儲了這個變量所引用的數據。
所以,了解變量在方法區、棧內存、堆內存中的分配要了解兩部分內容,一個是“變量在內存中的分配”,另一個是“變量所引用的數據在內存中的分配”。以下簡稱為“變量分配”與“數據分配”。
原始數據類型變量:
原始數據類型變量的“變量分配”與“數據分配”是在一起的(都在方法區或棧內存或堆內存)
引用數據類型變量:
引用數據類型變量的“變量分配”與“數據分配”不一定是在一起的。
示例代碼:
1 class Fruit { 2 static int x = 10; 3 static BigWaterMelon bigWaterMelon_1 = new BigWaterMelon(x); 4 5 int y = 20; 6 BigWaterMelon bigWaterMelon_2 = new BigWaterMelon(y); 7 8 public static void main(String[] args) { 9 final Fruit fruit = new Fruit(); 10 11 int z = 30; 12 BigWaterMelon bigWaterMelon_3 = new BigWaterMelon(z); 13 14 new Thread() { 15 @Override 16 public void run() { 17 int k = 100; 18 setWeight(k); 19 } 20 21 void setWeight(int waterMelonWeight) { 22 fruit.bigWaterMelon_2.weight = waterMelonWeight; 23 } 24 }.start(); 25 } 26 } 27 28 class BigWaterMelon { 29 public BigWaterMelon(int weight) { 30 this.weight = weight; 31 } 32 33 public int weight; 34 }
內存分配圖:
同一種顏色代表變量和對象的引用關系:
由於方法區和堆內存的數據都是線程間共享的,所以線程Main Thread,New Thread和Another Thread都可以訪問方法區中的靜態變量以及訪問這個變量所引用的對象的實例變量。ps:方法區存儲的都是只加載一次的。
棧內存中每個線程都有自己的虛擬機棧,每一個棧幀之間的數據就是線程獨有的了,也就是說線程New Thread中setWeight方法是不能訪問線程Main Thread中的局部變量bigWaterMelon_3,但是我們發現setWeight卻訪問了同為Main Thread局部變量的“fruit”,這是為什么呢?因為“fruit”被聲明為final了。
當“fruit”被聲明為final后,“fruit”會作為New Thread的構造函數的一個參數傳入New Thread,也就是堆內存中Fruit$1對象中的實例變量val$fruit會引用“fruit”引用的對象,從而New Thread可以訪問到Main Thread的局部變量“fruit”。