当前位置: 首页 > news >正文

JVM之类加载与字节码(二)

3. 编译期处理

什么是语法糖

所谓的 语法糖 ,其实就是指 java 编译器把 *.java 源码编译为 *.class 字节码的过程中,自动生成 和转换的一些代码,主要是为了减轻程序员的负担,算是 java 编译器给我们的一个额外福利(给糖吃)

以下代码分析的前提

注意,以下代码的分析,借助了 javap 工具、idea 的反编译功能、idea 插件、jclasslib 等工具。另外, 编译器转换的结果直接就是 class 字节码,只是为了便于阅读,给出了 几乎等价 的 java 源码方式,并不是编译器还会转换出中间的 java 源码,切记。

3.1 默认构造器

public class Candy1 {
}

编译成class后的代码:

public class Candy1 {// 这个无参构造是编译器帮助我们加上的public Candy1() {super(); // 即调用父类 Object 的无参构造方法,即调用 java/lang/Object."
<init>":()V}
}

3.2 自动拆装箱

这个特性是 JDK 5 开始加入的, 代码片段1 :

public class Candy2 {public static void main(String[] args) {Integer x = 1;int y = x;}
}

这段代码在 JDK 5 之前是无法编译通过的,必须改写为 代码片段2 :

public class Candy2 {public static void main(String[] args) {Integer x = Integer.valueOf(1);int y = x.intValue();}
}

        显然之前版本的代码太麻烦了,需要在基本类型和包装类型之间来回转换(尤其是集合类中操作的都是包装类型),因此这些转换的事情在 JDK 5 以后都由编译器在编译阶段完成。即 代码片段1 都会在编 译阶段被转换为 代码片段2

3.3 泛型集合取值

泛型也是在 JDK 5 开始加入的特性,但 java 在编译泛型代码后会执行 泛型擦除 的动作,即泛型信息 在编译为字节码之后就丢失了,实际的类型都当做了 Object 类型来处理:

public class Candy3 {public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(10); // 实际调用的是 List.add(Object e)Integer x = list.get(0); // 实际调用的是 Object obj = List.get(int index);}
}

所以在取值时,编译器真正生成的字节码中,还要额外做一个类型转换的操作:

// 需要将 Object 转为 Integer
Integer x = (Integer)list.get(0)

如果前面的 x 变量类型修改为 int 基本类型那么最终生成的字节码是:

// 需要将 Object 转为 Integer, 并执行拆箱操作
int x = ((Integer)list.get(0)).intValue();

擦除的是字节码上的泛型信息,可以看到 LocalVariableTypeTable 仍然保留了方法参数泛型的信息

public cn.itcast.jvm.t3.candy.Candy3();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."
<init>":()V4: returnLineNumberTable:line 6: 0LocalVariableTable:Start Length Slot Name Signature0     5     0   this Lcn/itcast/jvm/t3/candy/Candy3;public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=3, args_size=10: new #2 // class java/util/ArrayList3: dup4: invokespecial #3 // Method java/util/ArrayList."
<init>":()V7: astore_18: aload_19: bipush 1011: invokestatic #4 // Method
java/lang/Integer.valueOf:(I)Ljava/lang/Integer;14: invokeinterface #5, 2 // InterfaceMethod
java/util/List.add:(Ljava/lang/Object;)Z19: pop20: aload_121: iconst_022: invokeinterface #6, 2 // InterfaceMethod
java/util/List.get:(I)Ljava/lang/Object;27: checkcast #7 // class java/lang/Integer30: astore_231: returnLineNumberTable:line 8: 0line 9: 8line 10: 20line 11: 31LocalVariableTable:Start Length Slot Name Signature0     32     0   args [Ljava/lang/String;8     24     1   list Ljava/util/List;LocalVariableTypeTable:Start Length Slot Name Signature8    24     1   list Ljava/util/List<Ljava/lang/Integer;>;

使用反射,仍然能够获得这些信息:

public Set<Integer> test(List<String> list, Map<Integer, Object> map) {
}
Method test = Candy3.class.getMethod("test", List.class, Map.class);
Type[] types = test.getGenericParameterTypes();
for (Type type : types) {if (type instanceof ParameterizedType) {ParameterizedType parameterizedType = (ParameterizedType) type;System.out.println("原始类型 - " + parameterizedType.getRawType());Type[] arguments = parameterizedType.getActualTypeArguments();for (int i = 0; i < arguments.length; i++) {System.out.printf("泛型参数[%d] - %s\n", i, arguments[i]);}}
}

输出

原始类型 - interface java.util.List
泛型参数[0] - class java.lang.String
原始类型 - interface java.util.Map
泛型参数[0] - class java.lang.Integer
泛型参数[1] - class java.lang.Object

3.4 可变参数

可变参数也是 JDK 5 开始加入的新特性:

Java代码

public class Candy4 {public static void foo(String... args) {String[] array = args; // 直接赋值System.out.println(array);}public static void main(String[] args) {foo("hello", "world");}
}

被编译器转换后的代码

可变参数 String... args 其实是一个 String[] args ,从下面的代码中就可以看出来

public class Candy4 {public static void foo(String[] args) {String[] array = args; // 直接赋值System.out.println(array);}public static void main(String[] args) {foo(new String[]{"hello", "world"});}
}

注意:如果调用了 foo() 则等价代码为 foo(new String[]{}) ,创建了一个空的数组,而不会传递 null 进去

3.5 foreach 循环

仍是 JDK 5 开始引入的语法糖

1.数组的 foreach 循环

Java代码

public class Candy5_1 {public static void main(String[] args) {int[] array = {1, 2, 3, 4, 5}; // 数组赋初值的简化写法也是语法糖哦for (int e : array) {System.out.println(e);}}
}

被编译器转换后的代码

public class Candy5_1 {public Candy5_1() {}public static void main(String[] args) {int[] array = new int[]{1, 2, 3, 4, 5};for(int i = 0; i < array.length; ++i) {int e = array[i];System.out.println(e);}}
}

2.集合的 foreach 循环

Java代码

public class Candy5_2 {public static void main(String[] args) {List<Integer> list = Arrays.asList(1,2,3,4,5);for (Integer i : list) {System.out.println(i);}}
}

被编译器转换后的代码

实际被编译器转换为对迭代器的调用

public class Candy5_2 {public Candy5_2() {}public static void main(String[] args) {List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);Iterator iter = list.iterator();while(iter.hasNext()) {Integer e = (Integer)iter.next();System.out.println(e);}}
}

注意:foreach 循环写法,能够配合数组,以及所有实现了 Iterable 接口的集合类一起使用,其中 Iterable 用来获取集合的迭代器( Iterator )

3.6 switch

字符串从 JDK 7 开始,switch 可以作用于字符串和枚举类,这个功能其实也是语法糖

1. switch 字符串

Java代码

public class Candy6_1 {public static void choose(String str) {switch (str) {case "hello": {System.out.println("h");break;}case "world": {System.out.println("w");break;}}}
}

注意 switch 配合 String 和枚举使用时,变量不能为null,原因分析完语法糖转换后的代码应当自然清楚

被编译器转换后的代码

public class Candy6_1 {public Candy6_1() {}public static void choose(String str) {byte x = -1;switch(str.hashCode()) {case 99162322: // hello 的 hashCodeif (str.equals("hello")) {x = 0;}break;case 113318802: // world 的 hashCodeif (str.equals("world")) {x = 1;}}switch(x) {case 0:System.out.println("h");break;case 1:System.out.println("w");}}
}

可以看到,执行了两遍 switch,第一遍是根据字符串的 hashCode 和 equals 将字符串的转换为相应 byte 类型,第二遍才是利用 byte 执行进行比较。

为什么第一遍时必须既比较 hashCode,又利用 equals 比较呢?hashCode 是为了提高效率,减少可能的比较;而 equals 是为了防止 hashCode 冲突,例如 BM 和 C,这两个字符串的hashCode值都是 2123 ,如果有如下Java代码:

public class Candy6_2 {public static void choose(String str) {switch (str) {case "BM": {System.out.println("h");break;}case "C.": {System.out.println("w");break;}}}
}

被编译器转换后的代码

public class Candy6_2 {public Candy6_2() {}public static void choose(String str) {byte x = -1;switch(str.hashCode()) {case 2123: // hashCode 值可能相同,需要进一步用 equals 比较if (str.equals("C.")) {x = 1;} else if (str.equals("BM")) {x = 0;}default:switch(x) {case 0:System.out.println("h");break;case 1:System.out.println("w");}}}
}

2.switch 枚举

Java代码

enum Sex {MALE, FEMALE
}
public class Candy7 {public static void foo(Sex sex) {switch (sex) {case MALE:System.out.println("男"); break;case FEMALE:System.out.println("女"); break;}}
}

被编译器转换后的代码

public class Candy7 {/*** 定义一个合成类(仅 jvm 使用,对我们不可见)* 用来映射枚举的 ordinal 与数组元素的关系* 枚举的 ordinal 表示枚举对象的序号,从 0 开始* 即 MALE 的 ordinal()=0,FEMALE 的 ordinal()=1*/static class $MAP {// 数组大小即为枚举元素个数,里面存储case用来对比的数字static int[] map = new int[2];static {map[Sex.MALE.ordinal()] = 1;map[Sex.FEMALE.ordinal()] = 2;}}public static void foo(Sex sex) {int x = $MAP.map[sex.ordinal()];switch (x) {case 1:System.out.println("男");break;case 2:System.out.println("女");break;}}
}

3.7 枚举类

相关文章:

JVM之类加载与字节码(二)

3. 编译期处理 什么是语法糖 所谓的 语法糖 &#xff0c;其实就是指 java 编译器把 *.java 源码编译为 *.class 字节码的过程中&#xff0c;自动生成 和转换的一些代码&#xff0c;主要是为了减轻程序员的负担&#xff0c;算是 java 编译器给我们的一个额外福利&#xff08;给…...

安装linux操作系统

安装虚拟机的步骤&#xff1a; 安装linux系统 之后开启虚拟机 之后重启&#xff0c;打开虚拟机&#xff0c;登录root账号...

【SpringBoot】知识

.第一个程序HelloWorld 项目创建方式&#xff1a;使用 IDEA 直接创建项目 1、创建一个新项目 2、选择spring initalizr &#xff0c; 可以看到默认就是去官网的快速构建工具那里实现 3、填写项目信息 4、选择初始化的组件&#xff08;初学勾选 Web 即可&#xff09; 5、填…...

react ant add/change created_at

1.引入ant的 Table import { Table, Space, Button, message } from antd; 2.获得接口的数据的时候增加上创建时间 const response await axios.get(${Config.BASE_URL}/api/v1/calculation_plans?token${getToken()});if (response.data.message ok) {const data respon…...

OSPF 动态路由协议 路由传递

影响OSPF路由选择的因素&#xff1a; 1.OSPF路由的开销值&#xff1a;宽带参考值默认为100. COST1000/接口带宽。此时接口 带宽的值可更改&#xff0c;更改后只改变参考数值&#xff0c;带宽仍然为初始值。 注意&#xff1a;更改COST需要 在路由的入方向&#xff0c;数据的出方…...

5.kubeadm安装

文章目录 kubeadm部署环境初始化所有的节点安装Docker所有节点安装kubeadm&#xff0c;kubelet和kubectl初始化方法一&#xff0c;配置文件初始化方法二&#xff0c;命令初始化 网络插件node节点总结 证书过期方法一方法二总结 部署Dashboard kubeadm部署 环境初始化 ###所有…...

【雕爷学编程】Arduino动手做(180)---Seeeduino Lotus开发板2

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…...

6.5 池化层

是什么&#xff1a;池化层跟卷积层类似有个滑动窗口&#xff0c;用来取一个区域内的最大值或者平均值。 作用&#xff1a;卷积神经网络的最后的部分应该要看到整个图像的全局&#xff0c;通过池化(汇聚)操作&#xff0c;逐渐汇聚要取的像素&#xff0c;最终实现学习全局表示的…...

etcd

文章目录 etcd单机安装设置键值对watch操作读取键过往版本的值压缩修订版本lease租约&#xff08;过期机制&#xff09;授予租约撤销租约keepAlive续约获取租约信息 事务基于etcd实现分布式锁原生实现官方 concurrency 包实现 服务注册与发现Go 操作 Etcd 参考 etcd etcd 是一…...

W5500-EVB-PICO做DNS Client进行域名解析(四)

前言 在上一章节中我们用W5500-EVB-PICO通过dhcp获取ip地址&#xff08;网关&#xff0c;子网掩码&#xff0c;dns服务器&#xff09;等信息&#xff0c;给我们的开发板配置网络信息&#xff0c;成功的接入网络中&#xff0c;那么本章将教大家如何让我们的开发板进行DNS域名解析…...

单例模式(C++)

定义 保证一个类仅有一个实例&#xff0c;并提供一个该实例的全局访问点。 应用场景 在软件系统中&#xff0c;经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例&#xff0c;才能确保它们的逻辑正确性、以及良好的效率。如何绕过常规的构造器&#xff0c;提供一种…...

LeetCode 热题 100 JavaScript--234. 回文链表

function ListNode(val, next) {this.val val undefined ? 0 : val;this.next next undefined ? null : next; }var isPalindrome function (head) {if (!head || !head.next) {return true; }// 使用快慢指针法找到链表的中间节点let slow head;let fast head;while …...

Redis 6.5 服务端开启多线程源码

redis支持开启多线程&#xff0c;只有从socket到读取缓冲区和从输出缓冲区到socket这两段过程是多线程&#xff0c;而命令的执行还是单线程&#xff0c;并且是由主线程执行 借鉴&#xff1a;【Redis】事件驱动框架源码分析&#xff08;多线程&#xff09; 一、main启动时初始化…...

嵌入式面试笔试刷题(day6)

文章目录 前言一、进程和线程的区别二、共享内存的原理三、中断有传参和返回值吗四、串口数据帧格式五、进程通信有几种&#xff0c;哪几种需要借助内核1.方式2.需要借助内核的 六、flash有哪几种类型七、指针的本质是什么八、指针和数组的区别九、使用宏定义交换变量不能使用中…...

24考研数据结构-第五章:树与二叉树

目录 第五章&#xff1a;树5.1树的基本概念5.1.1树的定义5.1.2 基本术语5.1.3 树的性质 5.2二叉树的概念5.2.1 二叉树的定义与特性5.2.2 几种特殊的二叉树5.2.3 二叉树的性质5.2.4 完全二叉树的性质5.2.5 二叉树的存储结构1. 顺序存储重要的基本操作非完全二叉树2. 链式存储逆向…...

构建稳健的微服务架构:关键的微服务设计原则和最佳实践

在现代软件开发中&#xff0c;微服务架构正逐渐成为构建复杂应用程序的首选方法之一。微服务架构的核心理念是将应用程序划分为一系列小型、自治的服务&#xff0c;每个服务专注于一个特定的业务功能。然而&#xff0c;要实现一个稳健的微服务架构并不仅仅是将功能拆分成微服务…...

消息队列常见问题(1)-如何保障不丢消息

目录 1. 为什么消息队列会丢消息&#xff1f; 2. 怎么保障消息可靠传递&#xff1f; 2.1 生产者不丢消息 2.2 服务端不丢消息 2.3 消费者不丢消息 3. 消息丢失如何快速止损&#xff1f; 3.1 完善监控 3.2 完善止损工具 1. 为什么消息队列会丢消息&#xff1f; 现在主流…...

Circle of Mistery 2023牛客暑期多校训练营5 B

登录—专业IT笔试面试备考平台_牛客网 题目大意&#xff1a;给出一个n个数的数组a&#xff0c;求一个排列&#xff0c;使其形成的其中一个置换环上的数的和>k&#xff0c;并使产生的逆序对数量最少 1<n<1e3;-1e6<k<1e6;-1e6<ai<1e6 tips:关于置换环是什…...

VC9、VC10、VC11等等各对应什么版本的Visual Studio,以及含义

文章目录 1、_MSC_VER 定义编译器的版本2、示例 1、_MSC_VER 定义编译器的版本 MS VC 15.0 _MSC_VER 1910 (Visual Studio 2017) MS VC 14.0 _MSC_VER 1900 (Visual Studio 2015) MS VC 12.0 _MSC_VER 1800 (VisualStudio 2013) MS VC 11.0 _MSC_VER 1700 (VisualStudio…...

两数相加 LeetCode热题100

题目 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数都不会…...

Python基础 P2数字类型与优先级进阶练习

文章目录 Python基础 P2数字类型与优先级进阶练习1.闰年判断器2.进制转换及求和3.单位转换 Python基础 P2数字类型与优先级进阶练习 1.闰年判断器 简介 对于闰年的判断就是判断输入的内容类型是否符合要求&#xff0c;然后通过逻辑判断和运算得出该年份是否为闰年 举个栗子 …...

CAPL通过继电器实现CAN容错性自动化测试

系列文章目录 文章目录 系列文章目录前言一、环境搭建1.硬件环境2.软件环境3.继电器线路连接图:二、容错性测试方法1.CAN_H与CAN_L短路2.CAN_H与GND短路3.CAN_L与GND短路4.CAN_H与电源短路5.CAN_L与电源短路6.CAN_H断路7.CAN_L断路三、CAPL自动化测试1.测试用例目录2.测试报告…...

elasticsearch 配置用户名和密码

无密码的其他配置项在&#xff1a;https://blog.csdn.net/Xeon_CC/article/details/132064295 elasticsearch.yml配置文件&#xff1a; xpack.security.enabled: true xpack.security.http.ssl.enabled: true xpack.security.http.ssl.keystore.path: /path/to/elastic-certi…...

侯捷 C++面向对象编程笔记——9 复合 委托

9 复合 委托 9.1 Composition 复合 类似于c中结构里有结构——class里有class deque 是一个已经存在的功能很多的类&#xff08;两头进出的队列&#xff09;&#xff1b;利用deque的功能来实现queue的多种操作 该例只是复合的一种情况——设计模式 Adapter 9.1.1 复合下的构造…...

状态模式——对象状态及其转换

1、简介 1.1、概述 在软件系统中&#xff0c;有些对象也像水一样具有多种状态&#xff0c;这些状态在某些情况下能够相互转换&#xff0c;而且对象在不同的状态下也将具有不同的行为。为了更好地对这些具有多种状态的对象进行设计&#xff0c;可以使用一种被称为状态模式的设…...

Linux一阶段复习

Linux之父是林纳斯本纳第克特托瓦兹 Apache发布目录&#xff1a;/var/www/html nginx发布目录&#xff1a;/usr/share/nginx/html/ 配置dns的文件 &#xff1a; /etc/resolv.conf nginx的配置文件&#xff1a;/etc/nginx/ yum源的配置文件&#xff1a;/etc/yum.repos.d/ …...

宝塔Linux面板怎么升级?升级命令及失败解决方法

宝塔Linux面板怎么升级到新版本&#xff1f;root账号ssh登录到云服务器后&#xff0c;执行宝塔Linux面板升级命令即可搞定&#xff0c;新手站长分享宝塔Linux面板升级命令&#xff1a; 宝塔面板升级到新版本 1、使用root账号ssh登录到云服务器上 ssh root你的云服务器ip地址…...

前端面试的性能优化部分(6)每天10个小知识点

目录 系列文章目录前端面试的性能优化部分&#xff08;1&#xff09;每天10个小知识点前端面试的性能优化部分&#xff08;2&#xff09;每天10个小知识点前端面试的性能优化部分&#xff08;3&#xff09;每天10个小知识点前端面试的性能优化部分&#xff08;4&#xff09;每天…...

2023年 Java 面试八股文(20w字)

目录 第一章-Java基础篇 1、你是怎样理解OOP面向对象 难度系数&#xff1a;⭐ 2、重载与重写区别 难度系数&#xff1a;⭐ 3、接口与抽象类的区别 难度系数&#xff1a;⭐ 4、深拷贝与浅拷贝的理解 难度系数&#xff1a;⭐ 5、sleep和wait区别 难度系数&a…...

银河麒麟服务器ky10-server在线一键安装docker

脚本代码 # ---------------在线安装docker------------------- yum install docker -y # 修改docker拉取源为国内 rm -rf /etc/docker mkdir -p /etc/docker touch /etc/docker/daemon.json cat >/etc/docker/daemon.json<<EOF{"registry-mirrors": [&q…...