深入淺出JVM(六)之前端編譯過程與語法糖原理

本篇文章將圍繞Java中的編譯器,深入淺出的解析前端編譯的流程、泛型、條件編譯、增強for循環、可變長參數、lambda表達式等語法糖原理

編譯器與執行引擎

編譯器

Java中的編譯器不止一種,Java編譯器可以分為:前端編譯器、即時編譯器和提前編譯器

最為常見的就是前端編譯器javac,它能夠將Java源代碼編譯為字節碼文件,它能夠優化程序員使用起來很方便的語法糖

即時編譯器是在運行時,將熱點代碼直接編譯為本地機器碼,而不需要解釋執行,提升性能

提前編譯器將程序提前編譯成本地二進制代碼

前端編譯過程

  • 準備階段: 初始化插入式注解處理器

  • 處理階段

    • 解析與填充符號表

      1. 詞法分析: 將Java源代碼的字符流轉變為token(標記)流

        • 字符: 程序編寫的最小單位
        • 標記(token) : 編譯的最小單位
        • 比如 關鍵字 static 是一個標記 / 6個字符
      2. 語法分析: 將token流構造成抽象語法樹

      3. 填充符號表: 產生符號信息和符號地址

        • 符號表是一組符號信息和符號地址構成的數據結構
        • 比如: 目標代碼生成階段,對符號名分配地址時,要查看符號表上該符號名對應的符號地址
    • 插入式注解處理器的注解處理

      1. 注解處理器處理特殊注解: 在編譯器允許注解處理器對源代碼中特殊注解作處理,可以讀寫抽象語法樹中任意元素,如果發生了寫操作,就要重新解析填充符號表

        • 比如: Lombok通過特殊注解,生成get/set/構造器等方法
    • 語義分析與字節碼生成

      1. 標注檢查: 對語義靜態信息的檢查以及常量折疊優化

        ?int i = 1;char c1 = 'a';int i2 = 1 + 2;//編譯成 int i2 = 3 常量折疊優化char c2 = i + c1; //編譯錯誤 標注檢查 檢查語法靜態信息 

        image-20210524202623150.png

      2. 數據及控制流分析: 對程序運行時動態檢查

        • 比如方法中流程控制產生的各條路是否有合適的返回值
      3. 解語法糖: 將(方便程序員使用的簡潔代碼)語法糖轉換為原始結構

      4. 字節碼生成: 生成<init>,<clinit>方法,并根據上述信息生成字節碼文件

前端編譯流程圖

image-20210524205803664.png

源碼分析

image-20210524222754508.png 代碼位置在JavaCompiler的compile方法中

image-20210524221445424.png

Java中的語法糖

泛型

將操作的數據類型指定為方法簽名中一種特殊參數,作用在方法、類、接口上時稱為泛型方法、泛型類、泛型接口

Java中的泛型是類型擦除式泛型,泛型只在源代碼中存在,在編譯期擦除泛型,并在相應的地方加上強制轉換代碼

與具現化式泛型(不會擦除,運行時也存在泛型)對比

  • 優點: 只需要改動編譯器,Java虛擬機和字節碼指令不需要改變

    • 因為泛型是JDK5加入的,為了滿足對以前版本代碼的兼容采用類型擦除式泛型
  • 缺點: 性能較低,使用沒那么方便

    • 為提供基本類型的泛型,只能自動拆裝箱,在相應的地方還會加速強制轉換代碼,所以性能較低

    • 運行期間無法獲取到泛型類型信息

      • 比如書寫泛型的List轉數組類型時,需要在方法的參數中指定泛型類型

        ?public static <T> T[] listToArray(List<T> list,Class<T> componentType){T[] instance = (T[]) Array.newInstance(componentType, list.size());return instance;}
增強for循環與可變長參數

image-20210524213429033.png

增強for循環 -> 迭代器

可變長參數 -> 數組裝載參數

泛型擦除后會在某些位置插入強制轉換代碼

自動拆裝箱

自動裝箱、拆箱的錯誤用法

?        Integer a = 1;Integer b = 2;Integer c = 3;Integer d = 3;Integer e = 321;Integer f = 321;Long g = 3L;//trueSystem.out.println(c == d);//范圍小,在緩沖池中//falseSystem.out.println(e == f);//范圍大,不在緩沖池中,比較地址因此為false//trueSystem.out.println(c == (a + b));//trueSystem.out.println(c.equals(a + b));//falseSystem.out.println(g == (b + a));//trueSystem.out.println(g.equals(a + b));
  • 注意:

    1. 包裝類重寫的equals方法中不會自動轉換類型 image-20210524213853321.png

    2. 包裝類的 == 就是去比較引用地址,不會自動拆箱

條件編譯

布爾類型 + if語句 : 根據布爾值類型的真假,編譯器會把分支中不成立的代碼塊消除(解語法糖)

image-20210524214427206.png

Lambda原理

編寫函數式接口

?@FunctionalInterfaceinterface LambdaTest {void lambda();}

編寫測試類

?public class Lambda {private int i = 10;?public static void main(String[] args) {test(() -> System.out.println("匿名內部類實現函數式接口"));}?public static void test(LambdaTest lambdaTest) {lambdaTest.lambda();}}

使用插件查看字節碼文件

image-20210524230643123.png 生成了一個私有靜態的方法,這個方法中很明顯就是lambda中的代碼

在使用lambda表達式的類中隱式生成一個靜態私有的方法,這個方法代碼塊就是lambda表達式中寫的代碼

image-20210524232010510.png 執行class文件時帶上參數java -Djdk.internal.lambda.dumpProxyClasses 包名.類名即可顯示出這個匿名內部類

image-20210527083659256.png

使用invokedynamic生成了一個實現函數式接口的匿名內部類對象,在重寫函數式接口的方法實現中調用使用lambda表達式類中隱式生成的靜態私有方法

總結

本篇文章以Java中編譯器的分類為開篇,深入淺出的解析前端編譯的流程,Java中泛型、增強for循環、可變長參數、自動拆裝箱、條件編譯以及Lambda等語法糖的原理

前端編譯先將字符流轉換為token流,再將token流轉換為抽象語法樹,填充符號表的符號信息、符號地址,然后注解處理器處理特殊注解(比如Lombok生成get、set方法),對語法樹發生寫改動則要重新解析、填充符號,接著檢查語義靜態信息以及常量折疊,對運行時程序進行動態檢查,再解語法糖,生成init實例方法、clinit靜態方法,最后生成字節碼文件

Java中為了兼容之前的版本使用類型擦除式的泛型,在編譯期間擦除泛型并在相應位置加上強制轉換,想為基本類型使用泛型只能搭配自動拆裝箱一起使用,性能有損耗且在運行時無法獲取泛型類型

增加for循環則是使用迭代器實現,并在適當位置插入強制轉換;可變長參數則是創建數組進行裝載參數

自動拆裝箱提供基本類型與包裝類的轉換,但包裝類盡量不使用==,這是去比較引用地址,同類型比較使用equals

條件編譯會在if-else語句中根據布爾類型將不成立的分支代碼塊消除

lambda原理則是通過invokeDynamic指令動態生成實現函數式接口的匿名對象,匿名對象重寫函數時接口方法中調用使用lambda表達式類中隱式生成的靜態私有的方法(該方法就是lambda表達式中的代碼內容)

最后(不要白嫖,一鍵三連求求拉~)

本篇文章筆記以及案例被收入 gitee-StudyJava、 github-StudyJava 感興趣的同學可以stat下持續關注喔~

有什么問題可以在評論區交流,如果覺得菜菜寫的不錯,可以點贊、關注、收藏支持一下~

關注菜菜,分享更多干貨,公眾號:菜菜的后端私房菜

本文由博客一文多發平臺 OpenWrite 發布!

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

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

相關文章

(提供數據集下載)基于大語言模型LangChain與ChatGLM3-6B本地知識庫調優:數據集優化、參數調整、Prompt提示詞優化實戰

文章目錄 &#xff08;提供數據集下載&#xff09;基于大語言模型LangChain與ChatGLM3-6B本地知識庫調優&#xff1a;數據集優化、參數調整、提示詞Prompt優化本地知識庫目標操作步驟問答測試的預設問題原始數據情況數據集優化&#xff1a;預處理&#xff0c;先后準備了三份數據…

mac下C、C++項目出現‘stdio.h’ file not found的解決方法

【轉載】https://www.cnblogs.com/yongfengnice/p/14260997.html 有時候更新mac系統或者項目配置之后&#xff0c;打開之前的項目&#xff0c;發現出現莫名其妙的‘stdio.h’ file not found等頭文件找不到。 解決這個問題之前&#xff0c;我們要弄清楚開發工具是引用了系統哪…

C++:STL簡介

1. 什么是STL STL(standard template libaray- 標準模板庫 ) &#xff1a; 是 C 標準庫的重要組成部分 &#xff0c;不僅是一個可復用的組件庫&#xff0c;而且 是一個包羅數據結構與算法的軟件框架 。 2. STL的版本 3. STL的六大組件 4.STL的缺陷 1. STL庫的更新太慢了。這…

用于將Grafana默認數據庫sqlite3遷移到MySQL數據庫

以下是一個方案&#xff0c;用于將Grafana數據遷移到MySQL數據庫。 背景: grafana 默認采用的是sqlite3&#xff0c;當我們要以集群形式部署的時使用mysql較為方便&#xff0c;試了很多sqlite轉mysql的方法要么收費,最后放棄。選擇自己動手風衣足食。 目標: 遷移sqlite3切換…

速評谷歌開源大模型Gemma 7B

大家好,我是herosunly。985院校碩士畢業,現擔任算法研究員一職,熱衷于機器學習算法研究與應用。曾獲得阿里云天池比賽第一名,CCF比賽第二名,科大訊飛比賽第三名。擁有多項發明專利。對機器學習和深度學習擁有自己獨到的見解。曾經輔導過若干個非計算機專業的學生進入到算法…

day16_ListSet課后練習題 - 參考答案

文章目錄 day16_課后練習題第1題第2題第3題第4題第5題第6題第7題第8題 day16_課后練習題 第1題 案例&#xff1a; ? 1、用一個String[]數組存點數 ? 2、用一個String[]數組存花色 ? 3、用一個String[]數組存大王、小王 ? 4、用上面的數組&#xff0c;生成一副撲克牌 …

C++ 文件操作-文本文件-讀取和打開文件方法詳解

讀文件步驟 #include <iostream> using namespace std; #include <fstream> #include <string> //文本文件 讀文件void test(){// 1 包含頭文件// 2 創建流對象ifstream ifs;// 3 打開文件 并且判斷是否打開成功ifs.open("table.txt",ios::in); //…

VS 2015 發布 WebService

本文介紹了使用VS2015發布WebService的步驟 右鍵項目點擊發布 選擇文件系統和目標位置 配置選擇Debug-Any CPU&#xff08;選其他也可以&#xff09; 4. 點擊發布&#xff0c;在對應文件夾中可以看到發布出來的內容。 記錄遇到的問題&#xff0c; 發布前要選擇刪除所有現有文…

【PostgreSQL】PostgreSQL詳細介紹

PostgreSQL詳細介紹 一、什么是PostgreSQL&#xff1f;二、為什么要使用PostgreSQL&#xff1f;三、PostgreSQL功能列表3.1 數據類型3.2 數據完整性3.3 并發&#xff0c;性能3.4 可靠性、災難恢復3.5 安全3.6 可擴展性3.7 國際化&#xff0c;文本搜索 四、參考資料 關于Postgre…

使用MongoDB數據庫和Mongoose庫在Node.js中進行數據存儲

在Node.js中使用MongoDB數據庫和Mongoose庫進行數據存儲是前端開發中常用的技術之一。MongoDB是一種非關系型數據庫&#xff0c;具有高性能、易擴展等優點&#xff1b;而Mongoose是在Node.js中對MongoDB進行操作的框架&#xff0c;簡化了數據庫操作&#xff0c;并提供了豐富的功…

音視頻技術-雙聲道立體聲與卡儂平衡線的“糾葛”

目錄 一、新問題 二、問題排查 三、故障總結 四、原理分析 五、解決方案 1、救急 2、轉接線1 3、轉接線2

Js的 Promise的 then catch 筆記240222

Js的 Promise的 then catch 筆記240222 基本用法 new Promise(f>{setTimeout(ev>{f("一秒后輸出控制臺");},1000); }).then(f的參數>{console.log(f的參數); }); // 控制臺輸出: 一秒后輸出控制臺上面代碼中, f 的標準名叫做 resolve , 所以應該寫成 new …

知識圖譜數據處理流程是什么

在當今信息時代&#xff0c;數據已經成為企業決策和業務發展的重要驅動力。然而&#xff0c;隨著數據量的不斷增加&#xff0c;傳統的數據處理方法已經難以滿足需求。知識圖譜作為一種新興的技術&#xff0c;正逐漸成為處理大規模數據的關鍵工具。本文將深入探討知識圖譜的數據…

寫代碼中的一些“小技巧”

目錄 前言 1.不注重代碼格式 1.1 空格 1.2 換行 2.隨意的命名 2.1 有意義的參數名 2.2 見名知意 2.3 參數名風格一致 3.出現大量重復代碼 4.從不寫注釋 5.方法過長 6.參數過多 7.代碼層級太深 8.判斷條件太多 9.硬編碼 10.事務過大 11.在循環中遠程調用 11.1 …

CSS 面試題匯總

CSS 面試題匯總 1. 介紹下 BFC 及其應 參考答案&#xff1a; 參考答案&#xff1a; 所謂 BFC&#xff0c;指的是一個獨立的布局環境&#xff0c;BFC 內部的元素布局與外部互不影響。 觸發 BFC 的方式有很多&#xff0c;常見的有&#xff1a; 設置浮動overflow 設置為 auto、scr…

Swift基礎知識:20.Swift方法

在 Swift 中&#xff0c;方法是與特定類型相關聯的函數。方法可以用于實例類型&#xff08;實例方法&#xff09;或類型本身&#xff08;類型方法&#xff09;。方法允許類型的實例執行特定的任務&#xff0c;也可以修改實例本身或實例的屬性。 實例方法&#xff08;Instance …

【力扣hot100】刷題筆記Day10

前言 一鼓作氣把鏈表給刷完&#xff01;&#xff01;中等題困難題沖沖沖啊啊啊&#xff01; 25. K 個一組翻轉鏈表 - 力扣&#xff08;LeetCode&#xff09; 模擬 class Solution:def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:# 翻轉…

題記(46)--兩個多項式的和

目錄 一、題目內容 二、輸入描述 三、輸出描述 四、輸入輸出示例 五、完整C語言代碼 一、題目內容 輸入兩個多項式&#xff0c;計算它們的和。 每個多項式有若干對整數表示&#xff0c;每組整數中&#xff0c;第一個整數表示系數&#xff08;非0&#xff09;&#xff0c;第…

#LLM入門|Prompt#1.7_文本拓展_Expanding

輸入簡短文本&#xff0c;生成更加豐富的長文。 “溫度”&#xff08;temperature&#xff09;&#xff1a;控制文本生成的多樣性。 一、定制客戶郵件 根據客戶的評價和其中的情感傾向&#xff0c;使用大語言模型針對性地生成回復郵件。將大大提升客戶滿意度。 # 我們可以在…

云原生 - K8s命令合集

我是南城余&#xff01;阿里云開發者平臺專家博士證書獲得者&#xff01; 歡迎關注我的博客&#xff01;一同成長&#xff01; 一名從事運維開發的worker&#xff0c;記錄分享學習。 專注于AI&#xff0c;運維開發&#xff0c;windows Linux 系統領域的分享&#xff01; 知…