前言
有段时间没在工作中使用Java语言来开发项目了,冲浪了一下才发现Java21的正式版本都已经发布了。遥想以前用Java11就感觉已经感觉是紧跟技术潮流了😭,下面就来学习每个版本都更新了什么吧~
PS:由于能力受限,主要留意每个版本已经正式发布同时跟开发密切相关的新语法和新特性,虚拟机啥优化的以后慢慢研究
Java11
Java 11 是在 2018 年 9 月发布的长期支持(LTS)版本。主要包含了一些新语法糖、API更新以及性能改进
- JEP 323: 局部变量语法的增强:我们可以在lambda 参数中使用局部变量语法的 var,用来简洁代码
- 引入了一个新的 HTTP 客户端 API:该API支持HTTP2.0协议和
Websocket
协议,相对于之前原生的HttpURLConnection
更简单易用,用起来就和OkHttp
库类似 - String类新增方法:
strip()
,stripLeading()
,stripTrailing()
,repeat()
,isBlank()
,lines()
- Files类新增方法:增加了
readString()
和writeString()
方法,能够以字符串的形式读写文件内容
局部变量语法增强:
Java10引入了弱类型语言常见的var类型,可以用于局部变量的类型推断,我们可以不用显式声明局部变量的类型,编译器可以根据局部变量的初始化表达式来自动推断出类型。例如:
1 | var list = new ArrayList<String>(); // 自动推断为 ArrayList<String> |
而局部变量语法增强则是允许在lambda表达式中也使用var关键字
1 | // 使用var在lambda表达式中声明参数类型 |
HttpClient实现
个人感觉就是像OkHttp库的用法,只不过变成原生库了,用起来还是不错的
1 | public static void main(String[] args) throws IOException, InterruptedException { |
具体实现可以查看官方文档:https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/package-summary.html
String类的新方法
引入了开发中工具类框架常用的一些字符串方法
1 | static void testStringNewFeature(){ |
Files新方法
这个用起来很爽,终于不用写一堆BufferedReader
类的模板代码或者引入hutool
等工具类了
1 | static void testFilesNewFeature() throws IOException { |
Java12、13
感觉Java12、13对于平常开发来说没有什么改变,大部分都是一些预览特性,因此跳过
Java14
Java14是2020年3月发布的新版本,对于开发来说主要是新语法糖和报错信息的优化
- JEP 358: 可帮助性空指针异常(Helpful NullPointerExceptions)
- JEP 361: Switch 表达式
JEP 358: 可帮助性空指针异常
相信大家都有写过链式调用,例如user.address.name.toUpperCase()
,当如果此时这一行报空指针异常,我们并不知道是那个地方为null,JEP358特性就改进了空指针异常的消息,现在可以提供更详细上下文信息,帮助我们准确地定位问题所在。
例如name为空,从会出现如下提示信息,而不是简单的NullPointerExceptions
1 | Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because "user.address.name" is null |
JEP 361: Switch 表达式
当写过Golang
、Kotlin
等语言的switch语法时,再回过头来看Java14前的Switch实在是太繁琐了同时也有很多局限性。
- 只支持基于整数或枚举类型的case
- 每次都要显性去写break语句,如果忘记了就会导致穿透下去
- 不支持返回值,要么每个case里写return或者赋值给一个变量最后在return回去
Java14对Switch做了重大的改进
- switch现在可以作为表达式对变量进行赋值操作
- 同时使用箭头标签可以避免fall through的问题,每个case执行完毕后自动结束switch语句,无需显性写break语句
- 可以使用yield语句直接返回case分支的值
- 同时支持多个case值的合并,无需在写一个空case让他fall through了
1 | public static void main(String[] args) { |
Java15
Java15发布于2020年9月,对于开发来说新特性我认为就是文本块了
- JEP 378: Text Blocks(正式特性)
JEP 378: Text Blocks(正式特性)
例如Python的三个单引号或者双引号,JS的反引号就可以定义多行字符串
1 | // python |
而Java15以前就只能通过字符串拼接的方式或者StringBuilder等方式来实现,不仅返回而且不雅观,而JEP378特性就实现了类似其他语言已有的【文本快】特性。
1 | // Java15 以前 |
Java16
Java16发布于2021年3月,该版本有两个新语法特性,还是很有用滴
- JEP 394: instanceof 模式匹配
- JEP 395: Records
JEP 395: Records
Record类型是一种特殊的类,类似于Enum
类,用来声明一个受限类型的类,只用于存储数据、作为一个简单的数据聚合类。
它就是我们平常开发总会创建的JavaBeans,没有啥业务逻辑,只是用来将数据聚合在一起在各个层级之间传输,record类型做了和Lombok的@Data注解相似的工作。
当我们用record类型声明一个类时,编译器会做如下工作
- 构造器:一个公共的构造器,其参数与
record
组件相匹配。 - 访问器方法(Getters):每个
record
组件都会有一个对应的访问器方法(在Point
的例子中是x()
和y()
),返回组件的值。 equals()
方法:一个覆盖Object
类中的equals()
方法,根据record
组件的值进行比较。hashCode()
方法:一个覆盖Object
类中的hashCode()
方法,根据record
组件的值来计算哈希码。toString()
方法:一个覆盖Object
类中的toString()
方法,按照record
类型的名称和组件的值来生成字符串表示。record
自动继承java.lang.Record
1 | // 源码 |
使用record类型时需要注意以下限制
record
不能拥有其他的实例字段,只能有构造方法里定义的字段record
组件是final
的,因此不能被赋予新值。record
继承自java.lang.Record
,因此不能显式继承其他类。record
可以实现接口。record
类型是隐式final
的,不能被继承。
JEP 394: instanceof 模式匹配
在 Java 16 之前,当我们使用 instanceof
检查一个对象的类型并将其转换为特定类型时,我们需要执行显式的类型转换。就算我们instanceof判断通过,总是还要多写一个步骤,如下:
1 | if (obj instanceof String) { |
JEP394新特性允许我们在instanceof语法总新增一个变量,当判断通过时,会自动被初始化为被检查的类型。
1 | if (obj instanceof String s) { |
有两个地方需要注意
- 新增的变量是
final
类型的,所以新增后就不能对他进行赋值操作 - 变量作用域限定在
if
语句或者逻辑与表达式中。所以只能在instanceof
检查成功后的代码块中使用它。
Java17
Java17发布于2021年的9月,这是一个LTS即长期维护的版本,主要的新特性便是sealed密封类
- JEP 409: Sealed Classes密封类用于实现对类层次结构的继承限制,可以加强编译时类型检查和提高系统安全性
JEP 409: Sealed Classes密封类
这个新语法特性,对于各种框架来说,可以加强类的继承限制,同时能够更明确表达这个类的继承意图
1 | // 定义一个密封的基类 Shape 只能被Circle, Rectangle, Square类继承 |
需要注意密封类的规则:
- 所有允许的子类必须与密封类位于同一个模块或同一个包中。
- 子类必须选择性地被声明为 final,sealed,non-sealed以表明它们在密封层次结构中的角色。
- final:表明不能被继承
- sealed:即为密封类
- non-sealed:表明该类为普通类,可以正常被继承
- 密封类本身不能是
abstract
的,除非它至少有一个非abstract
的子类。
Java18、19、20
同样感觉都是预览特性,因此跳过
Java21
Java21是目前最新的版本,发布于2023年9月,其中最吸引人的当然是类似于Golang
协程的JEP 444虚拟线程了。
- JEP 444 Virtual Threads:Java21的虚拟线程与之前的 API 互相兼容,相比原先的Thread线程来说虚拟线程占用内存非常少,是一种轻量级线程,不应该被池化处理,同时与原先的API兼容。
JEP444 虚拟线程
虚拟线程的原理这里就不多介绍,本质上跟Golang
的协程一样,都是解决高并发场景下线程管理的问题,不过一组虚拟线程还是基于一个内核线程,所以不能跨核调用。
先来看下怎么使用吧
1 | // Thread.startVirtualThread方法接收Runnable接口参数,并立即启动虚拟线程 |
虚拟线程有以下特点:
- 由JVM管理的轻量级线程。
- 不需要任何显式分配或调度。
- 适合I/O密集型任务
- 也可以用来实现异步操作。