深入浅出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.mzph.cn/news/697386.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战

文章目录 &#xff08;提供数据集下载&#xff09;基于大语言模型LangChain与ChatGLM3-6B本地知识库调优&#xff1a;数据集优化、参数调整、提示词Prompt优化本地知识库目标操作步骤问答测试的预设问题原始数据情况数据集优化&#xff1a;预处理&#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切换…

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; 发布前要选择删除所有现有文…

使用MongoDB数据库和Mongoose库在Node.js中进行数据存储

在Node.js中使用MongoDB数据库和Mongoose库进行数据存储是前端开发中常用的技术之一。MongoDB是一种非关系型数据库&#xff0c;具有高性能、易扩展等优点&#xff1b;而Mongoose是在Node.js中对MongoDB进行操作的框架&#xff0c;简化了数据库操作&#xff0c;并提供了丰富的功…

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…

【力扣hot100】刷题笔记Day10

前言 一鼓作气把链表给刷完&#xff01;&#xff01;中等题困难题冲冲冲啊啊啊&#xff01; 25. K 个一组翻转链表 - 力扣&#xff08;LeetCode&#xff09; 模拟 class Solution:def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:# 翻转…

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

输入简短文本&#xff0c;生成更加丰富的长文。 “温度”&#xff08;temperature&#xff09;&#xff1a;控制文本生成的多样性。 一、定制客户邮件 根据客户的评价和其中的情感倾向&#xff0c;使用大语言模型针对性地生成回复邮件。将大大提升客户满意度。 # 我们可以在…

两种动态代理(可以看到代理类的样子,方便理解)

这里写目录标题 jdk动态代理例子CGlib动态代理例子手写spring中的事务部分自定义注解版aop实现方式 Spring的两大重点&#xff0c;IOC和AOP&#xff0c;今天我们就来学AOP&#xff0c;众所周知AOP的底层是动态代理&#xff0c;让我们看一下这两种动态代理的区别。 例子&#x…

【结合OpenAI官方文档】解决Chatgpt的API接口请求速率限制

OpenAI API接口请求速率限制 速率限制以五种方式衡量&#xff1a;RPM&#xff08;每分钟请求数&#xff09;、RPD&#xff08;每天请求数&#xff09;、TPM&#xff08;每分钟令牌数&#xff09;、TPD&#xff08;每天令牌数&#xff09;和IPM&#xff08;每分钟图像数&#x…

BUUCTF第二十四、二十五题解题思路

目录 第二十四题CrackRTF 第二十五题[2019红帽杯]easyRE1 第二十四题CrackRTF 查壳 无壳&#xff0c;32位&#xff0c;用32位IDA打开&#xff0c;打开后的main函数很短&#xff0c;可以找到一句“jmz _main_0”——跳转到 _main_0&#xff0c;说明真正的主函数是_main_0&am…

React 模态框的设计(二)

自定义组件是每个前端开发者必备的技能。我们在使用现有框架时难免有一些超乎框架以处的特别的需求&#xff0c;比如关于弹窗&#xff0c;每个应用都会用到&#xff0c;但是有时我们使用的框架中提供的弹窗功能也是功能有限&#xff0c;无法满足我们的应用需求&#xff0c;今天…

隐藏饿了么el-select组件的el-select-dropdown部分,只使用el-select的显示框

隐藏饿了么el-select组件的el-select-dropdown部分,只使用el-select的显示框 问题: 由于el-select组件的el-select-dropdown部分是自动插入在最外层Body上的&#xff0c;所以在当前组件的scoped中让el-select-dropdown组件display:none不会生效所以需要&#xff1a; :popper-…

TiDB 7.5.0 LTS 高性能数据批处理方案

过去&#xff0c;TiDB 由于不支持存储过程、大事务的使用也存在一些限制&#xff0c;使得在 TiDB 上进行一些复杂的数据批量处理变得比较复杂。 TiDB 在面向这种超大规模数据的批处理场景&#xff0c;其能力也一直在演进&#xff0c;其复杂度也变得越来越低&#xff1a; ○ 从…

11.CSS3的媒介(media)查询

CSS3 的媒介(media)查询 经典真题 如何使用媒体查询实现视口宽度大于 320px 小于 640px 时 div 元素宽度变成 30% 媒体查询 媒体查询英文全称 Media Query&#xff0c;顾名思义就是会查询用户所使用的媒体或者媒介。 在现在&#xff0c;网页的浏览终端是越来越多了。用户可…