1. 概述

在技术选型时,我们常面临两难选择:是升级到新版Java,还是继续使用现有版本?这本质上是在权衡新功能带来的收益与迁移成本之间的平衡。

本文将介绍Java 8到Java 17之间那些极其实用的新特性。这些特性不仅学习成本低,而且能快速落地,显著降低迁移难度。

2. String类增强

String类在后续版本中获得了多项重要改进。

2.1. 字符串压缩机制

Java 9引入了字符串压缩特性,通过优化内存表示提升性能。

核心变化:String对象内部由char[]改为byte[]存储。因为Java内部使用UTF-16编码,每个char占用2字节。而实际场景中,多数字符串(如英文)仅需1字节(LATIN-1编码)即可表示。

Java会根据字符串内容自动选择存储方式:

  • 纯LATIN-1字符 → byte[]
  • 包含特殊字符 → char[]

开发者无需关心底层实现,完全透明。对比内部字段变化:

// Java 8及之前
char[] value;

// Java 9+
byte[] value;

2.2. 文本块

Java 15之前,处理多行文本需要手动拼接换行符和字符串。文本块(Text Blocks)的引入彻底解决了这个痛点,特别适合嵌入HTML/JSON/SQL等代码片段。

文本块是String字面量的替代形式,使用三引号定义:

// 使用文本块
String value = """
            Multi-line
            Text
            """;

传统方式则繁琐易错:

// 使用传统字符串字面量
String value = "Multi-line"
                + "\n" // 换行符
                "Text"
                + "\n";
String str = "Multi-line\nText\n";

2.3. 新增String方法

过去处理字符串常依赖Apache Commons等第三方库。Java 11/12新增了多个实用方法,减少外部依赖:

isBlank() - 判断空白字符串
repeat() - 重复字符串
indent() - 添加缩进
lines() - 按行分割
strip() - 去除首尾空白
transform() - 链式转换

实际用法:

assertThat("  ".isBlank());
assertThat("Twinkle ".repeat(2)).isEqualTo("Twinkle Twinkle ");
assertThat("Format Line".indent(4)).isEqualTo("    Format Line\n");
assertThat("Line 1 \n Line2".lines()).asList().size().isEqualTo(2);
assertThat(" Text with white spaces   ".strip()).isEqualTo("Text with white spaces");
assertThat("Car, Bus, Train".transform(s1 -> Arrays.asList(s1.split(","))).get(0)).isEqualTo("Car");

3. 记录类(Records)

DTO(数据传输对象)需要大量模板代码:字段、构造器、getter/setter、equals()hashCode()toString()等。

public class StudentDTO {
    
    private int rollNo;
    private String name;
    
    // 构造器
    // getter/setter
    // equals(), hashCode(), toString()
}

记录类(Records)提供了一种极简的不可变数据类定义方式,效果类似Lombok。Java 14预览,Java 16转正:

public record Student(int rollNo, String name) {
}

编译器自动生成:

  • 全参构造器
  • private final字段
  • 访问方法(非getter)
  • equals()/hashCode()/toString()

使用示例:

Student student = new Student(10, "Priya");
Student student2 = new Student(10, "Priya");
        
assertThat(student.rollNo()).isEqualTo(10);
assertThat(student.name()).isEqualTo("Priya");
assertThat(student.equals(student2));
assertThat(student.hashCode()).isEqualTo(student2.hashCode());

4. 更友好的NullPointerException

NPE是Java开发者最常遇到的异常。传统错误信息难以定位具体空对象,尤其在链式调用中:

student.getAddress().getCity().toLowerCase();

当NPE发生时,很难快速判断是studentgetAddress()还是getCity()为null。

Java 14引入了增强型NPE提示,需添加JVM参数:

-XX:+ShowCodeDetailsInExceptionMessages

启用后错误信息更精确:

Cannot invoke "String.toLowerCase()" because the return value of "com.baeldung.java8to17.Address.getCity()" is null

⚠️ Java 15起该参数默认开启

5. 模式匹配

模式匹配简化了"条件判断+类型转换+变量绑定"的常见操作,使代码更简洁安全。

5.1. 增强的instanceof操作符

传统写法需要三步:类型检查 → 强制转换 → 变量赋值:

if (obj instanceof Address) {
    Address address = (Address) obj; 
    city = address.getCity();
}

Java 16的模式匹配直接在判断中完成类型转换和变量绑定

if (obj instanceof Address address) {
    city = address.getCity();
}

5.2. Switch表达式

Switch表达式(Java 14)可返回值,支持任意类型。Java 17进一步支持模式匹配(预览特性):

double circumference = switch(shape) {
    case Rectangle r -> 2 * r.length() + 2 * r.width();
    case Circle c -> 2 * c.radius() * Math.PI;
    default -> throw new IllegalArgumentException("Unknown shape");
};

关键改进:

  • 使用case L ->替代case L:
  • 无需break防止穿透
  • 支持任意类型选择器
  • 传统case L:需用yield返回值

6. 密封类(Sealed Classes)

某些业务场景需要限制继承范围(如领域驱动设计)。密封类通过明确许可的子类列表,实现可控继承

定义密封类:

public sealed class Shape permits Circle, Square, Triangle {
}

子类必须使用以下修饰符之一:

  • final - 不可再继承
  • sealed - 可继续密封
  • non-sealed - 开放继承

示例:

public final class Circle extends Shape {
    public float radius;
}

public non-sealed class Square extends Shape {
   public double side;
}   

public sealed class Triangle extends Shape permits ColoredTriangle {
    public double height, base;
}

7. 总结

从Java 8到Java 17,Java通过渐进式迭代引入了大量新特性。本文精选的特性学习成本低、见效快,特别是:

  • String类增强
  • 记录类
  • 模式匹配
  • 密封类

这些改进能显著提升开发效率、代码可读性和系统性能,是Java 8迁移到Java 17的绝佳切入点。

本文所有代码示例可在GitHub仓库获取。


原始标题:Migrate From Java 8 to Java 17