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

「JVM 编译优化」Java 语法糖(泛型、自动装箱/拆箱、条件编译)

「JVM 编译优化」Java 语法糖(泛型、自动装箱/拆箱、条件编译)

语法糖可以看做事前端编译期的一些小把戏;虽不会提供实质性的功能改进,但它们或能提高效率,或能提升语法的严谨性,或能减少编码出错的机会;不过语法糖也并不一定都是有益的,大量添加和使用含糖的语法,容易让程序员产生依赖,无法看清语法糖背后代码的真实面目(编译层面);

Java 的语法糖有泛型、自动装箱、自动拆箱、遍历循环、变长参数、条件编译、内部类、枚举类、断言语句、数值字面量、对枚举和字符串的 switch 支持、try-resource(JDK 7)、Lambda 表达式(JDK 8,不算单纯的语法糖,但前端编译器做了大量转换工作)等;

要了解小把戏背后的真实实现,才能最好的利用好它;

文章目录

      • 1. 泛型
      • 2. 自动装箱、拆箱、遍历循环
      • 3. 条件编译

1. 泛型

泛型本质是参数化类型(Parameterized Type)或参数化多态(Parametric Polymorphism)的应用,对泛化的数据类型编写相同的算法(抽象);将操作的数据类型指定为方法签名中的一种特殊参数,参数类型可用在类、接口、方法的创建中分别构造泛型类、泛型接口、泛型方法;

Java 泛型 vs. C# 泛型

  • 类型擦除式泛型Type Erasure Generics),Java 的实现方式,泛型只存在于程序源码,在编译后的字节码中所有泛型都变成原裸那类型(Raw Type),并插入相应的强制转型代码;在运行期的 ArrayList<Int>ArrayList<String> 是同一种类型;
  • 具现化式泛型Reified Generics),C# 的实现方式,泛型在程序源码、编译后的中间语言表示(Intermediate Language,泛型是一个占位符)、运行期的 CLR 里都是切实存在的;在运行期的 List<Int>List<String> 是两种不同类型,它们由系统在运行期生成;

Java 不合法的泛型用法

public class TypeErasureGenerics<E> {public void doSomething(Object item) {if (item instanceof E) {    // 不合法,无法对泛型进行实例判断;// ...}E newItem = new E();        // 不合法,无法使用泛型创建对象;E[] itemArray = new E[10];  // 不合法,无法使用泛型创建数组;}
}

相比 C# 的泛型,除了使用层面上需要更多代码、更多类型参数来编写使用;Java 的泛型在执行性能方面是难以用应用编码弥补的(需要大量拆箱装箱、构造容器,引入复杂度高的代码,降低了复用性,几乎丧失了泛型本身存在的价值);

Java 的泛型实现只需在 javac 编译器上做出改进,不需要改动字节码、JVM,保障了以前无泛型的库直接运行在新版 JDK 环境;

泛型的历史背景

《Java 语言规范》严肃承诺二进制向后兼容(Binary Backwards Compatibility);

// 协变(Covariant)演示
Object[] array = new String[10];
array[0] = 10;                      // 编译正常、运行报错;ArrayList things = new ArrayList();
things.add(Integer.valueOf(10));    // 编译、运行皆正常;
things.add("hello world");
  • C# 版)需要泛型化的类型(容器类型),以前有的保持不变,并平行添加一套泛型化版本的新类型;C# 新增一组 System.Collections.Generic 容器,原 System.Collections 和 System.Collection.Specialized 容器依旧存在;
  • Java 版)直接把所有需要泛型化的已有类型原地泛型化,不添加任何平行泛型版本;Java 也尝试了引入新的集合类,但因遗留代码规模和流行度较大、设计实现时间不足等原因,最终选择了类型擦除式实现;

类型擦除

  • 裸类型Raw Type),所有该类型泛型化的共同父类型(Super Type);
ArrayList<Integer> ilist = new ArrayList<Integer>();
ArrayList<String> slist = new ArrayList<String>();
ArrayList list;     // 裸类型
list = ilist;
list = slist;
  • 在运行期由 JVM 自动真实的构造 ArrayList 的类型,病自动实现从 ArrayList 派生自 ArrayList 的继承关系;
  • 在编译期把 ArrayList 还原会 ArrayList,只在元素反问、修改时自动插入一些类型强转和检查的指令;

泛型擦除示例

public static void main(String[] args) {Map<String, String> map = new HashMap<String, String>();map.put("hello", "你好");map.put("how are you?", "吃了吗?");System.out.println(map.get("hello"));System.out.println(map.get("how are you?"));
}

擦除后(编译后)的效果

public static void main(String[] args) {Map map = new HashMap();map.put("hello", "你好");map.put("how are you?", "吃了吗?");System.out.println((String) map.get("hello"));System.out.println((String) map.get("how are you?"));
}

擦除法的缺陷

  • 使用擦除法实现泛型时原始类型(Primitive Type)数据的支持很麻烦,因为基础类型与 Object 之间无法强转;因此 Java 的泛型直接不支持原始类型;
  • 运行期无法取到泛型类型信息;如需知道泛型类型信息,需额外通过 Class<T> 类型的参数传递进来;
  • 擦除发实现泛型导致一些面向对象思想变得模糊;
// 重载 1,无法编译通过;编译后的裸类型相同;
public class GenericTypes {public static void method(List<String> list) {System.out.println("invoke method(List<String> list)");}public static void method(List<Integer> list) {System.out.println("invoke method(List<Integer> list)");}
}// 重载 2,可以编译通过;Class 文件允许描述符不完全一致的两个方法共存;
public class GenericTypes {public static String method(List<String> list) {System.out.println("invoke method(List<String> list)");return "";}public static int void method(List<Integer> list) {System.out.println("invoke method(List<Integer> list)");return 1;}

Java 泛型的引入在 JVM 解析、反射等场景下的方法调用带来了新的需求,如泛型类中获取传入的参数化类型等;JCP 为此引入了诸如 Signature、LocalVariableTypeTable 等新的属性用于解决伴随泛型而来的参数类型识别问题;
擦除法实际仅仅对方法的 Code 属性中字节码进行了擦除,元数据中还是保留着泛型信息,反射手段是可以取到参数化类型的;

值类型与未来的泛型

Oracle 在 2014 年建立了 Valhalla 语言改进项目,用于改进 Java 语言中各种缺陷(泛型的缺陷是主要目标之一);

Valhalla 对新泛型实现规划了多种方案,如 Model 1 和 Model 3;其中泛型可能被具现化,也可能继续维持类型擦除(不完全擦除)以保兼容性;

目前比较明确的是未来 Java 会提供值类型Value Type)的语言层面支持;值类型可以与引用类型一样具有构造函数、方法、属性字段等,区别在于它的赋值通常是整体赋值,而不像引用类型的传递引用;这样值类型实例更容易实现在调用栈上分配,可以随着退出方法自动回收,从而减轻 GC 压力;

Valhalla 中的值类型方案被称为内联类型,通过一个新的关键字 inline 来定义,字节码层面则以与原生类型对应的 Q 开头的新操作码(如 iload 对应 qload)来支撑;

即时编译场景下,可以使用逃逸分析优化来处理内联类型;通过编码时标注和内联类型实例的不可变性,可以很好的解决逃逸分析面对传统引用类型时难以判断对象是否逃逸的问题;

2. 自动装箱、拆箱、遍历循环

自动装箱、自动拆箱、循环遍历(for-each 循环)等语法糖是 Java 中被使用最多的语法糖;

自动装箱、自动拆箱、循环遍历演示

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

编译后的效果

public static void main(String[] args) {// 1. 泛型被擦除;// 2. 自动装箱、拆箱被转化成对应的包装盒还原方法;// 3. 变长参数变成数组类型的参数;List list = Arrays.asList(new Integer[] {Integer.valueOf(1),Integer.valueOf(2),Integer.valueOf(3),Integer.valueOf(4)});int sum = 0;// 4. 循环遍历被还原成了迭代器的实现;这是遍历循环中被遍历的实力类需要实现 Iterable 接口的原因;for (Iterator localIterator = list.iterator(); localItertor.hasNext(); ) {int i = ((Integer) localIterator.next()).intValue();sum += i;}System.out.println(sum);
}

自动装箱的陷阱

public static void main(String[] args) {Integer a = 1;Integer b = 2;Integer c = 3;Integer d = 3;Integer e = 321;Integer f = 321;Long g = 3L;System.out.println(c == d);             // true,Integer 的享元模式实现方式让 -128 ~ 127 之间的实例复用;System.out.println(e == f);             // false,不在 Integer 享元范围,不是共享;System.out.println(c == (a + b));       // true,== 运算在遇到算术运算时自动拆箱;System.out.println(c.equals(a + b));    // true,类型与值皆相同System.out.println(g == (a + b));       // true,== 运算在遇到算术运算时自动拆箱;System.out.println(g.equals(a + b));    // false,值相同,但类型不同,equals() 方法不处理数据转型;
}

建议在实际编码中尽量避免自动装箱与拆箱;

3. 条件编译

Java 语言天然的编译方式不需使用预处理器(编译器并非一个个编译 Java 文件,而是通过编译单元构建语法树顶级节点待处理列表,再行编译,各个文件可以相互提供符号信息);

Java 语言的条件编译以使用条件为常量的 if 语句实现;跟进 boolean 值真假,编译器会将不成立的分支代码消除掉(编译器的解语法糖阶段完成);只支持方法体内部的语句基本块(Block)级别的条件编译,不支持整个 Java 类的控制;

public static void main(String[] args) { if (true) {System.out.println("block 1"); } else {System.out.println("block 2"); }
}

编译后的效果


public static void main(String[] args) { // 编译出来的结果只会保留 true 的分支System.out.println("block 1");
}

若其他带有条件判断能力的控制语句与常量搭配使用,可能会被拒绝编译;

public static void main(String[] args) { // 编译器将会提示“Unreachable code” while (false) {System.out.println(""); }
}

上一篇:「JVM 编译优化」javac 编译器源码解读

PS:感谢每一位志同道合者的阅读,欢迎关注、评论、赞!


参考资料:

  • [1]《深入理解 Java 虚拟机》

相关文章:

「JVM 编译优化」Java 语法糖(泛型、自动装箱/拆箱、条件编译)

「JVM 编译优化」Java 语法糖&#xff08;泛型、自动装箱/拆箱、条件编译&#xff09; 语法糖可以看做事前端编译期的一些小把戏&#xff1b;虽不会提供实质性的功能改进&#xff0c;但它们或能提高效率&#xff0c;或能提升语法的严谨性&#xff0c;或能减少编码出错的机会&a…...

Linux下的进程控制

目录 退出码 终止进程 进程等待 进程程序替换 自己实现简易shell命令行 内建命令 退出码 在编写代码时main函数内部我们通常都使用return 0;结尾&#xff0c;以此标识正常退出。这里的return 0就是所谓的退出码&#xff0c;Linux下也是一样&#xff1a; 看这个小程序&…...

QT 文件监视系统QFileSystemWatcher监视目录的改变directoryChanged和监视文件的改变fileChanged

QT 文件监视系统QFileSystemWatcher监视目录的改变相关操作说明mainwindow.hmainwindow.cpp调试结果相关操作说明 添加头文件 Header: #include qmake: QT core bool QFileSystemWatcher::addPath(const QString &path)如果路径存在&#xff0c;则会向文件系统监视器添…...

Typescript基础知识(类型断言、类型别名、字符串字面量类型、枚举、交叉类型)

系列文章目录 引入一&#xff1a;Typescript基础引入&#xff08;基础类型、元组、枚举&#xff09; 引入二&#xff1a;Typescript面向对象引入&#xff08;接口、类、多态、重写、抽象类、访问修饰符&#xff09; 第一章&#xff1a;Typescript基础知识&#xff08;Typescri…...

Windows系统扩充C盘空间系列方法总结

目录前言方法一 使用自带的Windows的DiskPart扩充C盘1. 打开cmd2.三步命令方法二&#xff1a;使用Windows系统内置磁盘管理扩展C盘方法三. 使用专业磁盘分区工具总结前言 本教程是总结Windows系统进行C盘&#xff08;系统盘&#xff09;扩充空间的系列方法&#xff0c;一般来讲…...

华为OD机试 - 跳格子(Python)

跳格子 题目 地上共有N个格子,你需要跳完地上所有的格子, 但是格子间是有强依赖关系的,跳完前一个格子后, 后续的格子才会被开启,格子间的依赖关系由多组steps数组给出, steps[0]表示前一个格子,steps[1]表示steps[0]可以开启的格子: 比如[0,1]表示从跳完第0个格子以后…...

Java配置文件的值注入

1.平常使用直接在变量头上加上Value就可以把配置文件的值注入进来 Value(“${environment.active}”) private String environment; 2.但是变量使用static修饰时&#xff0c;就不能注入进来了 Value(“${environment.active}”) private static String environment; 这是因…...

SAP 订单BOM与销售BOM的区别

订单BOM与销售BOM的区别 訂單BOM: 是實際生產時用的BOM, 在標準BOM和銷售BOM基礎上增減物料的BOM 銷售BOM: 是為特定客戶設定的BOM, 在主檔數據層次上的BOM, 在生產時是帶到訂單BOM中去的. 標準BOM: 是公司為標準生產的BOM, 在主檔數據層次上的BOM, 在生產時是帶到訂單BOM中去的…...

支付宝支付详细流程

1、二维码的生成二维码生成坐标 <!-- zxing生成二维码 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.3.3</version></dependency><dependency><groupId>co…...

TCP 的演化史-fast retransmit/recovery

工作原因要对一个 newreno 实现增加 sack 支持。尝试写了 3 天 C&#xff0c;同时一遍又一遍梳理 sack 标准演进。这些东西我早就了解&#xff0c;但涉及落地写实现&#xff0c;就得不断抠细节&#xff0c;试图写一个完备的实现。 这事有更简单的方法。根本没必要完全实现 RFC…...

CSS基础选择器,你认识多少?

前言在上一文初识CSS中&#xff0c;我们了解到了其格式&#xff1a;选择器{ }在初步尝试使用时&#xff0c;我们笼统的直接输入了p { }以选择p标签来对其操作&#xff0c;而这一章节里&#xff0c;我们再进一步探索有关基础选择器的相关内容&#xff0c;理解选择器的作用。选择…...

ChatGPT入门案例|商务智能对话客服(三)

本篇介绍智能客服的基本功能架构和基本概念&#xff0c;并利用对话流技术构建商务智能应用。 01、商务智能客服功能结构 互联网的发展已经深入到社会的各个方面&#xff0c;智能化发展已经成为社会发展的大趋势。在大数据和互联网时代&#xff0c;企业和组织愈加重视客户沟通…...

Matlab 最小二乘法拟合平面(SVD)

文章目录 一、简介1.1最小二乘法拟合平面1.2 SVD角度二、实现代码三、实现效果参考资料一、简介 1.1最小二乘法拟合平面 之前我们使用过最为经典的方式对平面进行了最小二乘拟合(点云最小二乘法拟合平面),其推导过程如下所示: 仔细观察一下可以发现...

AtCoder Regular Contest 126 D题题解

思路 首先我们看看假设选中 mmm 个数后的答案。 我们首先现将 mmm 个数移动到一起&#xff0c;在将他们重新排序。 我们知道&#xff0c;mmm 个数移在一起时&#xff0c;当位于中间的那个数不动时交换次数最少&#xff0c;于是可以列出式子&#xff08;cic_ici​ 是点 iii 的…...

Android R WiFi热点流程浅析

Android R WiFi热点流程浅析 Android上的WiFi SoftAp功能是用户常用的功能之一&#xff0c;它能让我们分享手机的网络给其他设备使用。 那Android系统是如何实现SoftAp的呢&#xff0c;这里在FWK层面做一个简要的流程分析&#xff0c;供自己记录和大家参考。 以Android R版本为…...

【C++进阶】二、多态详解(总)

目录 一、多态的概念 二、多态的定义及实现 2.1 多态的构成条件 2.2 虚函数 2.3 虚函数的重写 2.4 虚函数重写的两个例外 2.4.1 协变 2.4.2 析构函数的重写 2.5 C11 override 和 final 2.5.1 final 2.5.2 override 2.6 重载、覆盖(重写)、隐藏(重定义)的对比 三、…...

node-sass@4.14.1 包含风险, 如何升级依赖至 dart-sass

文章目录需求我上网都查到了哪些信息在 github 看到了 node-sass 依赖的最新版本的列表&#xff1a;关于方案2的失败不同版本的 nodejs 和 node-sass依赖的**适配关系**从何得知替代方案——dart-sass如何安装 dart sass&#xff1f;需求 在做一个基于Node、React的前端项目&a…...

DataWhale 大数据处理技术组队学习task2

三、Hadoop分布式文件系统 1. 产生背景 数据量越来越大&#xff0c;一台独立的计算机已经无法存储所有的数据---->将大规模的数据存储到成百上千的计算机中------为了解决数据管理以及维护极其繁琐与低效------>分布式文件系统 分布式文件系统是管理网络中跨多台计算机…...

一文读懂select、poll、epoll的用法

select&#xff0c;poll&#xff0c;epoll都是IO多路复用的机制。I/O多路复用就通过一种机制&#xff0c;可以监视多个描述符&#xff0c;一旦某个描述符就绪&#xff08;一般是读就绪或者写就绪&#xff09;&#xff0c;能够通知程序进行相应的读写操作。但select&#xff0c;…...

《C陷阱与缺陷》----词法“陷阱”

导言&#xff1a; 由于一个程序错误可以从不同层面采用不同方式进行考察&#xff0c;而根据程序错误与考察程序的方式之间的相关性&#xff0c;可以将程序错误进行划分为各种陷阱与缺陷&#xff1a; ①.词法“陷阱” ②.语法“陷阱” ③.语义“陷阱” ④.连接问题 ⑤.库函数问…...

千锋教育+计算机四级网络-计算机网络学习-04

UDP概述 UDP协议 面向无连接的用户数据报协议&#xff0c;在传输数据前不需要先建立连接&#xff1b;目地主机的运输层收到UDP报文后&#xff0c;不需要给出任何确认 UDP特点 相比TCP速度稍快些简单的请求/应答应用程序可以使用UDP对于海量数据传输不应该使用UDP广播和多播应用…...

蓝桥杯算法训练合集十四 1.P08052.P07053.同余方程4.P08015.ascii应用

目录 1.P0805 2.P0705 3.同余方程 4.P0801 5.ascii应用 1.P0805 问题描述 当两个比较大的整数相乘时&#xff0c;可能会出现数据溢出的情形。为避免溢出&#xff0c;可以采用字符串的方法来实现两个大数之间的乘法。具体来说&#xff0c;首先以字符串的形式输入两个整数&…...

判断字符串中的字符的类型isdecimal();isalpha();isdigit();isalnum()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 判断字符串中的字符的类型 isdecimal()&#xff1b;isalpha()&#xff1b;isdigit()&#xff1b;isalnum() [太阳]选择题 对于代码中isdecimal()和isalnum()输出的结果是? s "ABc123&…...

VSCode远程调试Linux代码,python解释器配置

安装插件并配置 安装后找到插件图标&#xff0c;点击 点击SSH上的 号 在弹出框中输入命令&#xff1a;ssh usernameip -p port username: 远程服务器的用户名 ip&#xff1a; 远程ip port&#xff1a;端口号&#xff0c;没有可以不用 输入完毕后点击enter 选择ssh配置文件保存…...

03:入门篇 - CTK Plugin Framework 基本原理

作者: 一去、二三里 个人微信号: iwaleon 微信公众号: 高效程序员 CTK Plugin Framework 技术是面向 C++ 的动态模型系统。该系统允许插件之间的松散耦合,并且提供了设计良好的方式来进行功能和数据的交互。此外,它没有预先对插件施加限制,这样就可以很容易地将插件的相关…...

面试攻略,Java 基础面试 100 问(九)

数组有没有 length()方法?String 有没有 length()方法&#xff1f; 数组没有 length()方法&#xff0c;有 length 的属性。String 有 length()方法。JavaScript 中&#xff0c;获得字符串的长度是通过 length 属性得到的&#xff0c;这一点容易和 Java混淆。 在 Java 中&…...

JavaScript 代码不嵌套主义

文章目录前言一、何为嵌套代码二、避免嵌套1.提炼抽取2.反转排列总结前言 看过不少过度嵌套的代码, 我真正意识到问题的严重性是刚入职那会, 我在一个老项目里看到了40个连续的else if, 套了6层的if, for和forEach, 因为我们并没有做什么限制代码嵌套的提前约定. 呃, 那之后认…...

使用默认参数的4大要点

概述 默认参数是C中新增的特性。在C中&#xff0c;可以为函数的参数指定默认值。调用函数时&#xff0c;如果没有指定实参&#xff0c;则自动使用默认参数。默认参数的基本语法这里就不作介绍了&#xff0c;下面重点介绍使用默认参数的一些知识要点。 基本规则 1、当函数中某个…...

Linux文件系统中的硬链接及常见面试题

如果能对inode的概念有所了解&#xff0c;对理解本文会有所帮助。如果对inode的概念不太清楚也没有关系&#xff0c;我们会捎带介绍一下。在文件系统的实现层面&#xff0c;我们可以认为包含两个组件&#xff1a;一个是包含数据块的池子&#xff0c;池子中的数据块是等大小的&a…...

opencv-StereoBM算法

原理解释目前立体匹配算法是计算机视觉中的一个难点和热点&#xff0c;算法很多&#xff0c;但是一般的步骤是&#xff1a;A、匹配代价计算匹配代价计算是整个立体匹配算法的基础&#xff0c;实际是对不同视差下进行灰度相似性测量。常见的方法有灰度差的平方SD&#xff08;squ…...

婚庆网站模板免费下载/电子商务网店运营推广

Python中多态的作用 让具有不同功能的函数可以使用相同的函数名&#xff0c;这样就可以用一个函数名调用不同内容(功能)的函数。 Python中多态的特点 1、只关心对象的实例方法是否同名&#xff0c;不关心对象所属的类型&#xff1b; 2、对象所属的类之间&#xff0c;继承关系…...

徐州品牌网站建设/今日新闻国内大事件

word条形码 换行Tables can be difficult to read. Adding shaded bands to a table improves readability and really just makes it look better. Here’s how you can add striping to your Excel tables. 表格可能很难阅读。 在表中添加阴影带可提高可读性&#xff0c;实际…...

网站的维护怎么做/百度怎么发布自己的广告

我安装了32位的office 然后今天突发奇想 安装了一个64位的 visio &#xff0c;之后看到有人在网上发文章 如何解决viso2013无法安装64位版本的Office https://jingyan.baidu.com/article/a65957f4db6ae124e67f9b9b.html 我就按照上面的操作进行了一番神操作 我在没有备份的情…...

上传网站到百度/2022重大时政热点事件简短

PicGo下载 官网地址&#xff1a;https://molunerfinn.com/PicGo/ 软件截图 阿里云OSS 进入阿里云官网&#xff0c;登录后&#xff0c;点击对象存储OSS进入阿里云OSS控制台&#xff0c;如果没有开通&#xff0c;就先开通一下点击创建Bucket输入参数&#xff0c;读写权限选择公…...

中小企业网站用什么技术/网站怎么做出来的

在一些现代的扁平化设计网站&#xff0c;特别是移动端网站&#xff0c;经常会包含许多简单而清晰的小图标&#xff0c;例如网站图标、用户的默认头像、移动端网页首页底部固定的切换栏等&#xff0c;这些小图标一般都是由美工做好&#xff0c;可能会放到精灵图上&#xff0c;前…...

怎么在一起做网站上拿货/竞价托管是啥意思

我一直坚持在Linux下钻研计算机软件的相关知识&#xff0c;三个半月前学完了《数据库系统概论》这本书。在学习中我使用了MySQL作为练兵场&#xff0c;但当我学到书的后半部分的时候&#xff0c;发现MySQL有一些问题&#xff1a; 1、MySQL没有角色权限控制功能&#xff0c;也就…...