1.實例變量和類變量
看到這個標題大家一定想到的是static關鍵字,沒錯,被static關鍵字修飾的變量,方法,內部類,初始化塊,這些都屬於類,而不具體屬於某個實例對象,可以直接通過類名來調用。關於static可能造成的內存泄漏,在後面的JVM文章裏總結。這裏我就簡單通過兩個程序總結一下類成員和實例成員的區別,以及創建一個對象後初始化的順序。
實例變量屬於對象,每實例化一個對象,就會爲其在內存中分配一塊空間。類變量只會在第一次使用該類時爲其分配一份內存空間,無論創建了多少個該類的對象,static變量永遠只有一份內存空間。一個類在使用之前,就會在JVM中對該類進行加載-連接-初始化,在連接時就會給類變量分配相應的內存空間。
1 public class B extends A { 2 public int m = method3(); 3 public static int n = method4(); 4 public int t = 0; 5 public B(){ 6 System.out.println(4); 7 } 8 public int method3(){ 9 System.out.println(5); 10 return 5; 11 } 12 public static int method4(){ 13 System.out.println(6); 14 return 6; 15 } 16 public static void main(String[] args) { 17 System.out.println(7); 18 A a = new B(); 19 } 20 } 21 22 class A { 23 public int i = method(); 24 public static int j = method2(); 25 public int k = 0; 26 public A(){ 27 System.out.println(1); 28 } 29 public int method(){ 30 System.out.println(2); 31 return 2; 32 } 33 public static int method2(){ 34 System.out.println(3); 35 return 3; 36 } 37 }
A、7 3 2 1 6 4 5 B、3 6 7 2 1 5 4 C、7 3 6 2 1 5 4 D、3 2 1 7 6 5 4
看看上述代碼的輸出結果應該選哪個呢?
讓我們一步一步來分析:A類是B類的父類,在執行程序時,A類先被加載,並且爲其類變量分配內存空間,所以先爲24行的 j 分配內存,初始化爲methond2()方法的返回值,這裏的method2()也必須被static修飾,才能被調用,所以先輸出3;同理,接下來爲B類的類變量分配空間,輸出6;然後執行16行main()方法,輸出7;實例化B類的對象,先執行其父類(A)的非靜態初始化(代碼23行),method()方法輸出2;然後是父類(A)的構造方法,輸出1;然後是B類同理,依次輸出5和4,程序結束,所以最終答案是B。
這個代碼大家可以複製下來進行單步debug來細細體會。
再來看一個有趣的代碼,細細品味。
1 public class PriceTest { 2 3 public static void main(String[] args) { 4 System.out.println(Price.instance.currentPrice); 5 //顯示的創建Price對象 6 Price p = new Price(2.8); 7 System.out.println(p.currentPrice); 8 } 9 } 10 11 class Price{ 12 //類變量 13 final static Price instance = new Price(2.8); 14 //類變量 15 static double initPrice = 20; 16 //實例變量 17 double currentPrice; 18 public Price(double discount){ 19 currentPrice = initPrice - discount; 20 } 21 }
上述代碼輸出了兩次currentPrice,而且都是通過new Price(2.8)來創建實例,看起來應該是輸出兩次17.2,親自運行一下這個代碼,會發現結果卻是-2.8和17.2。
下面從內存層面上來看看到底發生了什麼,當然,首先還是先爲Price類的類變量分配內存空間,這個時候兩個static變量instance和initPrice值爲null和0.0,接下來按這個順序爲它們初始化,instance的值爲new Price(2.8),立即執行18行,所以這時的currentPrice是等於0.0 - 2.8的,instance初始化完畢,然後是initPrice初始化爲20。所以第4行輸出-2.8,執行第6行時,類變量initPrice已經爲20,所以第7行輸出17.2。