JVM常用概念之常量

問題

final修飾的字段就一定是不能重新賦值嗎?

基礎知識

常量變量是使用常量表達式初始化的原始類型或 String 類型的最終變量。變量是否為常量變量可能對類初始化、二進制兼容性和明確賦值有影響。 —Java 語言規范

實驗

用例源碼-重新賦值

import java.lang.reflect.Field;public class ConstantValues {final int fieldInit = 42;final int instanceInit;final int constructor;{instanceInit = 42;}public ConstantValues() {constructor = 42;}static void set(ConstantValues p, String field) throws Exception {Field f = ConstantValues.class.getDeclaredField(field);f.setAccessible(true);f.setInt(p, 9000);}public static void main(String... args) throws Exception {ConstantValues p = new ConstantValues();set(p, "fieldInit");set(p, "instanceInit");set(p, "constructor");System.out.println(p.fieldInit + " " + p.instanceInit + " " + p.constructor);}}

執行結果

42 9000 9000

如上述執行結果所示,上面有3個被final關鍵字修飾的字段,其中的fieldInit字段賦有初值,其他兩個沒有賦初值,而在后續的通過Java反射機制對上述的3個被final修飾字段重新賦值后,執行結果驚奇的發現賦有初值的fieldInit字段的值沒有被修改,其他兩個沒有賦初值的字段的值發生了修改,那這是因為什么呢?我們可以通過查看通過javac靜態編譯的字節碼一查究竟,如下述代碼所示:

$ javap -c -v -p ConstantValues.class
...final int fieldInit;descriptor: Iflags: ACC_FINALConstantValue: int 42  <---- oh...final int instanceInit;descriptor: Iflags: ACC_FINALfinal int constructor;descriptor: Iflags: ACC_FINAL...
public static void main(java.lang.String...) throws java.lang.Exception;descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGSCode:...41: bipush        42   // <--- Oh wow, inlined fieldInit field43: invokevirtual #18  // StringBuilder.append46: ldc           #19  // String " "48: invokevirtual #20  // StringBuilder.append51: aload_152: getfield      #3   // Field instanceInit:I55: invokevirtual #18  // StringBuilder.append58: ldc           #19  // String ""60: invokevirtual #20  // StringBuilder.append63: aload_164: getfield      #4   // Field constructor:I67: invokevirtual #18  // StringBuilder.append70: invokevirtual #21  // StringBuilder.toString73: invokevirtual #22  // System.out.println

通過查看上述字節碼可以看出,被初始化賦值的fieldInit字段其實在javac靜態編譯時已經通過內聯操作賦值了,而對于在JVM動態編譯時不可能重新重寫字節碼,所以從此我們可以看出已經進行初始化賦值的由final關鍵字修飾的字段是不能修改的,而未進行初始化賦值的由final關鍵字修飾的字段卻是可以進行修改的。理論上進行初始化賦值的由final關鍵字修飾的字段性能表現肯定要比沒有進行初始化賦值的由final關鍵字修飾的字段要好,我們可以通過下面的測試用例進行進一步的驗證。

用例源碼-是否有final修飾的已初始化字段

@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class FinalInitBench {// Too lazy to actually build the example class with constructor that initializes// final fields, like we have in production code. No worries, we shall just model// this with naked fields. Right?final int fx = 42;  // Compiler complains about initialization? Okay, put 42 right here!int x  = 42;@Benchmarkpublic int testFinal() {return fx;}@Benchmarkpublic int test() {return x;}
}

執行結果

Benchmark                                  Mode  Cnt   Score    Error  Units
FinalInitBench.test                        avgt    9   1.920 ±  0.002  ns/op
FinalInitBench.test:CPI                    avgt    3   0.291 ±  0.039   #/op
FinalInitBench.test:L1-dcache-loads        avgt    3  11.136 ±  1.447   #/op
FinalInitBench.test:L1-dcache-stores       avgt    3   3.042 ±  0.327   #/op
FinalInitBench.test:cycles                 avgt    3   7.316 ±  1.272   #/op
FinalInitBench.test:instructions           avgt    3  25.178 ±  2.242   #/opFinalInitBench.testFinal                   avgt    9   1.901 ±  0.001  ns/op
FinalInitBench.testFinal:CPI               avgt    3   0.285 ±  0.004   #/op
FinalInitBench.testFinal:L1-dcache-loads   avgt    3   9.077 ±  0.085   #/op  <--- !
FinalInitBench.testFinal:L1-dcache-stores  avgt    3   4.077 ±  0.752   #/op
FinalInitBench.testFinal:cycles            avgt    3   7.142 ±  0.071   #/op
FinalInitBench.testFinal:instructions      avgt    3  25.102 ±  0.422   #/op

由上述通過perform 執行結果可以看出,都進行了初始化的兩個字段,有final修飾的字段的性能要更好。那這是因為什么呢?我們可以通過匯編代碼進行查證,具體如下:

# test
...
1.02%    1.02%  mov    0x10(%r10),%edx ; <--- get field x
2.50%    1.79%  nop
1.79%    1.60%  callq  CONSUME
...# testFinal
...
8.25%    8.21%  mov    $0x2a,%edx      ; <--- just use inlined "42"
1.79%    0.56%  nop
1.35%    1.19%  callq  CONSUME
...

通過上述的匯編代碼可以看出,由final修飾的字段在執行匯編指令過程中并沒有進行字段加載,而只是引入字節碼中的內聯常量,這就是性能提升的關鍵點。

用例源碼-是否有final修飾的未初始化字段

@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class FinalInitCnstrBench {final int fx;int x;public FinalInitCnstrBench() {this.fx = 42;this.x = 42;}@Benchmarkpublic int testFinal() {return fx;}@Benchmarkpublic int test() {return x;}
}

執行結果

Benchmark                                            Mode  Cnt   Score    Error  Units
FinalInitCnstrBench.test                             avgt    9   1.922 ±  0.003  ns/op
FinalInitCnstrBench.test:CPI                         avgt    3   0.289 ±  0.049   #/op
FinalInitCnstrBench.test:L1-dcache-loads             avgt    3  11.171 ±  1.429   #/op
FinalInitCnstrBench.test:L1-dcache-stores            avgt    3   3.042 ±  0.031   #/op
FinalInitCnstrBench.test:cycles                      avgt    3   7.301 ±  0.445   #/op
FinalInitCnstrBench.test:instructions                avgt    3  25.235 ±  1.732   #/opFinalInitCnstrBench.testFinal                        avgt    9   1.919 ±  0.002  ns/op
FinalInitCnstrBench.testFinal:CPI                    avgt    3   0.287 ±  0.014   #/op
FinalInitCnstrBench.testFinal:L1-dcache-loads        avgt    3  11.170 ±  1.104   #/op
FinalInitCnstrBench.testFinal:L1-dcache-stores       avgt    3   3.039 ±  0.864   #/op
FinalInitCnstrBench.testFinal:cycles                 avgt    3   7.278 ±  0.394   #/op
FinalInitCnstrBench.testFinal:instructions           avgt    3  25.314 ±  0.588   #/op

由上述執行結果可知,對于未進行初始化,不管是否有final關鍵字修飾的字段,這兩種情況執行的性能表現是一樣的。

總結

由final關鍵字修飾的字段需要進行初始化賦值。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/897600.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/897600.shtml
英文地址,請注明出處:http://en.pswp.cn/news/897600.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

數據采集技術之python網絡爬蟲(中國天氣網的爬取)

一、爬取中國天氣網所有地區當天的天氣數據&#xff08;PyCharm&#xff09;&#xff1a; 網址&#xff1a;https://www.weather.com.cn/ 下面爬取數據&#xff1a; 因為現在已經到了夜間&#xff0c;所以白天的數據已經不見了&#xff0c;但原理是一樣的。 二、代碼以及詳情…

樹莓集團落子海南,如何重構數字產業生態體系?

樹莓集團在海南的布局&#xff0c;是其整體商業戰略中的關鍵一環。這背后&#xff0c;是對政策機遇、產業協同、以及區域優勢的深度考量。 政策機遇 海南自貿港建設帶來前所未有的政策紅利&#xff0c;包括貿易、投資、資金等方面的自由便利。樹莓集團緊抓這一機遇&#xff0…

Ollama本地部署deepseek-r1蒸餾版

Docker安裝Ollama 拉取鏡像 docker pull ollama/ollama? 啟動-使用GPU docker run -d --gpusall -p 11434:11434 --name ollama ollama/ollamadocker run : Docker 的核心命令&#xff0c;用于創建并啟動一個新的容器。 -d : 后臺模式&#xff08;detached mode&#xff09…

41.HarmonyOS NEXT Layout布局組件系統詳解(八):自定義樣式與類

溫馨提示&#xff1a;本篇博客的詳細代碼已發布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下載運行哦&#xff01; HarmonyOS NEXT Layout 布局組件系統詳解&#xff08;八&#xff09;&#xff1a;自定義樣式與類 文章目錄 HarmonyOS NEXT Layout 布局組件系統詳…

【Go | 從0實現簡單分布式緩存】-7:增加etcd和gRPC功能

本文目錄 1.序2.引入etcd緩存流程項目結構 3.gocachepb.proto4.服務注冊register.go5.服務發現discover.go6.gRPC客戶端client.gopeers.goclient.go 7.gRPC服務端實現server.go一些問題緩存獲取流程緩存設置流程為什么要帶超時的上下文&#xff1f; 1.序 GeeCache項目并沒有引…

Pytorch系列教程:可視化Pytorch模型訓練過程

深度學習和理解訓練過程中的學習和進步機制對于優化性能、診斷欠擬合或過擬合等問題至關重要。將訓練過程可視化的過程為學習的動態提供了有價值的見解&#xff0c;使我們能夠做出合理的決策。訓練進度必須可視化的兩種方法是&#xff1a;使用Matplotlib和Tensor Board。在本文…

18 | 實現簡潔架構的 Handler 層

提示&#xff1a; 所有體系課見專欄&#xff1a;Go 項目開發極速入門實戰課&#xff1b;歡迎加入我的訓練營&#xff1a;云原生AI實戰營&#xff0c;一個助力 Go 開發者在 AI 時代建立技術競爭力的實戰營&#xff1b;本節課最終源碼位于 fastgo 項目的 feature/s14 分支&#x…

藍隊第三次

1.了解什么是盲注 盲注&#xff08;Blind SQL Injection&#xff09;是SQL注入的一種形式&#xff0c;攻擊者無法直接通過頁面回顯或錯誤信息獲取數據&#xff0c;而是通過觀察頁面的布爾狀態&#xff08;真/假&#xff09;或時間延遲來間接推斷數據庫信息。例如&#xff0c;通…

sql server 2016 版本補丁說明

包信息和發布類型 Microsoft為創建和分發的 SQL Server 的所有軟件更新包采用了標準化命名架構。 軟件更新包是一個可執行文件&#xff08;.exe 或 .msi&#xff09;文件&#xff0c;其中包含一個或多個文件&#xff0c;這些文件可能應用于 SQL Server 安裝以更正特定問題。 …

STM32之I2C硬件外設

注意&#xff1a;硬件I2C的引腳是固定的 SDA和SCL都是復用到外部引腳。 SDA發送時數據寄存器的數據在數據移位寄存器空閑的狀態下進入數據移位寄存器&#xff0c;此時會置狀態寄存器的TXE為1&#xff0c;表示發送寄存器為空&#xff0c;然后往數據控制寄存器中一位一位的移送數…

從青銅到王者:六大排序算法實戰解析

前言 在編程的世界里,排序算法如同一顆璀璨的明珠,閃耀著智慧的光芒。它不僅是計算機科學的基礎知識點,更是每一位程序員必備的技能。今天,就讓我們一同走進排序算法的世界,深入探究冒泡排序、選擇排序、插入排序、快速排序、歸并排序、堆排序這六大經典算法的精髓所在,…

小程序配置webview

1.在微信公眾平臺配置業務域名 1&#xff09;包括把校驗文件放在服務器根目錄 2&#xff09;配置域名 2.在小程序中 新建文件 小程序新建頁面&#xff1a;web-view json配置&#xff1a;{ "pageOrientation": "landscape", "renderer":&qu…

不用 Tomcat?SpringBoot 項目用啥代替?

在SpringBoot框架中&#xff0c;我們使用最多的是Tomcat&#xff0c;這是SpringBoot默認的容器技術&#xff0c;而且是內嵌式的Tomcat。 同時&#xff0c;SpringBoot也支持Undertow容器&#xff0c;我們可以很方便的用Undertow替換Tomcat&#xff0c;而Undertow的性能和內存使…

線索二叉樹構造及遍歷算法

線索二叉樹構造以及遍歷算法 線索二叉樹&#xff08;中序遍歷版&#xff09;構造線索二叉樹構造雙向線索鏈表遍歷中序線索二叉樹 線索二叉樹&#xff08;中序遍歷版&#xff09; 中序遍歷找到對應結點的前驅&#xff08;土方法&#xff09; #mermaid-svg-eunGO5d2GhjLxCn5 {fo…

基于SpringBoot的“體育購物商城”的設計與實現(源碼+數據庫+文檔+PPT)

基于SpringBoot的“體育購物商城”的設計與實現&#xff08;源碼數據庫文檔PPT) 開發語言&#xff1a;Java 數據庫&#xff1a;MySQL 技術&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系統展示 系統總體模塊設計 前臺用戶登錄界面 系統首頁界面…

數據篇| App爬蟲入門(一)

App 的爬取相比 Web 端爬取更加容易,反爬蟲能力沒有那么強,而且數據大多是以 JSON 形式傳輸的,解析更加簡單。在 Web 端,我們可以通過瀏覽器的開發者工具監聽到各個網絡請求和響應過程,在 App 端如果想要查看這些內容就需要借助抓包軟件。常見抓包軟件有: ?工具名稱??…

go context學習

1.Context接口2.emptyCtx3.Deadline()方法4.Done()方法5.Err方法6.Value方法&#xff08;&#xff09;7.contex應用場景8.其他context方法 1.Context接口 Context接口只有四個方法&#xff0c;以下是context源碼。 type Context interface {Deadline() (deadline time.Time, …

在VMware Workstation Pro上輕松部署CentOS7 Linux虛擬機

首先我們需要下載VM虛擬機和Centos7的鏡像 下載并安裝VMware Workstation Pro 訪問VMware Workstation Pro官網下載 https://www.vmware.com/ 第二步&#xff1a;下載centos7鏡像 訪問centos官網下載 https://www.centos.org/ 開始部署Centos7 點擊創建新的虛擬機 這里是Cen…

Jsoup 解析商品信息時需要注意哪些細節?

在使用Jsoup解析商品信息時&#xff0c;需要注意以下細節和最佳實踐&#xff0c;以確保爬蟲的穩定性和數據的準確性&#xff1a; 1. 檢查HTML文檔的合法性 在解析之前&#xff0c;需要確認所解析的文檔是否是一份合法正確的HTML文檔。如果HTML結構不完整或存在錯誤&#xff0…

Android AudioFlinger(五)—— 揭開AudioMixer面紗

前言&#xff1a; 在 Android 音頻系統中&#xff0c;AudioMixer 是音頻框架中一個關鍵的組件&#xff0c;用于處理多路音頻流的混音操作。它主要存在于音頻回放路徑中&#xff0c;是 AudioFlinger 服務的一部分。 上一節我們講threadloop的時候&#xff0c;提到了一個函數pr…