版本概览
Java 15 作为2020年9月发布的版本,引入了14个重要的增强特性(JEPs),其中包含了多个备受关注的预览特性和正式特性。相比Java 14,此版本在开发效率、代码简洁性和性能方面都有显著提升。
核心亮点一览
- 密封类(预览特性)- 更严格的继承控制
- 文本块(正式特性)- 多行字符串的终极解决方案
- 记录类(第二次预览)- 数据载体类的最佳实践
- 模式匹配 instanceof(第二次预览)- 类型检查和转换的简化
- ZGC 和 Shenandoah GC - 生产就绪的低延迟垃圾收集器
详细特性解析
1. 密封类(Sealed Classes)- JEP 360
特性背景
从Java 15开始预览,JEP 360引入了密封类的概念,这是对Java继承机制的重要补充。
解决问题
传统的Java类要么完全开放继承(public class),要么完全禁止继承(final class),缺少中间状态。密封类提供了精确控制哪些类可以继承的能力。
技术原理
通过sealed关键字和permits子句,限制只有指定的类才能继承该密封类,提供了比访问修饰符更细粒度的控制。
代码示例
// 新写法 - 使用密封类
public sealed class Shape
permits Circle, Rectangle, Triangle {
protected final String name;
protected Shape(String name) {
this.name = name;
}
public abstract double area();
}
// 只有这三个类可以继承Shape
public final class Circle extends Shape {
private final double radius;
public Circle(double radius) {
super("Circle");
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
public final class Rectangle extends Shape {
private final double width, height;
public Rectangle(double width, double height) {
super("Rectangle");
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
}
public non-sealed class Triangle extends Shape {
// non-sealed允许进一步继承
private final double base, height;
public Triangle(double base, double height) {
super("Triangle");
this.base = base;
this.height = height;
}
@Override
public double area() {
return 0.5 * base * height;
}
}
// 使用示例
public class ShapeProcessor {
public static void processShape(Shape shape) {
// 编译器知道所有可能的子类型
switch (shape) {
case Circle c -> System.out.println("处理圆形,面积:" + c.area());
case Rectangle r -> System.out.println("处理矩形,面积:" + r.area());
case Triangle t -> System.out.println("处理三角形,面积:" + t.area());
}
}
}
实际价值
- 更安全的API设计:防止不当继承导致的问题
- 更好的模式匹配:编译器可以进行穷尽性检查
- 清晰的设计意图:明确表达哪些类可以被继承
2. 文本块(Text Blocks)- JEP 378
特性背景
从Java 13开始预览,经过两个版本的完善,在Java 15中正式成为标准特性。
解决问题
传统的多行字符串处理需要大量的转义字符和字符串拼接,代码可读性差,维护困难。
代码示例
public class TextBlockExample {
// 旧写法 - 传统字符串拼接
public static String oldJsonExample() {
return "{n" +
" "name": "张三",n" +
" "age": 30,n" +
" "address": {n" +
" "city": "北京",n" +
" "zipCode": "100000"n" +
" }n" +
"}";
}
// 新写法 - 使用文本块
public static String newJsonExample() {
return """
{
"name": "张三",
"age": 30,
"address": {
"city": "北京",
"zipCode": "100000"
}
}
""";
}
// HTML模板示例
public static String htmlTemplate(String title, String content) {
return """
%s
%s
%s
""".formatted(title, title, content);
}
// SQL查询示例
public static String complexQuery() {
return """
SELECT u.id, u.name, u.email,
p.title, p.content, p.created_date
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
WHERE u.status = 'ACTIVE'
AND p.published = true
ORDER BY p.created_date DESC
LIMIT 10
""";
}
public static void main(String[] args) {
System.out.println("=== JSON示例 ===");
System.out.println(newJsonExample());
System.out.println("n=== HTML示例 ===");
System.out.println(htmlTemplate("我的博客", "欢迎来到我的博客"));
System.out.println("n=== SQL示例 ===");
System.out.println(complexQuery());
}
}
实际价值
- 极大提升可读性:多行文本一目了然
- 减少转义地狱:不需要大量的
n和" - 更好的IDE支持:语法高亮和格式化
- 维护成本降低:修改模板更加方便
3. 记录类(Records)- JEP 384
特性背景
第二次预览特性,继续完善这个用于创建不可变数据载体的语法糖。
代码示例
// 新写法 - 使用Record
public record Person(String name, int age, String email) {
// 自定义构造器验证
public Person {
if (age < 0) {
throw new IllegalArgumentException("年龄不能为负数");
}
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("邮箱格式不正确");
}
}
// 添加自定义方法
public boolean isAdult() {
return age >= 18;
}
// 格式化显示
public String getDisplayName() {
return name + " (" + age + "岁)";
}
}
// 传统写法对比 - 等效的普通类
public final class PersonTraditional {
private final String name;
private final int age;
private final String email;
public PersonTraditional(String name, int age, String email) {
if (age < 0) {
throw new IllegalArgumentException("年龄不能为负数");
}
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("邮箱格式不正确");
}
this.name = name;
this.age = age;
this.email = email;
}
public String name() { return name; }
public int age() { return age; }
public String email() { return email; }
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
PersonTraditional that = (PersonTraditional) obj;
return age == that.age &&
Objects.equals(name, that.name) &&
Objects.equals(email, that.email);
}
@Override
public int hashCode() {
return Objects.hash(name, age, email);
}
@Override
public String toString() {
return "PersonTraditional{name='" + name + "', age=" + age +
", email='" + email + "'}";
}
}
// 使用示例
public class RecordExample {
public static void main(String[] args) {
// 创建Record实例
Person person = new Person("张三", 25, "zhangsan@example.com");
// 自动生成的访问器方法
System.out.println("姓名:" + person.name());
System.out.println("年龄:" + person.age());
System.out.println("邮箱:" + person.email());
// 自动生成的toString()
System.out.println("完整信息:" + person);
// 自定义方法
System.out.println("是否成年:" + person.isAdult());
System.out.println("显示名称:" + person.getDisplayName());
// 创建另一个实例测试equals
Person person2 = new Person("张三", 25, "zhangsan@example.com");
System.out.println("两个对象相等:" + person.equals(person2));
}
}
实际价值
- 代码量减少90%:自动生成构造器、访问器、equals、hashCode、toString
- 不可变性保证:天然线程安全
- 更好的语义表达:明确表达这是一个数据载体
4. 模式匹配的instanceof(Pattern Matching for instanceof)- JEP 375
特性背景
第二次预览特性,简化类型检查和转换的常见模式。
代码示例
public class PatternMatchingExample {
// 旧写法 - 传统instanceof
public static String oldProcessObject(Object obj) {
String result = "未知类型";
if (obj instanceof String) {
String str = (String) obj; // 需要显式转换
result = "字符串长度:" + str.length();
} else if (obj instanceof Integer) {
Integer num = (Integer) obj; // 需要显式转换
result = "整数值:" + num;
} else if (obj instanceof Double) {
Double d = (Double) obj; // 需要显式转换
result = "小数值:" + String.format("%.2f", d);
}
return result;
}
// 新写法 - 使用模式匹配
public static String newProcessObject(Object obj) {
return switch (obj) {
case String str -> "字符串长度:" + str.length();
case Integer num -> "整数值:" + num;
case Double d -> "小数值:" + String.format("%.2f", d);
case null -> "空值";
default -> "未知类型";
};
}
// 复杂的模式匹配示例
public static void processShape(Object shape) {
if (shape instanceof Circle c && c.radius() > 10) {
System.out.println("大圆形,半径:" + c.radius());
} else if (shape instanceof Rectangle r && r.width() * r.height() > 100) {
System.out.println("大矩形,面积:" + (r.width() * r.height()));
} else if (shape instanceof String s && s.startsWith("shape:")) {
System.out.println("形状描述:" + s.substring(6));
}
}
// 实际应用场景 - JSON处理
public static void processJsonValue(Object value) {
switch (value) {
case String str when str.startsWith("http") ->
System.out.println("这是一个URL:" + str);
case String str ->
System.out.println("文本内容:" + str);
case Integer num when num > 0 ->
System.out.println("正整数:" + num);
case Integer num ->
System.out.println("非正整数:" + num);
case Boolean bool ->
System.out.println("布尔值:" + bool);
case null ->
System.out.println("空值");
default ->
System.out.println("其他类型:" + value.getClass().getSimpleName());
}
}
public static void main(String[] args) {
Object[] testData = {
"Hello World",
42,
3.14159,
null,
new java.util.Date()
};
System.out.println("=== 传统方式 ===");
for (Object obj : testData) {
System.out.println(oldProcessObject(obj));
}
System.out.println("n=== 模式匹配方式 ===");
for (Object obj : testData) {
System.out.println(newProcessObject(obj));
}
}
}
// 辅助类
record Circle(double radius) {}
record Rectangle(double width, double height) {}
实际价值
- 消除样板代码:不需要显式的类型转换
- 提高代码安全性:减少ClassCastException的风险
- 增强可读性:代码意图更加清晰
5. ZGC和Shenandoah GC增强
特性背景
ZGC(JEP 377)和Shenandoah GC(JEP 379)从实验性特性升级为生产就绪特性。
解决问题
传统GC在大堆内存场景下会产生明显的停顿时间,影响应用响应性能。
实际价值
- 超低延迟:GC停顿时间控制在10ms以内
- 大堆支持:支持TB级别的堆内存
- 生产就绪:经过充分测试,可用于生产环境
总结
文本块的正式发布解决了多行字符串的历史痛点,密封类和记录类的预览为Java带来了更现代化的类型系统