简单整理一下JDK9JDK17在开发中可能常用到的一些新功能

2023年 7月 14日 30.3k 0

简单梳理一下从 JDK9JDK17 这些版本在 CRUD 中可能常用到的一些新功能;

毕竟现在 Sping6、SpringBoot3 都已经要求是 JDK17

虽然现在用的还是 JDK8... 但是新版本增加了哪些新功能还是要了解一下的

平常都是大致浏览一下看看哪个版本有啥新功能,现在印象中只记得新增了记录类、密封类,还有 var ,但其实还有很多其它有用的功能,比如 JDK14 中的空指针异常,详情更加的清晰明了

如果你用的是 JDK17 ,那么下面列举的新功能都能使用

建议使用 JDK17,因为 JDK17 是一个长期支持的版本

本文只是简单梳理记录并简单演示一下怎么使用

JDK9

1. 模块化

2. 使用 of 创建不可变集合

List list = List.of(1, 2, 3, 4, 5);
Map map = Map.of("key1", 1, "key2", 2, "key3", 3);

3. stream api 新增了4个流操作方法

  • dropWhile

    从开头开始删除满足条件的元素,直到遇到第一个不满足条件的,然后返回剩余的元素

    List numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    List result = numbers.stream()
                            .dropWhile(n -> n < 5)
                            .collect(Collectors.toList());
    
    System.out.println(result); // 输出:[5, 6, 7, 8, 9, 10]
    
    
  • takeWhile

    从开头开始获取满足条件的元素,直到遇到第一个不满足条件的,然后返回前面满足条件的元素

    List numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    List result = numbers.stream()
                            .takeWhile(n -> n < 5)
                            .collect(Collectors.toList());
    
    System.out.println(result); // 输出:[1, 2, 3, 4]
    
  • ofNullable,可接受 null

    String name = "Hello World";
    Stream stream = Stream.ofNullable(name);
    // 输出:Hello World
    stream.forEach(System.out::println);
    
    name = null;
    stream = Stream.ofNullable(name);
    // 无输出
    stream.forEach(System.out::println);
    
    
  • iterate

    传入一个种子值和一个生成函数,根据生成函数生成一个无限流。

    文字有点太抽象,直接看代码吧

    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  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)

    私有方法作用范围限制在类或接口内部,外部类无法访问和重写

    有什么用呢?比如可以将一些通用功能封装到私有方法中,每个实现类不用重复的去重写该方法。

    下面是一个简单示例

        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 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. 局部变量类型推断(语法糖)

     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. streamcollectors 可指定为不可变的集合

    // 以前的语法
    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 一个命令就可以了。

    [root@localhost ~]# java Hello.java
    

    2. 最常用的 string

  • 空判断

     String str = "";
     boolean blank = str.isBlank();
    
  • 复制

     String str = "Hello World;";
     // 重复次数2
     String repeat = str.repeat(2);
     // 输出 Hello World;Hello World;
     System.out.println(repeat);
    
  • 将字符串拆分成行

     String text = "Hello\nWorld";
     // 使用 lines() 方法将字符串拆分成行,并返回一个流
     Stream lines = text.lines();
     // 此时 lineList 中是两个对象:Hello、World
     List lineList = lines.collect(Collectors.toList());
    
  • 去除字符串前后空格

     String str = "   Hello World   ;  ";
     // 输出 Hello World   ;
     System.out.println(str.strip());
    
  • 3. 读写文件 File API

     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 表达式中也能用了

    BinaryOperator 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

    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. 优化空指针异常打印信息

    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表达式 ,正式版本(语法糖)

    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 中正式发布

    java -XX:+UseZGC ApplicationName
    

    2. 文本块,正式版本

    终于不用各种转义拼接了

    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,正式版本

    Object object = "Hello";
    if (object instanceof String s) {
        System.out.println(s);
    }
    

    类型匹配对了后,直接就转换为指定类型了,省了一步

    2. 记录类 record ,正式版本

    详细使用可以看我之前写的文章:JDK16新特性中的record类是什么以及如何使用

    3. 密封类 sealed,预览版本

    详细使用看下面的 JDK17

    JDK17

    1. switch 类型匹配,预览版本

    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 使用起来更加灵活

    public sealed interface Expr permits Add, Multiply {  
        // 使用 permits 指定了只有 Add、Multiply 两个类可以实现该接口
    }
    
    public final class Add implements Expr {  
      
    }
    
    public final class Multiply implements Expr {  
      
    }
    
    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 接口类中指定了只有 AddMultiply 这两个类可以实现 Expr接口,所以在 switch 语句最后那里可以不写 default

    下面演示一下 recordsealedswitch 的类型匹配,三者的配合使用

  • 定义一个接口

    public interface Future {
        AsyncReturn get();
    }
    
  • 使用sealed定义一个密封类接口,里面定义 record 类型的实现类

    public sealed interface AsyncReturn {
    
        record Success(V result) implements AsyncReturn {
        }
    
        record Failure(Throwable cause) implements AsyncReturn {
        }
    
        record Timeout() implements AsyncReturn {
        }
    
        record Interrupted() implements AsyncReturn {
        }
    
    }
    
  • 使用

    public static void main(String[] args) {
        // 创建一个线程池
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future future = () -> {
            // 在这里执行异步操作,并返回相应的AsyncReturn实例
            try {
                // 模拟异步操作,这里使用线程池提交一个任务
                java.util.concurrent.Future 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 asyncResult = future.get();
    
        // 使用 switch 类型匹配进行处理
        switch (asyncResult) {
            case AsyncReturn.Success successResult -> {
                String result = successResult.result();
                System.out.println("异步操作成功: " + result);
            }
            case AsyncReturn.Failure failureResult -> {
                Throwable cause = failureResult.cause();
                System.out.println("异步操作失败: " + cause.getMessage());
            }
            case AsyncReturn.Timeout timeoutResult -> System.out.println("超时");
            case AsyncReturn.Interrupted interruptedResult -> System.out.println("中断");
        }
    
        // 关闭线程池
        executor.shutdown();
    
    }
    
  • 大致就这些吧。

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论