写点什么

JDK8 到 JDK17 都升级了那些新特性?又有哪些能常用好用的?

  • 2025-02-12
    福建
  • 本文字数:9248 字

    阅读完需:约 30 分钟

一、JDK 8 回顾


​ Java Development Kit (JDK) 8 于 2014 年发布,标志着 Java 进入了一个新的时代。这次更新不仅引入了许多重要的语言特性,还极大地提升了开发者的生产力和代码的可维护性。下面我们来详细探讨一下其中几个关键改进:


  • Lambda 表达式和 Stream API


​ Lambda 表达式的引入是 JDK 8 中最为显著的变化之一,它允许开发者以更加简洁的方式编写匿名内部类,从而使得代码更加紧凑且易于理解。Lambda 表达式配合 Stream API 使用,可以轻松实现对集合(如 List, Set 等)的数据处理操作,比如过滤(filter)、映射(map)和归约(reduce),大大简化了数据处理逻辑的编码工作。


示例代码片段:


List<String> names = Arrays.asList("Alice", "Bob", "Charlie");names.stream()    .filter(name -> name.startsWith("A"))    .forEach(System.out::println);
复制代码


double average = wdList.stream().mapToDouble(Float::floatValue).average().orElse(0.00);
复制代码


    public static String fieldListToFolderName(List<String> fields) {        return fields.stream()                .map(field -> "COALESCE(" + field + ", 'null')")                .collect(Collectors.joining(" , '-' , "));    }
复制代码


  • 新的时间日期 API (java.time)


​ JDK 8 之前,Java 的时间日期处理一直被批评为复杂且容易出错。为了解决这些问题,JDK 8 引入了一套全新的时间日期 API——java.time包。这套 API 设计得更为直观合理,提供了丰富的类库支持,包括表示日期(LocalDate)、时间(LocalTime)、日期时间(ZonedDateTime)等,以及用于处理不同时区的 ZoneId 和 ZoneOffset 类。此外,它还增强了日期和时间的解析与格式化功能,并有效解决了旧版日期时间 API 中存在的线程安全问题。


示例代码片段:


// 获取本地文件的修改时间ZonedDateTime localModifiedTime = ZonedDateTime.ofInstant(Files.getLastModifiedTime(localFilePath)                        	.toInstant(),ZoneId.systemDefault());
复制代码


  • 默认方法的接口改进


​ 在 JDK 8 以前,Java 中的接口只能包含抽象方法,这限制了接口的发展和演进。为了向后兼容并增加灵活性,JDK 8 允许在接口中定义默认方法(default method)。这意味着接口现在可以直接提供方法的具体实现,而无需强制其实现类重写这些方法。这一变化使得接口能够随着 Java 版本的升级而进化,同时保持与现有实现类的兼容性,极大地方便了框架和库的设计者进行功能扩展。


二、JDK 9 至 JDK 17 的新特性概览


​ 从 JDK 9 到 JDK 17,Java 经历了一系列激动人心的更新和改进。这些版本不仅增强了语言本身的功能,还对开发者体验进行了优化,下面将分别介绍每个版本的核心更新摘要,并重点讨论长期支持(LTS)版本。


1. JDK 9

  • 模块化系统(Jigsaw 项目): 引入了 Java 平台模块系统,允许定义清晰的边界来封装代码,提高安全性与维护性。

  • JShell 工具: 提供了一个交互式的环境用于快速测试 Java 代码片段。

  • 其他改进: 支持私有接口方法、增强的 Stream API 等。


2. JDK 10

  • 局部变量类型推断: 使用var关键字简化变量声明时的类型标注,提升编码效率。

  • 应用类数据共享(CDS): 提高了 JVM 启动速度并减少了内存占用。


3. JDK 11 (LTS)

  • 长期支持: 是继 JDK 8 之后的第一个 LTS 版本,为需要稳定性和长期支持的企业提供服务。

  • HTTP 客户端 API: 标准化了异步非阻塞 HTTP 请求处理,取代了过时的 HttpURLConnection。

  • 移除特性: 去除了 Java EE 和 CORBA 模块,使核心平台更加精简。


4. JDK 12 - JDK 16 这些版本主要集中在语言特性的改进、性能优化以及开发人员生产力的提升上。例如,引入了 switch 表达式(后在 JDK 14 中成为标准),增强了垃圾回收机制,提供了更多诊断和监控工具等。


5. JDK 17 (LTS)

  • 最新 LTS 版本: JDK 17 标志着一个新的稳定点,适用于寻求长期稳定性解决方案的企业用户。

  • 密封类(Sealed Classes): 允许更精确地控制哪些类可以扩展或实现某个类或接口,增强了安全性和可维护性。

  • 模式匹配(for instanceof): 进一步简化了类型检查后的类型转换操作。

  • 弃用和删除: 移除了某些不再推荐使用的 API,确保平台的清洁和发展方向。


三、详细探讨关键特性


1. 模块化系统(JDK 9)

  • 平台模块系统简介: JDK 9 引入了 Java 平台模块系统(JPMS),也称为 Project Jigsaw。它允许将代码组织成独立的模块,每个模块都可以明确声明其依赖关系和暴露给其他模块使用的 API。这一变化旨在提高安全性、可维护性,并支持更灵活的配置。

  • 如何利用模块化来组织代码: 开发者可以通过module-info.java文件定义模块名、所需依赖以及该模块公开的包。这样不仅有助于清晰地界定不同组件间的边界,还能有效减少不必要的耦合。


2. 局部变量类型推断(JDK 10)

  • 使用var关键字简化变量声明: 在声明局部变量时,可以使用var代替具体的类型名,编译器会根据赋值表达式自动推断出正确的类型。这使得代码更加简洁,同时不影响静态类型的检查。


// 在JDK 10之前,你需要明确指定类型List<String> list = new ArrayList<>();
// 使用var后,可以省略具体的类型名称var list = new ArrayList<String>();
// 对于基本数据类型也同样适用var number = 10; // intvar message = "Hello, World!"; // String
// 当从方法中返回一个值时var result = someMethod(); // 编译器会根据someMethod()的返回类型推断result的类型
// 注意:lambda表达式不能与var一起使用,因为编译器无法推断出合适的函数式接口
复制代码


3. HTTP 客户端 API(JDK 11)

  • HTTP/2 的支持与异步编程模型: 新的 HttpClient API 提供了对 HTTP/2 协议的全面支持,并且原生支持异步请求处理。开发者可以轻松地发送 GET、POST 等请求,并通过 CompletableFuture 对象管理响应结果。


import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;import java.util.concurrent.CompletableFuture;
//发送异步GET请求public class AsyncExample { public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("http://example.com")) .build();
CompletableFuture<HttpResponse<String>> future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
// 可以在这里执行其他操作,不需要等待请求完成
future.thenApply(HttpResponse::body) .thenAccept(System.out::println); // 打印响应体
// 确保主线程等待异步操作完成 future.join(); }}
复制代码


4. Switch 表达式(JDK 12+)

  • 改进后的 switch 语法及其使用案例: Switch 表达式的更新使得它可以返回值,并且 case 标签现在可以使用箭头(->)直接指定结果。这种改变不仅让代码更易读,还减少了传统 switch 语句中常见的错误,如忘记 break 语句。


    @Test    void switchCase() {        int dayOfWeek = 5;        var result = switch (dayOfWeek) {            case 1, 2, 3, 4, 5 -> {                System.out.println("工作日");                yield "工作日";            }            case 6, 7 -> {                System.out.println("休息日");                yield "休息日";            }            default -> {                System.out.println("无效的日期");                yield "无效的日期";            }        };    }
复制代码


5. 文本块(JDK 13+)

  • 多行字符串字面量的引入: 文本块允许编写跨越多行的字符串而不需手动添加换行符或转义字符。这对于构建 HTML、SQL 查询或其他格式化文本非常有用。


         //传统字符串	    String html = "<html>\n"                    + "    <body>\n"                    + "        <p>Hello, world</p>\n"                    + "    </body>\n"                    + "</html>";		//使用文本块        String html_text = """                            <html>                                <body>                                    <p>Hello, world</p>                                </body>                            </html>                            """;		//SQL查询        String query = """                       SELECT id, name, email                       FROM users                       WHERE status = 'ACTIVE'                       ORDER BY last_login DESC                       """;		//JSON示例        String json = """                      {                          "name": "John Doe",                          "age": 30,                          "email": "john.doe@example.com"                      }                      """;
复制代码


6. 记录(Records)与模式匹配(JDK 14+)

  • 数据载体类的简化形式: Records 提供了一种简洁的方式来定义纯数据持有类,自动生成 equals、hashCode、toString 方法及构造函数。

  • instanceof 模式匹配的增强: 增强的 instanceof 操作符允许在进行类型检查后直接进行类型转换,使代码逻辑更加流畅自然。


    @Test    void RecordAndInstanceof(Object obj){        if (obj instanceof Person person){            System.out.println("Name: " + person.name());        } else {            System.out.println("Unknown object type");        }    }
public record Person(String name, int age){ public Person { if (name == null || name.isBlank()) { throw new IllegalArgumentException("Name cannot be blank"); } if (age < 0) { throw new IllegalArgumentException("Age cannot be negative"); } } }
复制代码


7. 密封类(Sealed Classes)(JDK 15+)

  • 类层次结构的控制与安全性提升: 密封类允许作者精确控制哪些类可以继承或实现它们。这为创建安全、可靠的 API 提供了新的可能性,同时也增强了应用的安全性和可维护性。

  • 定义密封类

    要定义一个密封类,需要使用sealed关键字修饰类,并通过permits关键字明确列出哪些子类被允许扩展这个密封类:

public sealed class Shape permits Circle, Rectangle, Square {}
复制代码


在这个例子中,Shape类是一个密封类,它仅允许CircleRectangleSquare这三个类继承它。


子类的定义


对于密封类的直接子类,你需要指定它们如何与密封父类协作。子类可以通过以下三种修饰符之一来声明:

  • final:表示该子类不能被进一步继承,这是默认行为。

  • sealed:表示该子类也是密封的,且需要指定允许继承它的子类。

  • non-sealed:表示该子类不受密封类的限制,任何类都可以继承它。

例如:


public final class Circle extends Shape {    // ...}
public sealed class Rectangle extends Shape permits FilledRectangle, EmptyRectangle {}
public non-sealed class Square extends Shape { // ...}
复制代码


这里,Circle类是final的,意味着它不能有子类;Rectangle类也是密封的,但指定了两个可能的子类FilledRectangleEmptyRectangle;而Square类是非密封的,允许任意其他类继承它。


模式匹配与密封类


密封类与模式匹配结合使用时特别强大。由于编译器知道所有可能的子类型,因此可以在 switch 表达式中自动推断出完整的 case 集,无需手动添加 default 分支来处理未列举的情况。


public void printShapeInfo(Shape shape) {    System.out.println(shape + " is a " + switch (shape) {        case Circle c -> "circle"        case Rectangle r -> "rectangle"        case Square s -> "square"    });}
复制代码


  • 在这个例子中,因为Shape的所有子类型都是已知的,所以编译器可以确保 switch 表达式覆盖了所有的可能性,这不仅提高了代码的安全性,也使得逻辑更加清晰。

    总的来说,密封类为 Java 带来了更强的封装能力和更细粒度的访问控制,使得开发人员可以构建出既安全又灵活的应用程序架构


8. 其他语言和库的改进


这包括但不限于垃圾回收器的优化、新增的工具支持等。例如,ZGC 和 Shenandoah 收集器的引入显著降低了 GC 暂停时间;新版本还带来了增强的诊断工具,帮助开发者更好地理解和调优应用程序性能。


垃圾回收器的优化


  • ZGC(Z Garbage Collector):自 JDK 11 引入作为实验性功能,并在后续版本中逐步完善,ZGC 是一个可扩展的低延迟垃圾收集器。它的设计目标是能够在大堆内存(多 TB 级别)上保持非常低的暂停时间(通常不超过 10 毫秒),同时不影响应用程序的吞吐量。

    Shenandoah:与 ZGC 类似,Shenandoah 也是旨在减少 GC 暂停时间的收集器,但它采用了不同的方法来实现并发的内存回收。Shenandoah 可以在任意大小的堆上工作,并试图通过缩短 GC 暂停时间来提高应用响应性。它从 JDK 12 开始作为实验性功能加入,并在之后的版本中得到了进一步的优化和发展。


这两种垃圾收集器都极大地改善了处理大数据集时的性能和响应时间,特别适用于那些对延迟敏感的应用程序。


新增的工具支持


随着 Java 的发展,一系列新的诊断工具和支持库被添加进来,帮助开发者更好地理解和调优他们的应用程序性能。例如:


  • JFR(Java Flight Recorder):一个用于收集有关正在运行的 Java 应用程序的事件数据的工具。它可以帮助识别性能瓶颈、资源使用情况等。自 JDK 11 起,JFR 成为 OpenJDK 的一部分,不再局限于 Oracle JDK。

    JMC(Java Mission Control):与 JFR 紧密集成,提供了一个图形界面用于监控和管理 Java 应用程序。它允许用户实时查看 JFR 收集的数据,分析性能问题。


这些工具和改进使得开发者能够更深入地了解其应用程序的行为,从而做出更有根据的决策来优化性能和资源利用效率。


四、迁移指南


  • 从 JDK 8 迁移到 JDK 17 的最佳实践


​ 迁移至更新的 Java 版本,如从 JDK 8 到 JDK 17,虽然可能带来一系列挑战,但同样也提供了许多机会来利用新特性提高代码质量、性能和安全性。以下是详细的迁移指南,帮助您顺利完成这一过程:


评估当前环境兼容性检查: 在开始迁移之前,首先需要对现有应用程序进行一次全面的审查,以确定它是否与目标 JDK 版本兼容。特别注意那些依赖于特定 JDK 8 行为或 API 的代码。

第三方库支持: 确认所有使用的第三方库都已更新并支持 JDK 17。对于不兼容的库,寻找替代品或等待官方更新。


测试计划制定单元测试: 确保有充分的单元测试覆盖,这将帮助识别迁移过程中引入的问题。

集成测试: 针对整个系统执行集成测试,确保不同组件之间的交互正常工作。

回归测试: 进行广泛的回归测试,验证应用在新环境下运行无误。


逐步迁移策略模块化迁移: 如果您的项目规模较大,考虑采用渐进式的方法,首先将部分模块迁移到新的 JDK 版本,并逐步扩展到整个项目。

使用工具辅助: 利用现代 IDE 提供的自动化重构工具,以及像 jdeps 这样的分析工具,帮助发现潜在问题。


探索新功能语言特性和 API 改进: 学习并应用 JDK 9 以来的新特性,比如模块化系统、局部变量类型推断(var)、增强的 switch 表达式等,这些都可以显著提升代码质量和开发效率。

性能优化: JDK 17 包含了多项垃圾收集器优化(例如 ZGC, Shenandoah),了解如何调整这些设置以获得最佳性能表现。


准备回滚方案备份与恢复计划: 准备好数据备份和快速恢复机制,以防万一出现不可预见的问题时能够迅速回滚到稳定状态。


可能遇到的问题及解决方案


在从 JDK 8 迁移到 JDK 17 的过程中,可能会遇到一系列技术挑战和兼容性问题。以下是一些常见的障碍及相应的解决方案:


模块化系统(Java Platform Module System, JPMS)问题: JDK 9 引入了 JPMS,这可能导致依赖于类路径而非模块路径的应用程序出现问题。


解决方案: 对应用程序进行模块化设计,或使用--add-modules--add-exports等命令行选项来调整模块访问权限。


弃用 API 的移除问题: 随着时间推移,一些在早期版本中被标记为废弃的 API 可能在较新的版本中被彻底移除。


解决方案: 使用工具如 jdeps 分析代码库,识别并替换所有使用已移除 API 的地方。


第三方库兼容性问题: 某些第三方库可能尚未更新以支持最新的 JDK 版本。


解决方案: 更新至最新版本的第三方库,或寻找替代方案。必要时可联系维护者了解未来支持计划。


字节码版本不匹配问题: 编译器生成的字节码版本必须与运行时环境相匹配,否则会导致UnsupportedClassVersionError错误。


解决方案: 确保编译器和 IDE 设置指向正确的 JDK 版本,并且构建脚本正确配置了源和目标兼容性级别。


性能变化问题: 虽然新版本通常带来性能提升,但在某些情况下,特定应用的行为可能会受到负面影响。


解决方案: 进行详尽的性能测试,比较不同版本之间的表现,调整 JVM 参数以优化性能。


安全策略变更问题: 新版本加强了安全性措施,例如对反射访问进行了更严格的控制。


解决方案: 根据需要调整代码以符合新的安全要求,利用官方文档指导如何适当地使用这些特性。


部署环境限制问题: 生产环境中可能存在限制,使得直接升级到最新版 JDK 变得困难。


解决方案: 制定详细的迁移计划,包括逐步过渡策略、回滚方案以及与相关团队沟通协调。


通过提前规划并准备好应对这些潜在问题的策略,可以大大增加迁移过程的成功率,并确保最终产品能够充分利用 JDK 17 的新功能和改进。


五、未来展望


  • Java 生态系统的发展趋势


​ 随着技术的不断进步,Java 生态系统正朝着几个关键方向发展。首先,性能优化始终是 Java 发展的核心关注点之一。新的 JVM 改进和垃圾回收算法持续减少延迟并提高吞吐量,为开发者提供了更强大的工具来构建高效的应用程序。其次,云计算和微服务架构的普及推动了 Java 平台在这些领域的适应性增强,包括更好的模块化支持(如 JPMS)和容器友好型特性。此外,随着物联网(IoT)设备的增长,Java ME (Micro Edition)也正在经历变革,以更好地服务于资源受限环境下的应用开发。最后,安全性依然是 Java 不可忽视的一个方面,预计未来版本将进一步加强数据保护机制,并提供更加精细的安全控制选项。


​ 随着 Spring AI 框架的发布,Java 生态系统在人工智能领域的应用潜力得到了显著增强。Spring AI 为开发者提供了一种新的方式来集成和部署机器学习模型,使其更容易地将 AI 功能整合到现有的 Java 应用程序中。这不仅简化了开发流程,还降低了技术门槛,使得更多的开发者能够涉足 AI 领域,而无需依赖于 Python 等其他语言。

​ 然而,尽管 Java 通过 Spring AI 在 AI 领域取得了进展,认为它可以完全替代与其他语言(如 Python)的交互可能还为时过早。Python 因其简洁的语法以及强大的科学计算库(如 NumPy, Pandas, TensorFlow 等),仍然是数据科学家和研究人员的首选语言。对于某些特定任务或项目,跨语言协作仍可能是最优解。

​ 在未来,我们可能会看到更多专门为 Java 设计的智能库和支持工具的涌现,这将进一步巩固 Java 在新兴技术领域的地位。同时,为了最大化利用各种语言的优势,构建混合语言环境下的解决方案也将成为一个趋势。因此,与其说是一种替代关系,不如说是不同技术栈之间相互补充、共同发展的局面。Java 开发者应保持开放的心态,积极探索如何将 Spring AI 与其他先进技术相结合,以应对日益复杂的业务需求和技术挑战。


  • 对于开发者来说,如何保持技术的前沿性


​ 为了在这个快速变化的技术环境中保持竞争力,开发者需要采取一系列措施来确保自己处于技术前沿。首先,紧跟官方发布的信息至关重要。Oracle 和其他主要贡献者定期发布关于 Java 新特性的详细信息和技术文档,通过订阅这些资源可以及时获取最新动态。其次,参与社区活动也是不可或缺的一部分。无论是线上论坛还是线下会议,都是交流思想、分享经验的好地方。此外,尝试新技术和框架,例如 Spring Boot、Micronaut 等,可以帮助开发者更快地适应市场变化。学习现代软件开发实践,比如 DevOps、持续集成/持续部署(CI/CD),也能极大地提升个人技能树。最后,不要忽视跨学科知识的重要性,了解一些与编程紧密相关的领域,如数据库管理、网络安全或用户体验设计,可以使你在团队中扮演更为全面的角色。通过上述方法,开发者不仅能够紧跟 Java 生态系统的步伐,还能拓宽自己的职业道路。


​ 随着 AI 技术的不断进步和普及,每个人都拥有前所未有的机会来提升自我,实现个人与职业发展的突破。AI 不再仅仅局限于科技巨头或高级研究人员的领域,它正在逐渐渗透到我们日常生活的各个方面,并成为各行各业提升效率、创新服务的重要工具。

​ 不要忘记将 AI 作为一种工具来提升自己的日常生活和工作效率。无论是使用智能日程管理软件优化时间安排,还是借助语音助手快速查找信息,合理利用 AI 助手都能极大地提升你的生产力和个人效能。


结论


​ Java 作为世界上最受欢迎的编程语言之一,其发展和更新从未停止。从 JDK 8 到 JDK 17,Java 经历了许多重要的变化和发展,不仅增强了语言本身的性能和功能,还大大提高了开发者的生产力。下面是对这一时期主要变化的总结,并鼓励开发者积极探索和采用这些新特性。


JDK 8 到 JDK 17 的主要变化

  • Lambda 表达式与 Stream API(JDK 8):JDK 8 引入了 Lambda 表达式,使得代码更加简洁、易读。同时,Stream API 为集合操作提供了更强大的支持,简化了数据处理流程。

  • 新的日期时间 API(JDK 8):修复了旧版日期时间库中的设计缺陷,提供了线程安全、不可变且易于使用的日期时间处理类。

  • 接口中的默认方法(JDK 8):允许在接口中定义带有实现的方法,这为接口的演进提供了更大的灵活性,而不会破坏现有的实现。

  • 模块系统(JDK 9):引入了 Java 平台模块系统(JPMS),通过定义清晰的依赖关系和边界来改善封装性,减少运行时环境的大小。

  • 局部变量类型推断(JDK 10):使用var关键字简化了局部变量类型的声明,让代码更加简洁。

  • 增强的垃圾回收机制(多个版本):包括 G1 垃圾收集器的改进和其他新型垃圾收集器的引入,如 ZGC 和 Shenandoah,它们都旨在降低停顿时间和提高应用性能。

  • 文本块(JDK 13, 正式在 JDK 15):提供了一种书写多行字符串的新方式,减少了转义字符的使用,使代码更加直观易懂。

  • 记录(JDK 14, 预览, JDK 16 正式):用于定义一种简化的类,主要用于存储不可变的数据,简化了数据载体对象的编写。

  • 模式匹配(JDK 14 开始, 预览, JDK 17 进一步增强):通过instanceof运算符进行模式匹配,简化了对象类型检查和转换的过程。


鼓励开发者积极探索和采用新特性


​ 随着每一个版本的发布,Java 都在不断地进化,为开发者带来了更多的可能性。然而,要充分利用这些新特性,开发者需要积极主动地学习和实验。尝试将这些新功能应用到实际项目中,不仅可以提升个人技能,还可以显著提高项目的质量和效率。此外,积极参与社区讨论和技术分享会,可以更快地了解最新的技术趋势和最佳实践。拥抱变化,勇于创新,是每个 Java 开发者在这个快速发展的领域中保持竞争力的关键。让我们一起探索 Java 的新世界,开启更加精彩的编程旅程。


文章转载自:Comfortable

原文链接:https://www.cnblogs.com/ComfortableM/p/18710154

体验地址:http://www.jnpfsoft.com/?from=001YH

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
JDK8到JDK17都升级了那些新特性?又有哪些能常用好用的?_Python_不在线第一只蜗牛_InfoQ写作社区