日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

你知道Java中final和static修饰的变量是在什么时候赋值的吗?

發布時間:2025/3/12 java 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 你知道Java中final和static修饰的变量是在什么时候赋值的吗? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

開始

一位朋友在群里問了這樣一個問題:

本著樂于助人的想法,我當時給出的回答:

后來我總覺得哪里不對勁,仔細翻閱了《Java虛擬機規范》和《深入理解Java虛擬機》這一部分的內容,害!發現自己理解的有問題。

因為自己的理解出錯而誤導了別人,實在是讓我萬分羞愧!

自己菜但是不能誤導別人,于是我加了這位朋友的好友,向這位朋友表達了歉意,這位朋友也非常隨和,對此表示理解。

今天討論的問題就是從這個故事開始的。

final修飾的實例變量

我們先分析一下這個問題:深入Java虛擬機有一句是“ConstantValue屬性的作用是通知虛擬機自動為靜態變量賦值,只有被static關鍵字修飾的變量才可以使用這項屬性。但為什么private final a = 10也可以被賦值?”

我翻閱了《深入理解Java虛擬機》第二版,在第191頁,確實有前面那句話

書中說的很清楚,ConstantValue屬性的作用是通知虛擬機自動為靜態變量賦值。

那就意味著只有static修飾的類變量才會在class文件中對應的字段表加上ConstantValue屬性嗎?

答案是否定的。用final修飾的實例變量,編譯成class文件的時候,對應的字段表也有可能會加上ConstantValue屬性。

注意,我這里用了“可能”這兩個字,因為這是有條件的。哪些情況會有ConstantValue屬性呢?

我們寫一段代碼,列舉一下用final修飾的實例變量的幾種情況,編譯之后,然后用javap -verbose命令查看Java編譯器為我們生成的字節碼。

我們可以看到,在字段表集合里面有四個字段表,分表對應這a,b,c,d,e五個實例屬性,他們都帶有ACC_PUBLIC(public)和ACC_FINAL(final)的訪問標志。但只有a和b對應的字段表帶有ConstantValue屬性。我們總結一下:

用final修飾不是在構造方法賦值的String類型或者基本類型成員變量,編譯成字節碼文件時,對應的字段表也會帶有ConstantValue屬性。

這個結論不和《深入理解Java虛擬機》沖突嗎?

于是我翻閱了JVM Spec Java SE 8Edition(周志明前輩是翻譯過,書名《Java虛擬機規范》,但是我手里沒有翻譯后的中文版),在4.7.2部分我找到了這樣一句話:

書中說的很清楚,如果field_info(字段表)表示的非靜態字段包含了ConstantValue屬性,那么這個ConstantValue屬性會被Java虛擬機所忽略。也就是說,對于非靜態字段,就算你編譯器加上了ConstantValue屬性,JVM也會忽略掉,你加不加結果是一樣的。

看完《Java虛擬機規范》里面的說明,再回來看《深入理解Java虛擬機》里面的這句話:

ConstantValue屬性的作用是通知虛擬機自動為靜態變量賦值,只有被static關鍵字修飾的類變量才可以使用這項屬性。

作者的這句話的前半句沒有什么爭議,但我覺得后半句的表述的不太明確,容易造成誤解。

以我的理解,應該是“只有被static關鍵字修飾的類變量才可以使用這項屬性來進行初始化,否則使用這項屬性也會被JVM忽略掉

好了,我們再回到那位朋友問的問題:為什么private final a = 10也可以被賦值?

首先,這個問題的本身就問的不太準確。我理解這位朋友真正想問的是“為什么private final a = 10也可以通過ConstantValue屬性的形式賦值?”

我覺得這是一個很好的問題,這位朋友通過實驗發現用final修飾的實例變量對應的字段表有ConstantValue屬性,結合《深入理解Java虛擬機》,他認為a是通過ConstantValue屬性讓虛擬機知道然后為其賦值的。最后他發現和書中沖突,于是提出了上文的這個問題。

這樣的思路有問題嗎?我覺得是沒有問題的。

不過這樣的理解是對的嗎?顯然是不對的。

因為虛擬機規范是這樣規范的。對于非靜態字段,ConstantValue屬性是不會生效的。

至于為什么要這樣設計,功力不夠的我暫時無法理解設計者的想法。

那單獨用final修飾的實例變量到底是在什么時候賦值的呢?

這個問題也不難回答,看一下字節碼就清楚了。

通過查看字節碼,我們可以看到有一個方法,右邊是它的字節碼指令。

什么是方法?我們看看Java虛擬機規范上的解釋:

我們溫習一下這個英語四級短語:appear as

然后,我們一起翻譯一下:在JVM層面上,每一個用Java寫的構造方法都表現為實例初始方法,這個方法就是方法。

記住,這個方法會在實例初始化的時候被調用。

我們再來看一下putfield這個字節碼指令的含義:putfield指令就是為指定的類的實例域賦值的,也就是為實例變量賦值的指令。

現在我們可以清晰的知道,這些用final修飾實例變量是在實例構造器方法里面賦值的,也就是對象創建的時候賦值。

static修飾的類變量

上面講到ConstantValue屬性的作用是通知虛擬機自動為靜態變量賦值。

我們再回過來講一下靜態變量,一個很關鍵的關鍵字static。

在這之前,我需要把類加載的幾個過程大致給你講一下:

類的生命周期由7個階段組成,類加載說的是前5個階段,即加載—>驗證—>準備—>解析—>初始化。

類的生命周期圖

我們簡單過一下這幾個階段:

  • 加載:將字節碼所代表的靜態存儲結構轉化為方法區的運行時數據結構。
  • 驗證:驗證字節碼格式,確保Class文件的字節流中包含的信息符合當前虛擬機的要求,并且不會危害虛擬機自身的安全。
  • 準備:創建類或者接口的靜態字段,并為靜態變量設置初始值。
  • 解析:將常量池內的符號引用替換為直接引用。
  • 初始化:執行類構造器方法。

類構造器方法又是個什么東西呢?

JVM Spec Java SE 8Edition這樣說道:

說白了,編譯器會收集所有靜態變量的賦值動作、所有靜態代碼塊,合并產生一個方法,即方法。這個方法在類加載的初始化階段執行。

而對于類變量(static修飾的),則有兩種賦值方式可以選擇:

  • 使用ConstantValue屬性賦值。
  • 在類構造器方法中賦值。

目前Oracle公司實現的Javac編譯器的選擇是:

  • final+static修飾:使用ConstantValue屬性賦值。
  • 僅僅使用static修飾:在方法中賦值。

需要注意點的是,用生成ConstantValue屬性來進行初始化,這個變量必須是基本類型或者java.lang.String類型。

這是因為Class文件格式的常量類型中只有與基本屬性和字符串相對應的字面量,所以就算ConstantValue屬性想支持別的類型也無能為力。

對于這一點,我們也可以通過javap -verbose命令反編譯驗證一下:

final+static修飾的常量

上面我們說過,方法是在類加載的出初始化階段賦值的。

那static+final修飾的常量是在類加載的那一階段進行的呢?我們可以看一下JVM規范:

我們可以看到在JVM規范里面,static+final修飾的常量是在初始化階段執行方法之前執行的。

咦?我們平時背的不都是在類加載的準備階段會對普通類屬性賦初始值,帶有ConstantValue的類屬性直接賦值嗎?

《深入理解Java虛擬機》也是這樣說的啊?

書上是錯的嗎?不是的,因為《深入理解Java虛擬機》里面講的具體實現,是基于HotSpot VM講的。

確確實實,HotSpot VM就是這么干的,我們也可以在openJdk中找到對應的源碼:

看起來,HotSpot VM對基本類型或者字符串類型的常量的賦值確實在準備階段完成了。

但一個很關鍵的點是,仍然在調用之前賦值了。

外界是不會觀察到HotSpot VM提前做了這個初始化賦值的,所以是沒問題。

不過要記住的是,規范里明確說了正確的初始化時機是在“初始化(Initialization)”階段。

總結

  • final修飾的實例屬性,在實例創建的時候才會賦值。
  • static修飾的類屬性,在類加載的準備階段賦初值,初始化階段賦值。
  • static+final修飾的String類型或者基本類型常量,JVM規范是在初始化階段賦值,但是HotSpot VM直接在準備階段就賦值了。
  • static+final修飾的其他引用類型常量,賦值步驟和第二點的流程是一樣的。
  • 還有一點,一定不要把《深入理解Java虛擬機》和《Java虛擬機規范》搞混了。

    • 《Java虛擬機規范》是翻譯的官方JVM規范文檔,所有的JVM實現都要遵從規范,但有強制要求的規范和建議的規范。
    • 《深入理解Java虛擬機》是作者根據自己的理解,結合HotSpot VM的具體實現,為了讓讀者更容易理解JVM而寫的一本書。

    總結

    以上是生活随笔為你收集整理的你知道Java中final和static修饰的变量是在什么时候赋值的吗?的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。