final修飾符不可變的底層

final修飾符的底層原理

在 Java 中,final 修飾符的底層實現涉及 編譯器優化JVM 字節碼層面的約束

其核心目標是保證被修飾元素的【不可變性】或 【不可重寫 / 繼承性】


一、final 修飾類:禁止繼承的底層約束

當一個類被 final 修飾時,例如 String 、Integer

JVM 在字節碼層面會通過 訪問標志(access flags) 標記該類為 ACC_FINAL

  • 編譯器在編譯時會檢查:如果子類試圖繼承被 final 修飾的類,會直接拋出編譯錯誤
    【無法繼承最終類】
  • JVM 在類加載階段也會驗證這一約束,確保沒有非法繼承行為

本質

  1. 通過字節碼標記禁止繼承
  2. 屬于編譯期和類加載期的靜態約束,不涉及運行時的特殊處理

二、final 修飾方法:禁止重寫的底層機制

final 修飾方法時,字節碼中該方法的訪問標志會被標記為 ACC_FINAL

  • 編譯器在編譯子類時,若發現子類試圖重寫被 final 修飾的父類方法,會直接報錯
  • JVM 在字節碼驗證階段也會檢查方法重寫的合法性,拒絕非法重寫的類加載


private 方法的區別
隱式final:private 方法默認被隱式視為 final,但字節碼中不會標記 ACC_FINAL->方法無法被重寫,因為子類不可見

顯式 final :方法會明確標記,且可見性可以是 public/protected


三、final 修飾變量:保證不可變的底層實現

final 修飾變量(局部變量、成員變量、靜態變量)的核心是 【一旦賦值就不能被修改】
其底層實現涉及編譯期約束和運行期優化
分兩種情況:

1. 基本類型變量(如 final int a = 10)
  • 編譯期約束:編譯器會檢查變量是否只被賦值一次。若在編譯期能確定賦值(如直接賦值字面量),則會將其視為 【編譯期常量】,并可能觸發 常量折疊 優化(如將代碼中所有引用 a 的地方直接替換為10)
  • 運行期保障:若變量在編譯期無法確定值(如通過方法返回值賦值,final int a = getValue())
    JVM 會在字節碼中通過 putfield(成員變量)或 astore(局部變量)指令賦值后,禁止后續對該變量的寫操作(編譯器會攔截所有二次賦值的代碼,直接報錯)

2. 引用類型變量(如 final List<String> list = new ArrayList<>())

final 對引用類型的約束是 【引用不可變】但對象本身的內容可以修改(如 list.add("a") 是允許的)

  • 底層通過字節碼標記 ACC_FINAL 實現:編譯器會檢查引用變量是否被二次賦值(如list = new LinkedList<>()),若有則編譯報錯
  • 與基本類型不同,引用類型的 final 變量不會觸發常量折疊,因為其指向的對象內容可能在運行時變化,僅保證引用本身不變

3. final 與多線程: happens-before 規則的底層支持

final 變量在多線程環境中具有特殊的內存語義:final 修飾的變量,一旦在構造方法中初始化完成,且構造方法沒有 “逸出”(即 this 引用未被其他線程獲取),則其他線程看到的 final 變量一定是初始化后的值,無需額外同步


這一特性的底層依賴 JVM 的內存屏障

  • 在構造方法中對 final 變量賦值后,JVM 會插入 StoreStore 屏障,禁止該賦值操作與構造方法外的操作重排序,確保 final 變量的初始化對其他線程可見
  • 其他線程讀取 final 變量時,JVM 會插入 LoadLoad 屏障,禁止讀取操作與之前的操作重排序,確保讀取到的是初始化后的值

四、final 與 JIT 優化:常量傳播與不可變分析

JIT(即時編譯器)在運行時會對 final 變量進行額外優化:

  • 常量傳播:若 final 變量是編譯期常量(如 final int MAX = 100),JIT 會將代碼中所有引用 MAX 的地方直接替換為 100,減少變量訪問開銷
  • 不可變分析:對于 final 引用類型(如 final String s),JIT 可以假設其引用不會變化,從而進行更激進的優化(如避免重復計算、減少鎖競爭等)

總結:final 底層的核心邏輯

修飾對象

底層實現核心

典型場景

字節碼標記 ACC_FINAL,禁止繼承

String、Integer

等不可變類

方法

字節碼標記 ACC_FINAL

,禁止重寫

工具類中的固定邏輯方法(如 Objects.requireNonNull

變量

編譯期禁止二次賦值 + 運行期內存屏障(多線程可見性)

常量定義、多線程共享的不可變引用

final 的底層機制本質是 通過編譯期約束和運行期優化,保證 【不可變】或 【不可繼承 / 重寫】

同時為多線程環境提供了安全的內存語義,是 Java 中實現不可變性和線程安全的重要手段

?


final是如何保證多線程可見的

核心目標: 確保在多線程環境下,當一個對象被構造完成后,其他線程看到的該對象中被 final 修飾的字段的值,一定是構造方法中設置的那個值,不會看到未初始化的默認值(如 0, null 等)


關鍵前提條件:

  1. final 變量在構造方法中初始化完成
  2. 構造方法沒有“逸出”(this 引用未逃逸): 在構造方法執行結束之前,對象的 this 引用沒有被其他任何線程獲取到。這是安全發布的基礎

JVM 如何保證(底層依賴內存屏障):

為了達到這個目標,JVM 在編譯和運行時會插入特定的內存屏障指令

內存屏障可以簡單理解為阻止 CPU 或編譯器對指令進行重排序的柵欄,確保屏障前后的指令執行順序符合預期


  1. 寫屏障:

禁止對final字段賦值操作與構造方法結束后發生的所有的對final變量的寫入操作進行重排序

final字段的寫入一定發生在對象【引用發布】之前,也就是保證對象構建好后外部線程才能訪問

  1. 讀屏障 :

禁止對讀取final字段的操作與該讀取操作前的任何讀取操作進行重排序

強制要求讀取final字段時必須先去檢查最新的內存值,保證不丟失修改,保證讀取的數據值最新值


Happens-Before 規則的體現:

JMM 的 final 語義建立了一個 happens-before 關系:

  • 構造方法中對 final 字段的賦值操作 happens-before 于構造方法的結束(return)
  • 由于 StoreStore 屏障的保證,構造方法的結束 happens-before 于后續任何線程通過一個正確發布的引用對該對象的 final 字段的讀取操作
  • 因此,構造方法中對 final 字段的賦值 happens-before 于任何線程對該 final 字段的讀取。 這就是為什么讀取線程一定能看到正確初始化的值

簡單來說:

JVM 通過在寫 final 字段后加寫屏障,在讀 final 字段前加讀屏障

配合構造方法不逸出的前提,巧妙地利用了內存屏障阻止了可能導致看到未初始化值的指令重排序

從而讓 final 字段成為多線程環境下一種安全、無需額外同步就能保證可見性的常量發布機制

這就是 final 字段 happens-before 語義的底層實現基礎


final底層-簡單總結

類:字節碼標記 ACC_FINAL,禁止繼承。編譯期和類加載期會檢查約束

方法:字節碼標記 ACC_FINAL,禁止重寫。編輯期和字節碼驗證期拒絕重寫

變量:底層通過字節碼標記 ACC_FINAL禁止二次賦值 + 運行期內存屏障保證多線程可見性

-基本類型變量:若在編譯期能確定賦值則會將其視為 【編譯期常量】,并可能觸發 常量折疊 優化

-引用類型變量:final 對引用類型的約束是 【引用不可變】但對象本身的內容可以修改(如 list.add("a") 是允許的)


final的內存屏障如何保證多線程可見:

利用happen-before規則結合寫屏障和寫屏障

寫屏障:

禁止對final字段賦值操作與構造方法結束后發生的所有的對final變量的寫入操作進行重排序

final字段的寫入一定發生在對象【引用發布】之前,也就是保證對象構建好后外部線程才能訪問

讀屏障 :

禁止對讀取final字段的操作與該讀取操作前的任何讀取操作進行重排序

強制要求讀取final字段時必須先去檢查最新的內存值,保證不丟失修改,保證讀取的數據值最新值

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

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

相關文章

如何排查服務器 CPU 飆高

服務器 CPU 飆高&#xff08;CPU 使用率持續超過 80% 甚至接近 100%&#xff09;是典型的性能瓶頸問題&#xff0c;可能由應用邏輯缺陷、資源競爭、外部壓力或硬件/系統異常引起。以下是系統化的排查步驟&#xff0c;覆蓋從現象確認到根因定位的全流程。?一、確認 CPU 飆高的現…

【DataWhale】快樂學習大模型 | 202507,Task05筆記

前言 今天是Transformer的編碼實戰階段&#xff0c;照著示例代碼執行一遍吧 embedding self.tok_embeddings nn.Embedding(args.vocab_size, args.dim)把token向量轉為embedding矩陣&#xff08;一個token一個embedding向量&#xff09; 位置編碼 為了解決“我喜歡你”和…

用ffmpeg 進行視頻的拼接

author: hjjdebug date: 2025年 07月 22日 星期二 17:06:02 CST descrip: 用ffmpeg 進行視頻的拼接 文章目錄1. 指定協議為concat 方式.1.1 協議為concat 模式,會調用 concat_open 函數1.2 當讀數據時,會調用concat_read2. 指定file_format 為 concat 方式2.1 調用concat_read_…

HTTP與HTTPS技術細節及TLS密鑰交換與證書校驗全流程

HTTP與HTTPS技術細節及TLS密鑰交換與證書校驗全流程 引言 文檔目的與范圍 核心技術棧概述 本文檔的核心技術棧圍繞傳輸層安全協議&#xff08;TLS&#xff09;展開。TLS協議作為安全套接字層&#xff08;SSL&#xff09;的后繼標準&#xff0c;是現代網絡安全通信的基礎&am…

廣播分發中心-廣播注冊流程

廣播是怎么注冊的呢&#xff1f;階段組件/數據結構作用描述存儲位置/關聯關系App進程階段BroadcastReceiver開發者自定義的廣播接收器&#xff0c;實現onReceive方法處理事件。App進程&#xff08;Activity/Service等組件內&#xff09;ReceiverDispatcher將BroadcastReceiver封…

OpenCV計算機視覺實戰(16)——圖像分割技術

OpenCV計算機視覺實戰&#xff08;16&#xff09;——圖像分割技術0. 前言1. 分水嶺算法1.1 應用場景1.2 實現過程2. GrabCut 交互式分割2.1 應用場景2.2 實現過程3. FloodFill3.1 應用場景3.2 實現過程小結系列鏈接0. 前言 圖像分割是計算機視覺中將像素劃分為具有特定語義或…

Coturn打洞服務器

* 概念理解&#xff1a;1. SDP協議&#xff1a;會話描述協議&#xff0c;視頻通話的雙方通過交換SDP信息進行媒體協商&#xff0c;從而選擇使用某一相同的媒體協議進行通信&#xff1b;TLS協議&#xff1a;基于TCP的安全層傳輸協議DTLS協議&#xff1a;基于UDP的安全層傳輸協議…

python flusk 監控

# 創建虛擬環境目錄 python3 -m venv /sda1/xunjian/venv # 激活虛擬環境 source /sda1/xunjian/venv/bin/activate # 激活后終端會顯示 (venv)創建虛擬環境&#xff08;在當前目錄&#xff09;&#xff1a;bashpython3 -m venv venv激活虛擬環境&#xff1a;bashsource venv/b…

VUE2 項目學習筆記 ? 語法 v-if/v-show

?語法頁面渲染的時候&#xff0c;需要服務器傳過來的對象中的一個屬性&#xff0c;然后根據這個屬性用v-for渲染標簽&#xff0c;這里寫的v-for".... in dataList.goodsList"但是當解析到這行語法的時候&#xff0c;dataList還沒返回&#xff0c;因此控制臺會報錯找…

使用qemu命令啟動虛擬機

1. 安裝相關軟件 yum install qemu edk2* libvirt -y 啟動libvirt服務 systemctl start libvirtd systemctl status libvirtd2. 創建虛擬機 2.1. qemu啟動命令示例 /usr/bin/qemu-system-loongarch64 \-machine virt,accelkvm \-nodefaults \-m 2048 \-smp 2,maxcpus4,co…

大模型系統化學習路線

人工智能大模型系統化學習路線一、基礎理論筑基&#xff08;1-2個月) 目標&#xff1a;建立大模型核心認知框架 核心內容&#xff1a; 深度學習基礎&#xff1a;神經網絡原理、CNN/RNN結構、梯度下降算法大模型本質&#xff1a;Transformer架構&#xff08;重點掌握注意力機制、…

LLaMA-Factory 微調可配置的模型基本參數

LLaMA-Factory 微調可配置的模型基本參數 flyfish 基本參數 一、模型加載與路徑配置參數名類型描述默認值model_name_or_pathOptional[str]模型路徑&#xff08;本地路徑或 Huggingface/ModelScope 路徑&#xff09;。Noneadapter_name_or_pathOptional[str]適配器路徑&#xf…

Ubuntu 22 安裝 ZooKeeper 3.9.3 記錄

Ubuntu 22 安裝 ZooKeeper 3.9.3 記錄 本文記錄在 Ubuntu 22.04 系統上安裝 ZooKeeper 3.9.3 的過程&#xff0c;包含 Java 環境準備、配置文件調整、啟動與停機操作、以及如何將 ZooKeeper 注冊為系統服務。 一、準備環境 ZooKeeper 3.9.x 要求 Java 11 或更高版本&#xff…

FreeSwitch通過Websocket(流式雙向語音)對接AI實時語音大模型技術方案(mod_ppy_aduio_stream)

FreeSwitch通過WebSocket對接AI實時語音大模型插件技術方案1. 方案概述 基于FreeSWITCH的實時通信能力&#xff0c;通過WebSocket協議橋接AI大模型服務&#xff0c;實現低延遲、高并發的智能語音交互系統。支持雙向語音流處理、實時ASR/TTS轉換和動態業務指令執行。 1753095153…

航班調度優化策略全局概覽

在機場關閉場景下的航班恢復工作&#xff0c;是將機場關閉期間所有的航班進行取消然后恢復還是將機場關閉期間航班全部延誤而后調整呢&#xff1f;簡單來說&#xff0c;在實際操作中&#xff0c;既不是無差別地全部取消&#xff0c;也不是無差別地全部延誤。這兩種“一刀切”的…

spring boot 異步線程@Async 傳遞 threadLocal數據

將父類的 threadLocal 的數據 在線程池時&#xff0c;可以轉給子線程使用。 Async 的使用。 第一步在啟動服務加上 EnableAsync 注解。 EnableAsync public class NetCoreApplication {... ... }第二步&#xff1a;導入阿里 線程工具類<dependency><groupId>com.a…

AI產品經理成長記《零號列車》第一集 邂逅0XAI列車

《零號列車》絕非傳統意義上的 AI 產品經理教程 —— 它是我沉淀二十多年跨行業數字化轉型與工業 4.0 實戰經驗后,首創的100集大型小說體培養指南。那些曾在千行百業驗證過的知識與經驗,不再是枯燥的文字堆砌,而是化作一場沉浸式的學習旅程。? 這里沒有生硬的理論灌輸,而…

[C++11]范圍for循環/using使用

范圍for循環 范圍for循環&#xff08;Range-based for loop&#xff09;是 C11 引入的一種簡潔的循環語法&#xff0c;用于遍歷容器中的元素或者其他支持迭代的數據結構。 范圍for循環可以讓代碼更加簡潔和易讀&#xff0c;避免了傳統for循環中索引的操作。 下面是范圍for循環的…

簡單了解下npm、yarn 和 pnpm 中 add 與 install(i) 命令的區別(附上兩圖帶你一目明了)

目錄 pnpm 中 add 和 i 的區別 npm 中 add 和 i 的區別 yarn 中 add 和 i 的區別 附上兩圖帶你一目明了&#xff1a; npm、yarn和pnpm的三者區別圖&#xff1a; i 和 add 的核心區別圖&#xff1a; 個人建議&#xff1a;在項目中保持命令使用的一致性&#xff0c;選擇一種…

ESP32-S3學習筆記<2>:GPIO的應用

ESP32-S3學習筆記&#xff1c;2&#xff1e;&#xff1a;GPIO的應用1. 頭文件包含2. GPIO的配置2.1 pin_bit_mask2.2 mode2.3 pull_up_en和pull_down_en2.4 intr_type3. 設置GPIO輸出/獲取GPIO輸入4. 中斷的使用4.1 gpio_install_isr_service4.2 gpio_isr_handler_add4.3 gpio_…