简单梳理一下从 JDK9
到 JDK17
这些版本在 CRUD 中可能常用到的一些新功能;
毕竟现在 Sping6、SpringBoot3
都已经要求是 JDK17
了
虽然现在用的还是 JDK8
... 但是新版本增加了哪些新功能还是要了解一下的
平常都是大致浏览一下看看哪个版本有啥新功能,现在印象中只记得新增了记录类、密封类,还有 var
,但其实还有很多其它有用的功能,比如 JDK14
中的空指针异常,详情更加的清晰明了
如果你用的是 JDK17
,那么下面列举的新功能都能使用
建议使用 JDK17
,因为 JDK17
是一个长期支持的版本
本文只是简单梳理记录并简单演示一下怎么使用
JDK9
1. 模块化
2. 使用 of
创建不可变集合
ini
复制代码
List<Integer> list = List.of(1, 2, 3, 4, 5); Map<String, Integer> map = Map.of("key1", 1, "key2", 2, "key3", 3);
3. stream api
新增了4个流操作方法
-
dropWhile
从开头开始删除满足条件的元素,直到遇到第一个不满足条件的,然后返回剩余的元素
ini复制代码
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); List<Integer> result = numbers.stream() .dropWhile(n -> n < 5) .collect(Collectors.toList()); System.out.println(result); // 输出:[5, 6, 7, 8, 9, 10]
-
takeWhile
从开头开始获取满足条件的元素,直到遇到第一个不满足条件的,然后返回前面满足条件的元素
ini复制代码
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); List<Integer> result = numbers.stream() .takeWhile(n -> n < 5) .collect(Collectors.toList()); System.out.println(result); // 输出:[1, 2, 3, 4]
-
ofNullable,可接受 null
ini复制代码
String name = "Hello World"; Stream<String> stream = Stream.ofNullable(name); // 输出:Hello World stream.forEach(System.out::println); name = null; stream = Stream.ofNullable(name); // 无输出 stream.forEach(System.out::println);
-
iterate
传入一个种子值和一个生成函数,根据生成函数生成一个无限流。
文字有点太抽象,直接看代码吧
arduino复制代码
public static void main(String[] args) { // 初始值从1开始,每次将前一个数乘以2 Stream.iterate(1, n -> n * 2) .limit(10) .forEach(System.out::println); // 也可以添加判断条件,当n大于10时停止 Stream.iterate(1, n -> n <= 10, n -> n + 1) .forEach(System.out::println); // 生成自定义对象 Stream.iterate(new Person("John Wick", 30), person -> new Person(person.getName(), person.getAge() + 1)) .limit(10) .forEach(System.out::println); // 生成斐波那契数列 Stream.iterate(new int[]{0, 1}, arr -> new int[]{arr[1], arr[0] + arr[1]}) .mapToInt(arr -> arr[0]) .limit(10) .forEach(System.out::println); // 生成无限序列的日期 Stream.iterate(LocalDate.now(), date -> date.plusDays(1)) .limit(10) .forEach(System.out::println); }
自己可以复制代码运行看看......
4. 私有接口方法(Private Interface Methods)
私有方法作用范围限制在类或接口内部,外部类无法访问和重写
有什么用呢?比如可以将一些通用功能封装到私有方法中,每个实现类不用重复的去重写该方法。
下面是一个简单示例
csharp
复制代码
public interface Calculator { int add(int a, int b); int subtract(int a, int b); int multiply(int a, int b); int divide(int a, int b); /** * 默认方法 */ default int calculate(int a, int b, String operation) { // 这里用的是switch新语法,不过不是在JDK9 return switch (operation) { case "add" -> validateAndCompute(a, b, this::add); case "subtract" -> validateAndCompute(a, b, this::subtract); case "multiply" -> validateAndCompute(a, b, this::multiply); case "divide" -> validateAndCompute(a, b, this::divide); default -> throw new IllegalArgumentException("Invalid operation: " + operation); }; } /** * 私有方法 */ private int validateAndCompute(int a, int b, BinaryOperator<Integer> operator) { if (a < 0 || b < 0) { throw new IllegalArgumentException("Numbers must be non-negative."); } return operator.apply(a, b); } } public static void main(String[] args) { CalculatorImpl impl = new CalculatorImpl(); int a = -1; int b = 2; int add = impl.calculate(a, b, "add"); System.out.println(add); }
JDK10
1. 局部变量类型推断(语法糖)
csharp
复制代码
public class VarExample { public static void main(String[] args) { // 推断message为String类型 var message = "Hello, world!"; // 推断number为int类型 var number = 10; System.out.println(message); System.out.println(number); var list = new ArrayList<>(); list.add("Java"); list.add("Python"); list.add("C++"); list.add(1); for (var item : list) { System.out.println(item + ",类型:" + item.getClass()); } // 输出结果 Java,类型:class java.lang.String Python,类型:class java.lang.String C++,类型:class java.lang.String 1,类型:class java.lang.Integer } }
2. stream
中 collectors
可指定为不可变的集合
scss
复制代码
// 以前的语法 list.stream().collect(Collectors.toList()) // 新增加的 list.stream().collect(Collectors.toUnmodifiableList()); list.stream().collect(Collectors.toUnmodifiableSet()); list.stream().collect(Collectors.toUnmodifiableMap());
JDK11
1. 命令行运行java
程序
在命令行下,如果要运行一个 java
类,需要先使用 javac
命令编译类文件,再使用 java
命令运行编译后的文件,这个操作在刚学的时候肯定都做过;
现在在 JDK11
中只需要 java
一个命令就可以了。
csharp
复制代码
[root@localhost ~]# java Hello.java
2. 最常用的 string
-
空判断
ini复制代码
String str = ""; boolean blank = str.isBlank();
-
复制
ini复制代码
String str = "Hello World;"; // 重复次数2 String repeat = str.repeat(2); // 输出 Hello World;Hello World; System.out.println(repeat);
-
将字符串拆分成行
ini复制代码
String text = "Hello\nWorld"; // 使用 lines() 方法将字符串拆分成行,并返回一个流 Stream<String> lines = text.lines(); // 此时 lineList 中是两个对象:Hello、World List<String> lineList = lines.collect(Collectors.toList());
-
去除字符串前后空格
ini复制代码
String str = " Hello World ; "; // 输出 Hello World ; System.out.println(str.strip());
3. 读写文件 File API
ini
复制代码
public static void main(String[] args) { Path filePath = Path.of("C:\Users\Desktop\test.txt"); String content = "Hello World!"; try { // 写文件 writeString Files.writeString(filePath, content); System.out.println("写好了!"); } catch (IOException e) { e.printStackTrace(); } try { // 读文件 readString String text = Files.readString(filePath); System.out.println(text); } catch (IOException e) { e.printStackTrace(); } // bytes 写 String content = "Hello, World!"; try { Files.write(filePath, content.getBytes(StandardCharsets.UTF_8)); System.out.println("写好了!"); } catch (IOException e) { e.printStackTrace(); } // bytes 读 try { byte[] fileBytes = Files.readAllBytes(filePath); String fileContent = new String(fileBytes, StandardCharsets.UTF_8); System.out.println(fileContent); } catch (IOException e) { e.printStackTrace(); } }
4. JDK10
中的 var
局部变量类型推断,在 lambda
表达式中也能用了
ini
复制代码
BinaryOperator<Integer> sum = (var a, var b) -> a + b; int result = sum.apply(10, 20); // 输出 30 System.out.println(result);
JDK12
1. switch
表达式,预览版本
优化 switch
表达式语法
2. 文件内容对比 Files.mismatch
返回它们之间第一个不匹配的字符的位置,如果两个文件完全相同,该方法将返回 -1
。
ini
复制代码
String file1 = "file1.txt"; String file2 = "file2.txt"; long mismatch = Files.mismatch(Path.of(file1), Path.of(file2)); System.out.println(mismatch);
JDK13
1. switch
新增 yield
(预览版本)
2. 文本块,预览版本
JDK14
1. 优化空指针异常打印信息
ini
复制代码
public static void main(String[] args) { String a = "hello"; String b = null; int len = a.length() + b.length(); System.out.println(len); }
现在空指针异常,在异常信息中会直接告诉你是哪个对象导致的,以前还要自己去排查,这个功能还是非常有用的
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "b" is null
2. switch
表达式 ,正式版本(语法糖)
ini
复制代码
int day = 2; String dayName = switch (day) { case 1 -> "星期一"; case 2 -> "星期二"; case 3 -> "星期三"; default -> { // yield 关键字 yield "无效数字"; } }; // 输出 星期三 System.out.println(dayName); int day = 6; String dayType; switch (day) { case 1, 2, 3, 4, 5 -> dayType = "上班"; case 6, 7 -> dayType = "不上班"; default -> dayType = "无效数字"; } // 输出 不上班 System.out.println(dayType);
JDK15
1. ZGC
没用过应该也都听过了吧,JDK15 中正式发布
ruby
复制代码
java -XX:+UseZGC ApplicationName
2. 文本块,正式版本
终于不用各种转义拼接了
ini
复制代码
public static void main(String[] args) { String textBlock = """ Hello, This is a text block. It allows you to write multiple lines of text without the need for escape characters or concatenation operators. """; System.out.println(textBlock); }
JDK16
1. instanceof
,正式版本
typescript
复制代码
Object object = "Hello"; if (object instanceof String s) { System.out.println(s); }
类型匹配对了后,直接就转换为指定类型了,省了一步
2. 记录类 record
,正式版本
详细使用可以看我之前写的文章:JDK16新特性中的record类是什么以及如何使用
3. 密封类 sealed
,预览版本
详细使用看下面的 JDK17
JDK17
1. switch
类型匹配,预览版本
csharp
复制代码
Object value = 42; // 使用 switch 类型匹配进行处理 switch (value) { case Integer i -> System.out.println("Integer value: " + i); case String s -> System.out.println("String value: " + s); case Double d -> System.out.println("Double value: " + d); case Long l && l > 0 -> System.out.println("Positive Long value: " + l); default -> System.out.println("Unknown value"); }
2. 密封类(sealed),正式版本
密封类什么意思呢,就是你想让这个类只能指定的类能继承/实现,其它的类不能继承/实现,对比 final
使用起来更加灵活
kotlin
复制代码
public sealed interface Expr permits Add, Multiply { // 使用 permits 指定了只有 Add、Multiply 两个类可以实现该接口 } public final class Add implements Expr { } public final class Multiply implements Expr { }
csharp
复制代码
public class Test2 { public static void main(String[] args) { Expr expr1 = new Add(); Expr expr2 = new Multiply(); process(expr1); process(expr2); } private static void process(Expr expr) { switch (expr) { case Add add -> System.out.println("add"); case Multiply multiply -> System.out.println("multiply"); // 这里不需要 default } } }
因为在 Expr
接口类中指定了只有 Add
和 Multiply
这两个类可以实现 Expr
接口,所以在 switch
语句最后那里可以不写 default
下面演示一下 record
、 sealed
和 switch
的类型匹配,三者的配合使用
-
定义一个接口
csharp复制代码
public interface Future<V> { AsyncReturn<V> get(); }
-
使用
csharpsealed
定义一个密封类接口,里面定义record
类型的实现类复制代码
public sealed interface AsyncReturn<V> { record Success<V>(V result) implements AsyncReturn<V> { } record Failure<V>(Throwable cause) implements AsyncReturn<V> { } record Timeout<V>() implements AsyncReturn<V> { } record Interrupted<V>() implements AsyncReturn<V> { } }
-
使用
arduino复制代码
public static void main(String[] args) { // 创建一个线程池 ExecutorService executor = Executors.newSingleThreadExecutor(); Future<String> future = () -> { // 在这里执行异步操作,并返回相应的AsyncReturn实例 try { // 模拟异步操作,这里使用线程池提交一个任务 java.util.concurrent.Future<String> asyncResult = executor.submit(() -> { // 模拟耗时操作 Thread.sleep(2000); return "Hello World!"; }); // 获取异步操作结果,阻塞等待结果 String result = asyncResult.get(); // 返回异步操作成功的结果 return new AsyncReturn.Success<>(result); } catch (Exception e) { // 返回异步操作失败的原因 return new AsyncReturn.Failure<>(e); } }; // 调用Future的get()方法获取异步操作的结果 AsyncReturn<String> asyncResult = future.get(); // 使用 switch 类型匹配进行处理 switch (asyncResult) { case AsyncReturn.Success<String> successResult -> { String result = successResult.result(); System.out.println("异步操作成功: " + result); } case AsyncReturn.Failure<String> failureResult -> { Throwable cause = failureResult.cause(); System.out.println("异步操作失败: " + cause.getMessage()); } case AsyncReturn.Timeout<String> timeoutResult -> System.out.println("超时"); case AsyncReturn.Interrupted<String> interruptedResult -> System.out.println("中断"); } // 关闭线程池 executor.shutdown(); }
大致就这些吧。