先來看一道最常見的面試題:靜態代碼塊的執行順序?
看一下實際運行:
class A{ static { System.out.println("A的靜態代碼塊"); } { System.out.println("A的構造代碼塊/非靜態代碼塊"); } public A() { System.out.println("A的構造函數"); } } class B extends A{ static { System.out.println("B的靜態代碼塊"); } { System.out.println("B的構造代碼塊/非靜態代碼塊"); } public B() { System.out.println("B的構造函數"); } }
//測試程序執行
public static void main(String[] args) { System.out.println("==== main開始執行 ===="); new B(); System.out.println("==== B已經被創建 ====="); }
答案是:
==== main開始執行 ====
A的靜態代碼塊
B的靜態代碼塊
A的構造代碼塊/非靜態代碼塊
A的構造函數
B的構造代碼塊/非靜態代碼塊
B的構造函數
==== B已經被創建 =====
好了,現在開始小白的問答環節:
Q1:不是說,靜態塊里的內容在類被加載的時候就執行,存在方法區(靜態區)中,那為什么是先有main開始執行的語句打印呢?
A1:首先要明白,什么叫類加載:類加載就是把需要的類的代碼加載到內存中,且在此類首次使用時靜態代碼塊被加載執行,也就是說靜態代碼塊只執行一次。
什么時候有類加載:1.創建實例 2.調用該類的靜態方法時(靜態塊代碼優先於靜態函數執行) 3.使用該類的非常量靜態字段 4.使用反射方法時 5.初始化該類的子類
Q2:上面說存在方法區中,那么方法區是什么?Java內存布局里沒有方法區呀?
A2:《Java虛擬機規范》只是規定了有方法區這個概念,在HotSpot上使用永久代來實現方法區。而到了JDK8,永久代被元空間(MetaSpace)替換,其他內容由永久代移入元空間,比如說
類元信息,字段,靜態屬性,方法,常量等,除了字符串常量,它被移入了堆內存。
Q3:什么是構造代碼塊呢?
A3:構造代碼塊:初始化所有對象。屬於對象的,在new 的時候才會執行,而構造函數是new的時候,調用了構造方法,所以構造代碼塊會先一步
Q4:為什么我構造方法寫成了 public void A(){} 就不可以了呢?構造方法為什么不可以有返回值?
A4:構造方法在Java語言規范里面叫 “Constructor” ,並不是一個成員方法(成員方法叫 Method),其實應該叫 “構造器”更合適。
構造方法在類創建的期間(new)被執行用於對象初始化的,只有在構造方法執行完了,類的創建才算是完成了。
既然是用於初始化對象的,那么沒有返回值是很正常的事情了,虛擬機規范中也是這么形容的。