查看原文
其他

从Java 6到Java 21的重要变动

麦客奥德彪 郭霖
2024-07-22


/   今日科技快讯   /

特斯拉多年来一直在研发人形机器人 Optimus,并每月分享该项目的最新进展。埃隆・马斯克近期宣布,特斯拉不仅计划销售 Optimus 并将其用于自有工厂,还将提供该机器人的租赁服务。

马斯克表示,特斯拉计划将提供 Optimus 机器人的租赁和销售两种选择,其中租赁服务会早于销售率先推出。根据马斯克四月份的说法,特斯拉计划明年开始销售 Optimus。

/   作者简介   /

本篇文章转载自麦客奥德彪的博客,文章主要分享了java 6-21 重要的变动,相信会对大家有所帮助!

原文地址:
https://juejin.cn/post/7379431208429584393

/   正文   /

kotlin 成为了Android的首选语言,但是很遗憾,有的项目不能更换语言开发或者混合开发,但是在Android 开发中很容易忽略JDK的版本特性,因为它不像Android的版本特性一样总是被使用。JDK 这些年都增加了哪些新的功能和语法呢?

Java 6(2006年12月)

  1. 改进的 JIT 编译器:增强了 Java 程序的性能。
  2. 脚本语言支持:集成了对脚本语言的支持,特别是 JavaScript(通过 Rhino 引擎)。
  3. Java 编译 API:提供了一组 API 用于编译 Java 源文件。
  4. 对 Web 服务的增强:包括对 JAX-WS 2.0, JAXB 2.0 的支持。
  5. 改进的 Swing:引入了桌面外观(SystemTray API)、桌面集成(Desktop API)和对 Windows Vista 的更好支持。
  6. Pluggable Annotation Processing API:提供了一个新的 API 用于注解处理。
  7. 增强的监控和管理:增加了对 JMX 和 JVM 的增强监控和管理能力。
  8. 垃圾收集改进:包括并发标记扫描垃圾收集器(CMS)的改进。

Java 7(2011年7月)

  1. 语言增强(Project Coin):
  • Switch 支持字符串:switch 语句可以使用字符串。
  • 多捕获块:可以在一个 catch 块中捕获多个异常。
  • Try-with-resources 语句:自动管理资源关闭。
  • 钻石操作符:推断泛型类型以简化代码。
  • 二进制字面量:使用前缀 0b 或 0B 表示二进制字面量。
  • 下划线分隔符:数字字面量中可以使用下划线分隔符以提高可读性,例如 1_000_000。
  • Fork/Join 框架:用于并行执行任务的新框架,旨在充分利用多处理器系统。
  • NIO.2 文件 I/O:新的文件 I/O API,提供更丰富的文件系统操作和改进的异常处理。
  • 异步 I/O:提供了异步文件通道和异步套接字通道。
  • 增强的并发实用工具:包括 Phaser、TransferQueue 和 ConcurrentLinkedDeque。
  • 动态语言支持(InvokeDynamic):改进 JVM 以更好地支持动态语言(如 JRuby 和 Groovy)。
  • 安全增强:提高了密码学和安全包的性能和灵活性。
  • Java 核心类库的改进:增强了基本库,如 java.util.Objects,提供了一些实用方法。

  • 7以前基本没什么需要注意的,除了NIO这块。

    Java 8(2014年3月)

    • Lambda 表达式:支持函数式编程。
    • 函数式接口:使用 @FunctionalInterface 注解。
    • Stream API:处理集合的序列化操作。
    • 默认方法:接口可以包含默认实现的方法。
    • 静态方法:接口可以包含静态方法。
    • Optional 类:避免空指针异常的容器类。
    • 新的日期和时间 API:包括 LocalDate,LocalTime,LocalDateTime,ZonedDateTime 等。
    • Nashorn JavaScript 引擎:更快的 JavaScript 引擎替代 Rhino。
    • 重复注解:支持在同一位置重复使用注解。

    8就不一样了,lambda、Stream API、Optional 开发中使用频率还是比较高的

    Java 9(2017年9月)

    • 模块系统:提供更好的封装性和依赖管理。
    • JShell:交互式 Java REPL 工具。
    • 改进的 Javadoc:包括搜索功能和 HTML5 支持。
    • 多版本 JAR:支持在一个 JAR 文件中包含多个版本的类文件。
    • 增强的 Stream API:添加了 takeWhile,dropWhile,iterate 方法。
    • 工厂方法:用于创建不可变集合的快捷方法,如 List.of(),Set.of(),Map.of()。

    Java 10(2018年3月)

    • 局部变量类型推断:使用 var 关键字。
    • 应用数据类共享(AppCDS):改进的类加载性能。
    • 并行 Full GC for G1:G1 垃圾收集器的 Full GC 并行化。
    • Root Certificates:默认包含根证书,简化 SSL 连接。

    局部变量的类型推断, 这里需要和kotlin 做个对比

    Java 10 的局部变量类型推断和 Kotlin 的类型推断在功能和实现上有许多相似之处,但也有一些显著的区别。以下是两者的详细对比。

    Java 10 的局部变量类型推断

    使用 var 关键字

    • 只能在局部变量声明中使用,如方法内的局部变量、增强的 for 循环变量、try-with-resources 语句中的变量。
    • 语法示例:

    var list = new ArrayList<String>();
    for (var item : list) {
        System.out.println(item);
    }

    类型推断的范围

    • 只能在初始化时推断类型,必须在声明时赋值。
    • 不能用于类成员变量、方法参数或返回类型。

    限制

    • 无法改变变量的类型,推断类型是编译时确定的,不能使用 var 关键字来声明未初始化的变量。
    • var 不能用于 lambda 表达式和方法引用的参数。

    目标

    • 主要目的是简化代码的编写,减少样板代码,提高代码的可读性,同时保持 Java 的静态类型系统。

    Kotlin 的类型推断

    使用 val 和 var 关键字

    • val 表示不可变变量,var 表示可变变量。Kotlin 可以在更多场景中使用类型推断,包括类成员变量、方法参数和返回类型。
    • 语法示例:

    val list = ArrayList<String>()
    for (item in list) {
        println(item)
    }

    类型推断的范围

    • 可以在很多地方使用类型推断,包括局部变量、类属性、函数返回类型等。
    • 不要求在声明时初始化,可以在函数参数、返回类型和类属性中使用类型推断。

    灵活性

    • Kotlin 的类型推断更为广泛和灵活,可以根据上下文推断类型。
    • 支持更复杂的类型推断,例如 lambda 表达式和返回类型推断。

    目标

    • 提供更简洁的语法,提高开发效率,减少样板代码,同时保持 Kotlin 的静态类型系统。

    对比总结

    使用场景和灵活性

    • Java 10:类型推断只能用于局部变量声明,且必须在声明时初始化。它不能用于类成员变量、方法参数和返回类型。
    • Kotlin:类型推断的应用范围更广,几乎可以在任何地方使用类型推断,包括类成员变量、方法参数和返回类型。

    关键字

    • Java 10:使用 var 关键字来表示局部变量的类型推断。
    • Kotlin:使用 val 和 var 来分别表示不可变和可变变量,并在更多场景中使用类型推断。

    静态类型系统

    • 两者皆有:Java 和 Kotlin 都保持了静态类型系统,通过类型推断来简化代码,同时保持类型安全。

    限制和特点

    • Java 10:局部变量类型推断主要是为了减少样板代码,限制较多,使用范围较窄。
    • Kotlin:更加灵活和广泛的类型推断,允许在更多上下文中使用,并且支持更复杂的类型推断逻辑。

    示例对比

    Java 10 示例

    public class Main {
        public static void main(String[] args) {
            var list = new ArrayList<String>();
            list.add("Java");
            for (var item : list) {
                System.out.println(item);
            }
        }
    }

    Kotlin 示例

    fun main() {
        val list = ArrayList<String>()
        list.add("Kotlin")
        for (item in list) {
            println(item)
        }
    }

    总结

    Java 10 的局部变量类型推断和 Kotlin 的类型推断都旨在简化代码编写,提高开发效率,但 Kotlin 的类型推断更为广泛和灵活。Java 的局部变量类型推断则更为保守和有限,主要聚焦于局部变量声明,确保不改变 Java 语言的静态类型特性和向后兼容性。

    Java 11(2018年9月)

    • 新的字符串方法:isBlank,lines,strip,stripLeading,stripTrailing,repeat。
    • 运行 Java 文件:使用 java 命令直接运行 .java 文件。
    • 局部变量语法的 lambda 参数:lambda 参数支持 var。
    • HttpClient:新的 HttpClient API 用于 HTTP/2 和 WebSocket 支持。
    • ZGC(Z Garbage Collector):低延迟垃圾收集器。

    Java 11 引入了一些新的字符串方法,包括 isBlank,lines,strip,stripLeading,stripTrailing,repeat。下面是这些方法的具体用法:

    isBlank():判断字符串是否为空白(只包含空格、制表符、换行符等空白字符)。

    String str = "   ";
    System.out.println(str.isBlank());  // 输出 true

    lines():将字符串拆分为行,返回一个流(Stream)。可以拆分\n \r 或者\n\r这种

    String str = "Hello\nWorld\nJava";
    str.lines().forEach(System.out::println);
    // 输出:
    // Hello
    // World
    // Java

    strip():去除字符串两端的空白字符(空格、制表符、换行符等)。

    String str = "  Hello World  ";
    System.out.println(str.strip());  // 输出 "Hello World"

    stripLeading():去除字符串开头的空白字符。

    String str = "  Hello World";
    System.out.println(str.stripLeading());  // 输出 "Hello World"

    stripTrailing():去除字符串末尾的空白字符。

    String str = "Hello World  ";
    System.out.println(str.stripTrailing());  // 输出 "Hello World"

    repeat(int count):将字符串重复指定次数。

    String str = "Java ";
    System.out.println(str.repeat(3));  // 输出 "Java Java Java "

    这些新的字符串方法可以使字符串操作更加方便和灵活,特别是在处理文本数据时,可以更容易地进行格式化、清理和处理。

    Java 12(2019年3月)

    • 增强的 switch 语句(预览): switch 语句可以返回值,并简化语法。
    • JVM 常量 API:新的 API 用于描述常量池中的常量。
    • G1 垃圾收集器改进:改进的暂停时间。

    Java 12 引入了增强的 switch 语句,允许 switch 语句返回一个值,并简化了语法。这个功能提供了一种更简洁、更灵活的方式来处理多路分支逻辑。下面是它的具体用法:

    public class Main {
        public static void main(String[] args) {
            int day = 3;
            String dayString = switch (day) {
                case 1 -> "Monday";
                case 2 -> "Tuesday";
                case 3 -> "Wednesday";
                case 4 -> "Thursday";
                case 5 -> "Friday";
                default -> throw new IllegalArgumentException("Invalid day of the week: " + day);
            };

            System.out.println("Today is " + dayString);
        }
    }

    在这个例子中,switch 语句根据 day 的值执行不同的分支,并将结果赋给 dayString 变量。这里的新语法包括:

    • 使用 -> 替代了 case 关键字后的冒号。
    • 语句块中的最后一个表达式会作为整个 switch 语句的结果返回。
    • 可以使用 yield 关键字来显式返回值,但这是可选的,如果没有 yield,则会隐式地返回语句块的最后一个表达式的值。

    这种增强的 switch 语句简化了多路分支逻辑的编写,使代码更加清晰易读。它还允许 switch 语句作为表达式使用,从而在更多情况下简化代码。

    Java 13(2019年9月)

    • 文本块(预览):多行字符串文字。
    • 动态 CDS 档案:改进的类数据共享。
    • ZGC 改进:支持解除未使用的内存。

    Java 14(2020年3月)

    • Switch 表达式:switch 语句的新语法已正式发布。
    • 记录类型(预览):简化数据载体类的定义。
    • 模式匹配(预览):instanceof 操作符的模式匹配。

    instanceof 操作符的模式匹配。这个功能使得对对象进行类型检查和类型转换变得更加简洁和易读。下面是一个示例:

    public class Main {
        public static void main(String[] args) {
            Object obj = "Hello, world!";

            if (obj instanceof String str) {
                System.out.println("obj 是一个字符串: " + str);
                // 在这里可以直接使用 str 变量,它已经被声明为 String 类型
                int length = str.length();
                System.out.println("字符串长度为:" + length);
            } else {
                System.out.println("obj 不是一个字符串");
            }
        }
    }

    在这个示例中,我们使用 instanceof 操作符进行类型检查,并且将匹配的结果赋值给了一个新的变量 str。如果 obj 是一个 String 类型的实例,那么 str 变量就会被声明为 String 类型,并且可以直接在 if 语句的代码块中使用。这样,我们就避免了在 if 语句中进行类型转换的繁琐操作,使代码更加简洁和易读。

    重点是判断类型后不用强转,之前我们使用时是需要强转的。

    Java 15(2020年9月)

    • 文本块:多行字符串文字成为正式功能。
    • 隐藏类:用于框架的动态类生成。
    • ZGC 改进:ZGC 的多项性能改进。
    • Sealed 类(预览): 控制哪个类可以继承某个类。

    Sealed 类,这是一种控制继承关系的机制,可以限制哪些类可以继承某个类。通过将一个类声明为 sealed,可以明确指定允许继承它的类,并对其他类进行限制。这个功能有助于提高代码的安全性和可维护性,减少意外的继承和扩展,从而降低代码的复杂度。下面是一个示例:

    public sealed class Shape permits Circle, Rectangle, Triangle {
        // Shape 类的定义
    }

    public final class Circle extends Shape {
        // Circle 类的定义
    }

    public final class Rectangle extends Shape {
        // Rectangle 类的定义
    }

    public final class Triangle extends Shape {
        // Triangle 类的定义
    }

    在这个示例中,我们首先声明了一个 sealed 类 Shape,并通过 permits 关键字明确指定了允许继承它的子类。在这个例子中,Shape 类允许被继承的子类有 Circle、Rectangle 和 Triangle。其他类不被允许继承 Shape,如果有其他类尝试继承 Shape,编译器会报错。

    通过使用 Sealed 类,可以更加精确地控制继承关系,防止不合理的类继承和扩展,从而提高代码的安全性和可维护性。需要注意的是,Sealed 类是一个预览功能,可能会在未来的版本中发生变化,因此在实际项目中使用时需要谨慎考虑,并且可能需要等到它成为稳定特性后才能广泛应用。

    Java 16(2021年3月)

    • 记录类型:记录类型成为正式功能。
    • 强封闭 JDK:更严格的封装 JDK 内部 API。
    • 模式匹配(instanceof):instanceof 模式匹配成为正式功能。

    Java 17(2021年9月,长期支持版本)

    • Sealed 类:控制哪个类可以继承某个类成为正式功能。
    • 增强的 switch 语句:switch 表达式成为正式功能。
    • 新 JEP 集合:包括改进的伪随机数生成器、增强的 Foreign Function & Memory API、上下文自适应 G1 和 ZGC 等。

    Java 18(2022年3月)

    • UTF-8 默认字符集:默认字符集改为 UTF-8。
    • 简单的 Web 服务器:方便测试和开发的小型 Web 服务器。
    • 代码段的 Javadoc:在 Javadoc 中嵌入代码段。

    Java 19(2022年9月)

    • 虚拟线程(预览):更轻量级的线程实现,改进并发性能。
    • 结构化并发(预览):简化并发任务管理。
    • 外部函数和内存 API(预览):更好的本地代码调用支持。
    • 模式匹配:增强的模式匹配功能。

    Java 20(2023年3月)

    • 记录模式(预览):用于解构记录的模式匹配。
    • 模式匹配 for switch(第三个预览):继续改进和完善 switch 语句的模式匹配。
    • 外部函数和内存 API(第二个预览):继续改进和完善。

    一种用于解构记录的模式匹配方式。记录模式使得在模式匹配中可以更方便地访问记录中的字段,并且可以根据记录的结构进行匹配和解构。下面是一个简单的示例,展示了如何使用记录模式:

    public record Point(int x, int y) {}

    public class Main {
        public static void main(String[] args) {
            Point point = new Point(10, 20);

            if (point instanceof Point p) {
                System.out.println("x = " + p.x() + ", y = " + p.y());
            }
        }
    }

    在这个示例中,我们定义了一个名为 Point 的记录(record),包含两个字段 x 和 y。然后,我们创建了一个 Point 对象,并使用模式匹配检查它是否是一个 Point 类型的实例,并将其解构为变量 p。如果匹配成功,则打印出 x 和 y 字段的值。

    记录模式使得在 Java 中更方便地处理记录类型的数据,可以通过模式匹配的方式轻松地解构记录,并访问其中的字段。

    Java 21(2023年9月,长期支持版本)

    • 虚拟线程:作为正式功能发布,更轻量级的线程实现。
    • 结构化并发:作为正式功能发布,简化并发任务管理。
    • 记录模式:作为正式功能发布,用于解构记录的模式匹配。
    • 模式匹配 for switch:作为正式功能发布,增强 switch 语句的模式匹配。
    • 外部函数和内存 API:作为正式功能发布,更好的本地代码调用支持。
    • 字符串插值:通过直接在字符串中插入变量和表达式,简化字符串操作。

    让我们逐个解释并提供代码示例来说明每个功能。

    虚拟线程

    虚拟线程是 Java 21 中的一项正式功能,它是一种更轻量级的线程实现,旨在提高并发性能和资源利用率。虚拟线程是通过 Executors.newVirtualThreadExecutor() 方法创建的,可以与传统的线程一起使用,但是更轻量级。

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;

    public class Main {
        public static void main(String[] args) {
            ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
            for (int i = 0; i < 10; i++) {
                executor.submit(() -> {
                    System.out.println("Hello from virtual thread: " + Thread.currentThread());
                });
            }
            executor.shutdown();
        }
    }

    这是一个虚拟线程线程池的演示,虚拟线程有得搞,可以单独学习一下,然后其他的还行1.8中的lambda、Stream API、Optional也是常搞的API。

    结构化并发

    结构化并发是 Java 21 中的一项正式功能,它旨在简化并发任务的管理。通过结构化并发,可以更轻松地管理并发任务的执行和结果处理,提高代码的可读性和可维护性。

    import java.util.concurrent.CompletableFuture;

    public class Main {
        public static void main(String[] args) {
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                System.out.println("Running in parallel");
            });

            future.thenRun(() -> {
                System.out.println("After completion");
            }).join();
        }
    }

    记录模式

    记录模式是 Java 21 中的一项正式功能,它用于解构记录的模式匹配。记录模式使得在模式匹配中可以更方便地访问记录中的字段,并根据记录的结构进行匹配和解构。

    public record Point(int x, int y) {}

    public class Main {
        public static void main(String[] args) {
            Point point = new Point(10, 20);

            if (point instanceof Point p) {
                System.out.println("x = " + p.x() + ", y = " + p.y());
            }
        }
    }

    模式匹配 for switch

    模式匹配 for switch 是 Java 21 中的一项正式功能,它增强了 switch 语句的模式匹配能力,使得在 switch 语句中可以更方便地进行模式匹配操作。

    public class Main {
        public static void main(String[] args) {
            Object obj = "Hello";

            switch (obj) {
                case String s -> System.out.println("String: " + s);
                case Integer i -> System.out.println("Integer: " + i);
                default -> System.out.println("Unknown type");
            }
        }
    }

    外部函数和内存 API

    外部函数和内存 API 是 Java 21 中的一项正式功能,它提供了更好的本地代码调用支持,使得在 Java 中更容易地调用本地代码并处理本地内存。

    import jdk.incubator.foreign.*;
    import static jdk.incubator.foreign.CLinker.*;

    public class Main {
        public static void main(String[] args) throws Exception {
            MemorySegment segment = CLinker.toCString("Hello, world!");

            try (var scope = ResourceScope.newConfinedScope()) {
                var printfFn = CLinker.getInstance().lookup("printf", 
                    FunctionDescriptor.ofVoid(CLinker.C_POINTER), 
                    FunctionDescriptor.ofVoid(CLinker.C_POINTER)
                );
                printfFn.invokeExact(scope, "%s%n", segment);
            }
        }
    }

    字符串插值

    字符串插值是 Java 21 中的一项正式功能,它通过直接在字符串中插入变量和表达式,简化了字符串操作,提高了代码的可读性和可维护性。

    public class Main {
        public static void main(String[] args) {
            String name = "Alice";
            int age = 30;
            String message = String.format("Hello, my name is %s and I am %d years old.", name, age);
            System.out.println(message);
        }
    }

    推荐阅读:
    我的新书,《第一行代码 第3版》已出版!
    原创:写给初学者的Jetpack Compose教程,用derivedStateOf提升性能
    ViewModel为什么可以保存数据?

    欢迎关注我的公众号
    学习技术或投稿


    长按上图,识别图中二维码即可关注
    继续滑动看下一个
    向上滑动看下一个

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存