前言 最近入职了新公司,发现新东家采用的技术栈相当新潮,这让长期“坚守”Java 8 的我颇感意外。正是这份新鲜感,这才催生了这篇博客。 本文无意深入原理,仅对新特性做简要梳理,方便日后查阅与回顾。
新特性 Var局部类型推断 var是Java 10版本引入的新特性,它的核心能力是让编译器根据初始表达式来推断左边的数据类型,而非传统的由程序员手动指定。var有助于减少样板代码,在使用得当的情况下也有助于提高代码可读性,同时它也有它的局限性,比如只能用于局部变量、必须初始化变量、不能用于Lambda表达式等等,下面觉几个简单的例子。
1 2 3 4 5 6 7 8 9 10 static void main () { var a = 520 ; String b = "hello" ; System.out.println(b + " " + a); var c = new HashMap <>(); c.put("name" , "张三" ); for (var entry : c.entrySet()){ System.out.println(entry.getKey() + ":" + entry.getValue()); } }
文本块 文本块在JDK15正式引入,主要是解决Java传统的字符串在编写涉及多行内容时不便问题,这些操作往往需要包含换行、转义等行为,导致代码可读性特别差。
文本块以 “”” 开始,以 “”” 结束,下面我们比较下传统的写法,和文本块的写法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static void main () { String traditionalStr = "{\n" + " \"name\": \"张三\",\n" + " \"age\": 30,\n" + " \"hobbies\": [\"阅读\", \"游泳\"]\n" + "}" ; String textBlockStr = """ { "name": "张三", "age": 30, "hobbies": ["阅读", "游泳"] } """ ; System.out.println(traditionalStr); System.out.println(textBlockStr); }
Java Record(纪录类) Java中的record类是Java16正式引入特性,被设计为一种承载不可变数据的载体的类。另外,它自动实现了构造器、访问方法、哈希方法、toString()方法等等,使代码开起来会更简洁。
首先看下如何创建Record类
1 2 3 4 5 6 7 8 9 public record UserInfo (Long id, String name, Integer age) { public UserInfo { if (age < 0 ){ throw new IllegalArgumentException ("age can not be less than zero" ); } } }
构造record对象和普通的通过new关键字创建对象很类似的,只是由于对象中的字段都是不可变的,所以record对象是不能手动调用set赋值的。
1 2 3 4 5 6 7 8 9 10 11 static void main () { UserInfo user1 = new UserInfo (1L , "张三" , 18 ); System.out.println(user1); UserInfo user2 = new UserInfo (2L , "张三" , -1 ); System.out.println(user2); } 输出结果为: UserInfo[id=1 , name=张三, age=18 ] Exception in thread "main" java.lang.IllegalArgumentException: age can not be less than zero at new_feature.UserInfo.<init>(UserInfo.java:8 )
模式匹配 JDK16引入的一个增强instanceof特性的能力。
1 2 3 4 5 6 7 8 9 10 11 12 void patternMatch () { Object object = "张三" ; if (object instanceof String){ String name = (String) object; System.out.println(name); } if (object instanceof String name){ System.out.println( name); } }
switch模式匹配 在一次次的更新中,switch语法也在不断的优化,在jdk8中我们还需要为每个分支设置break,否则就可能执行多个分支的逻辑,自jdk14后已经不存在这个问题了,并且和模式匹配配合起来能发挥不可思议的作用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 static void patternMatchSwitch (Object object) { System.out.println(switch (object) { case Integer i -> "数值类型" ; case String s -> "字符串" ; case UserInfo userInfo when userInfo.id().equals(1L ) -> "用户信息: " + userInfo; default -> "object = " + object; }); } static void main () { UserInfo user1 = new UserInfo (1L , "张三" , 18 ); UserInfo user2 = new UserInfo (2L , "张三" , 1 ); patternMatchSwitch(user1); patternMatchSwitch(user2); patternMatchSwitch(1 ); patternMatchSwitch("张三" ); } 结果: 用户信息: UserInfo[id=1 , name=张三, age=18 ] object = UserInfo[id=2 , name=张三, age=1 ] 数值类型 字符串
Sealed Classes密封类 密封类的目的是提供一个比使用final修饰更灵活,比使用开放继承更安全的继承控制。
在之前的使用方式中,类的继承是“全有或全无”的,全有意味着类可以被任何类继承;全无则是使用final修饰,任何类不允许继承当前类。而密封类则提供了一种特性,允许我们指定特定的类来继承或者实现它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 public sealed interface Shape permits Circle, Rectangle, Square { double area () ; } final class Circle implements Shape { private final double radius; public Circle (double radius) { this .radius = radius; } @Override public double area () { return Math.PI * radius * radius; } } final class Rectangle implements Shape { private final double width, height; public Rectangle (double width, double height) { this .width = width; this .height = height; } @Override public double area () { return width * height; } } sealed class Square implements Shape permits UnitSquare, CustomSquare { private final double side; public Square (double side) { this .side = side; } @Override public double area () { return side * side; } } final class UnitSquare extends Square { public UnitSquare () { super (1.0 ); } } non-sealed class CustomSquare extends Square { public CustomSquare (double side) { super (side); } }
虚拟线程 虚拟线程是Java 21推出的轻量级线程,由JVM管理。它让数百万并发任务像普通对象一样创建,无需复杂异步编程。特别适合高并发I/O场景,用简单阻塞代码即可实现极高吞吐量,是Java并发的革命性进化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 static void virtualThreadTest () { Thread.ofVirtual().start(() -> { System.out.println("Virtual Thread-1" ); }); Thread.startVirtualThread(() -> { System.out.println("Virtual Thread-2" ); }); ThreadFactory threadFactory = Thread.ofVirtual().factory(); try (var executor = Executors.newThreadPerTaskExecutor(threadFactory)){ for (int i = 0 ; i < 10 ; i++) { int finalI = i; executor.submit(() -> { System.out.println("VirtualThreadFactory-" + finalI); }); } } }