Java 靜態變數生命週期
Static:
類初始化順序:
結論:想要用static存一個變數,使得下次程式執行時還能使用上次的值是不可行的。因為靜態變數生命週期雖然長(就是類的生命週期),但是當程式執行完,也就是該類的所有物件都已經被回收,或者載入類的ClassLoader已經被回收,那麼該類就會從jvm的方法區解除安裝,即生命期終止。 更進一步來說,static變數終究是存在jvm的記憶體中的,jvm下次重新執行時,肯定會清空裡邊上次執行的內容,包括方法區、常量區的內容。 要實現某些變數在程式多次執行時都可以讀取,那麼必須要將變數存下來,即存到本地檔案中。常用的資料存取格式:XML、JSON、Propertities類(類似map的鍵值對)等 |
引言最近有位細心的朋友在閱讀筆者的文章時,對java類的生命週期問題有一些疑惑,筆者開啟百度搜了一下相關的問題,看到網上的資料很少有把這個問題講明白的,主要是因為目前國內java方面的教材大多隻是告訴你“怎樣做”,但至於“為什麼這樣做”卻不多說,所以造成大家在基礎和原理方面的知識比較匱乏,所以筆者今天就斗膽來講一下這個問題,權當拋磚引玉,希望對在這個問題上有疑惑的朋友有所幫助,文中有說的不對的地方,也希望各路高手前來指正。 首先來了解一下jvm(java虛擬機器)中的幾個比較重要的記憶體區域,這幾個區域在java類的生命週期中扮演著比較重要的角色:
除了以上四個記憶體區域之外,jvm中的執行時記憶體區域還包括本地方法棧和程式計數器,這兩個區域與java類的生命週期關係不是很大,在這裡就不說了,感興趣的朋友可以自己百度一下。 類的生命週期當我們編寫一個java的原始檔後,經過編譯會生成一個字尾名為class的檔案,這種檔案叫做位元組碼檔案,只有這種位元組碼檔案才能夠在java虛擬機器中執行,java類的生命週期就是指一個class檔案從載入到解除安裝的全過程。 一個java類的完整的生命週期會經歷載入、連線、初始化、使用、和解除安裝五個階段,當然也有在載入或者連線之後沒有被初始化就直接被使用的情況,如圖所示:
下面我們就依次來說一說這五個階段。 載入在java中,我們經常會接觸到一個詞——類載入,它和這裡的載入並不是一回事,通常我們說類載入指的是類的生命週期中載入、連線、初始化三個階段。在載入階段,java虛擬機器會做什麼工作呢?其實很簡單,就是找到需要載入的類並把類的資訊載入到jvm的方法區中,然後在堆區中例項化一個java.lang.Class物件,作為方法區中這個類的資訊的入口。 類的載入方式比較靈活,我們最常用的載入方式有兩種,一種是根據類的全路徑名找到相應的class檔案,然後從class檔案中讀取檔案內容;另一種是從jar檔案中讀取。另外,還有下面幾種方式也比較常用:
對於載入的時機,各個虛擬機器的做法並不一樣,但是有一個原則,就是當jvm“預期”到一個類將要被使用時,就會在使用它之前對這個類進行載入。比如說,在一段程式碼中出現了一個類的名字,jvm在執行這段程式碼之前並不能確定這個類是否會被使用到,於是,有些jvm會在執行前就載入這個類,而有些則在真正需要用的時候才會去載入它,這取決於具體的jvm實現。我們常用的hotspot虛擬機器是採用的後者,就是說當真正用到一個類的時候才對它進行載入。 載入階段是類的生命週期中的第一個階段,載入階段之後,是連線階段。有一點需要注意,就是有時連線階段並不會等載入階段完全完成之後才開始,而是交叉進行,可能一個類只加載了一部分之後,連線階段就已經開始了。但是這兩個階段總的開始時間和完成時間總是固定的:載入階段總是在連線階段之前開始,連線階段總是在載入階段完成之後完成。 連線連線階段比較複雜,一般會跟載入階段和初始化階段交叉進行,這個階段的主要任務就是做一些載入後的驗證工作以及一些初始化前的準備工作,可以細分為三個步驟:驗證、準備和解析。
連線階段完成之後會根據使用的情況(直接引用還是被動引用)來選擇是否對類進行初始化。 初始化如果一個類被直接引用,就會觸發類的初始化。在java中,直接引用的情況有:
除了以上四種情況,其他使用類的方式叫做被動引用,而被動引用不會觸發類的初始化。請看主動引用的示例程式碼:
上面的程式演示了主動引用觸發類的初始化的四種情況。 類的初始化過程是這樣的:按照順序自上而下執行類中的變數賦值語句和靜態語句,如果有父類,則首先按照順序執行父類中的變數賦值語句和靜態語句。先看一個例子,首先建兩個類用來顯示賦值操作:
下面是演示初始化順序的程式碼:
上面的程式碼中,初始化的順序是:第03行,第05行,第11行,第13行。第04行是宣告操作,沒有賦值,所以不會被執行。而下面的程式碼:
初始化順序為:第02行、第05行、第10行、第12行,各位可以執行程式檢視結果。 在類的初始化階段,只會初始化與類相關的靜態賦值語句和靜態語句,也就是有static關鍵字修飾的資訊,而沒有static修飾的賦值語句和執行語句在例項化物件的時候才會執行。 使用類的使用包括主動引用和被動引用,主動引用在初始化的章節中已經說過了,下面我們主要來說一下被動引用:
被動引用的示例程式碼:
當使用階段完成之後,java類就進入了解除安裝階段。 解除安裝關於類的解除安裝,筆者在單例模式討論篇:單例模式與垃圾回收一文中有過描述,在類使用完之後,如果滿足下面的情況,類就會被解除安裝:
如果以上三個條件全部滿足,jvm就會在方法區垃圾回收的時候對類進行解除安裝,類的解除安裝過程其實就是在方法區中清空類資訊,java類的整個生命週期就結束了。 總結做java的朋友對於物件的生命週期可能都比較熟悉,物件基本上都是在jvm的堆區中建立,在建立物件之前,會觸發類載入(載入、連線、初始化),當類初始化完成後,根據類資訊在堆區中例項化類物件,初始化非靜態變數、非靜態程式碼以及預設構造方法,當物件使用完之後會在合適的時候被jvm垃圾收集器回收。讀完本文後我們知道,物件的生命週期只是類的生命週期中使用階段的主動引用的一種情況(即例項化類物件)。而類的整個生命週期則要比物件的生命週期長的多。 如果有疑問或者發現本文中不對的地方,歡迎各位留言。本文doc文件版本的下載地址: |