當前位置:
首頁 > 最新 > 非靜態內部類中 static/final 成員變數相關的一道趣題

非靜態內部類中 static/final 成員變數相關的一道趣題

問:為什麼非靜態內部類中不能有 static 成員變數卻可以有 static final 屬性的編譯期常量?

答:這是一個陷阱題,看起來似乎很簡單,實際是一箭雙鵰,即考察了非靜態內部類相關知識,還考察了 final 的各種常量分類細則(很多人回答時會疏忽這個點)。

由於 Java 中非靜態內部類默認持有了外部類引用,也就是說可以將它看成是其外部類的一個成員,所以其必須跟外部類實例相關聯才能初始化。而靜態成員是屬於類層次的,是不需要類實例就可以初始化訪問的。此時如果假設讓一個非靜態內部類擁有了靜態變數,則其應該可以不依託於任何外部類實例就能訪問,而非靜態內部類卻沒法做到不實例化其外部類而使用,所以這種設計從語法層面就是互斥的。

而對於 static final 成員來說就不一樣了,至於為什麼不一樣我們需要先切換補充一些知識點再說。我們先看下面程序段:

上面程序片段分別編譯運行的結果在注釋部分已經給出了,在我們挨個解釋上面語句現象前先說說 Java 對常量的一些定義和處理機制。

對於 Java 中的常量其實可以分為編譯期常量和非編譯期常量。編譯期常量指的是在程序編譯階段(不需要載入類的位元組碼)就可以確定具體值的常量,其中會涉及到編譯期常量摺疊(編譯器可以語法分析計算出值的常量表達式進行計算取值)。非編譯期常量(運行期常量)指的是在程序運行階段(需要載入類的位元組碼)才可以確定具體值的常量(編譯期無法摺疊,編譯器能做的只是對所有可能修改它的地方進行檢查和報錯)。

當我們通過類名訪問被 static final 修飾的常量時,如果該常量是編譯期常量則該類不會被 JVM 載入,如果該常量是非編譯期常量則該類會被 JVM 載入。當通過類名訪問被 static 修飾的變數時都會觸該類被 JVM 載入。

有了上面這個概念我們再來分析上面代碼段的原因。由於 Java 類屬性的初始化順序為(靜態變數、靜態初始化塊)>(變數、初始化塊)> 構造器,所以 JVM 要求所有的靜態屬性必須在類對象創建之前完成初始化。故對於 t1 屬性調用處來說,必須要等到外部類 Outer 實例化之後(即有創建一個外部類對象)JVM 才能載入其內部類 Inner 的位元組碼,而我們調用處並沒有對外部類進行實例化,所以內部類 Inner 也不會被載入,而 t1 是 static 屬性,要初始化 t1 就必須先載入內部類的位元組碼;而 t3 是非編譯期常量,要初始化它們也必須載入內部類的位元組碼才能確定它們的值;只有 t2 是編譯時常量,所以不會觸發內部類載入機制;故而有了上面代碼段的結果。

所以說非靜態內部類中不能有 static 成員變數卻可以有 static final 屬性的編譯期常量而不能有 static final 屬性的運行期常量。

由此又引出了一個新的面試題和代碼優化小技巧。

為什麼我們在類中定義 static 修飾的 String 常量時經常會在它前面加上 final 修飾符?因為這樣可以使 JVM 不必載入該類的類體就能直接使用其值從而節省了內存空間。

關於本題相關拓展題目和涉及知識點題目參見如下歷史推送:

《Java final、static 關鍵字基礎面試題》

《Java 內部類(Part 1)相關面試題》

《Java 內部類(Part 2)相關面試題》

《Java 內部類(Part 3)相關面試題》

《Java 內部類(Part 4)相關面試題》

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 碼農每日一題 的精彩文章:

TAG:碼農每日一題 |