Java 编程
Java 9
接口私有方法
支持在接口内声明私有默认方法。
匿名内部类类型推断
List<Integer> numbers = new ArrayList<>() {
// ..
}
try-with-resources 新语法
BufferedReader br1 = new BufferedReader(...);
BufferedReader br2 = new BufferedReader(...);
try (br1; br2) {
System.out.println(br1.readLine() + br2.readLine());
}
弃用下划线标识符
int _ = 10; // Compile error
警告提升
私有方法支持 @SafeVarargs。
引入废弃类型时不再警告。
Java 11
本地变量类型推断
省略类型声明并不表示动态类型,而是更加智能的类型推断:
var greetingMessage = "Hello!";
var date = LocalDate.parse("2019-08-13");
var dayOfWeek = date.getDayOfWeek();
var dayOfMonth = date.getDayOfMonth();
Map<String, String> myMap = new HashMap<String, String>(); // Pre Java 7
Map<String, String> myMap = new HashMap<>(); // Using Diamond operator
Java 14
Switch 表达式
新的 switch 语句:
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
default -> {
String s = day.toString();
int result = s.length();
yield result;
}
};
可以只用作为表达式使用:
int k = 3;
System.out.println(
switch (k) {
case 1 -> "one";
case 2 -> "two";
default -> "many";
}
);
每个 case 都拥有自己的域:
String s = switch (k) {
case 1 -> {
String temp = "one";
yield temp;
}
case 2 -> {
String temp = "two";
yield temp;
}
default -> "many";
}
switch 的 case 必须详尽,这意味着 String、原始类型及其包装类型的 default 必须提供:
int k = 3;
String s = switch (k) {
case 1 -> "one";
case 2 -> "two";
default -> "many";
}
对于枚举来说,要么匹配所有子类,要么提供 default case:
enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
Day day = Day.TUESDAY;
switch (day) {
case MONDAY -> ":(";
case TUESDAY, WEDNESDAY, THURSDAY -> ":|";
case FRIDAY -> ":)";
case SATURDAY, SUNDAY -> ":D";
}
Java 15
文本块
String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
System.out.println(html);
每个换行尾部都会自动追加 \n
标识,如果在尾部显式添加一个 \
字符,则表示不换行,但又增加了可读性:
String singleLine = """
Hello \
World
""";
额外缩进会被自动移除:
String indentedByToSpaces = """
First line
Second line
""";
String indentedByToSpaces = """
First line
Second line
""";
目前还不支持插值语法,但可以使用 formatted 方法:
var greeting = """
hello
%s
""".formatted("world");
NPE 更有意义
原有异常栈:
node.getElementsByTagName("name").item(0).getChildNodes().item(0).getNodeValue();
Exception in thread "main" java.lang.NullPointerException
at Unlucky.method(Unlucky.java:83)
新的异常栈:
Exception in thread "main" java.lang.NullPointerException:
Cannot invoke "org.w3c.dom.Node.getChildNodes()" because
the return value of "org.w3c.dom.NodeList.item(int)" is null
at Unlucky.method(Unlucky.java:83)
Java 16
Record 类
与 Scala 中 case class 类似。
用于声明一个不可变的数据类。
public record Point(int x, int y) { }
var point = new Point(1, 2);
point.x(); // returns 1
point.y(); // returns 2
该声明的含义如下:
- two
private
final
fields,int x
andint y
- a constructor that takes
x
andy
as a parameter x()
andy()
methods that act as getters for the fieldshashCode
,equals
andtoString
, each takingx
andy
into account
其限制如下:
- 不能含有任何非 final 字段
- 默认构造器需要包含所有字段,可以声明额外构造器以提供默认字段值
- 不能继承其他类
- 不能声明 native 方法
- 隐式 final,不能声明为 abstract
提供隐私无参构造器,也可以显式声明无参构造器以实现参数校验:
public record Point(int x, int y) {
public Point {
if (x < 0) {
throw new IllegalArgumentException("x can't be negative");
}
if (y < 0) {
y = 0;
}
}
}
声明额外构造器必须委托给其他构造器:
public record Point(int x, int y) {
public Point(int x) {
this(x, 0);
}
}
访问器可以被覆写,其他隐私的方法如 hashCode、equals、toString 也可以被覆写:
public record Point(int x, int y) {
@Override
public int x() {
return x;
}
}
能够声明静态或实例方法:
public record Point(int x, int y) {
static Point zero() {
return new Point(0, 0);
}
boolean isZero() {
return x == 0 && y == 0;
}
}
可以实现 Serializable 接口,且不需要提供 serialVersionUID:
public record Point(int x, int y) implements Serializable { }
public static void recordSerializationExample() throws Exception {
Point point = new Point(1, 2);
// Serialize
var oos = new ObjectOutputStream(new FileOutputStream("tmp"));
oos.writeObject(point);
// Deserialize
var ois = new ObjectInputStream(new FileInputStream("tmp"));
Point deserialized = (Point) ois.readObject();
}
可以直接在方法体内声明 Record 类:
public List<Product> findProductsWithMostSaving(List<Product> products) {
record ProductWithSaving(Product product, double savingInEur) {}
products.stream()
.map(p -> new ProductWithSaving(p, p.basePriceInEur * p.discountPercentage))
.sorted((p1, p2) -> Double.compare(p2.savingInEur, p1.savingInEur))
.map(ProductWithSaving::product)
.limit(5)
.collect(Collectors.toList());
}
模式匹配:instanceof
instanceof 语法支持自动 cast:
if (obj instanceof String s) {
// use s
}
新的 instanceof 检查与原来的逻辑类似,但如果检查总是通过,将会直接抛出错误:
// "old" instanceof, without pattern variable:
// compiles with a condition that is always true
Integer i = 1;
if (i instanceof Object) { ... } // works
// "new" instanceof, with the pattern variable:
// yields a compile error in this case
if (i instanceof Object o) { ... } // error
模式检查通过则提取出一个模式变量,该变量为常规的 non-final 变量类似:
- 可以被修改
- 覆盖字段声明
- 如果有相同名称的本地变量,则编译失败
模式变量可以直接用于后置的检查逻辑:
if (obj instanceof String s && s.length() > 5) {
// use s
}
模式变量的作用域也不仅限于检查内部:
private static int getLength(Object obj) {
if (!(obj instanceof String s)) {
throw new IllegalArgumentException();
}
// s is in scope - if the instanceof does not match
// the execution will not reach this statement
return s.length();
}
Java 17
Sealed 类
与 Scala 中 sealed trait 类似。
用于声明一个边界清晰的抽象层级。Sealed 类的子类可以选择 3 种修饰符,以约束抽象边界:
- final:子类无法再被继承
- sealed:子类仅能被允许的类继承
- non-sealed:子类可以被自由继承
public sealed class Shape {
public final class Circle extends Shape {}
public sealed class Quadrilateral extends Shape {
public final class Rectangle extends Quadrilateral {}
public final class Parallelogram extends Quadrilateral {}
}
public non-sealed class WeirdShape extends Shape {}
}
替代 Enum
在支持 Sealed 类之前,只能在单个类文件内通过 Enum 建模固定类型,Sealed 则更加灵活,而且能够用于模式匹配。
Enum 类可以通过 values 方法遍历子类,而 Seaed 类的子类也可以通过 getPermittedSubclasses 来遍历。
模式匹配:switch
之前的 switch 表达式是有限的,仅能用于判定完全相等性,且仅支持有限的类型:数字、枚举、字符串。
该预览特性支持 switch 表达式走用于任意类型,以及更加复杂的匹配模式。
原来的模式:
var symbol = switch (expression) {
case ADDITION -> "+";
case SUBTRACTION -> "-";
case MULTIPLICATION -> "*";
case DIVISION -> "/";
};
增强后支持类型模式语法:
return switch (expression) {
case Addition expr -> "+";
case Subtraction expr -> "-";
case Multiplication expr -> "*";
case Division expr -> "/";
};
比如:
String formatted = switch (o) {
case Integer i && i > 10 -> String.format("a large Integer %d", i); // 引用了 i
case Integer i -> String.format("a small Integer %d", i);
default -> "something else";
};
同时支持 null 值,不再是抛出 NPE:
switch (s) {
case null -> System.out.println("Null");
case "Foo" -> System.out.println("Foo");
default -> System.out.println("Something else");
}
如果匹配语句没有包含所有可能的输入,编译器则会直接报错:
Object o = 1234;
// OK
String formatted = switch (o) {
case Integer i && i > 10 -> String.format("a large Integer %d", i);
case Integer i -> String.format("a small Integer %d", i);
default -> "something else";
};
// Compile error - 'switch' expression does not cover all possible input values
String formatted = switch (o) {
case Integer i && i > 10 -> String.format("a large Integer %d", i);
case Integer i -> String.format("a small Integer %d", i);
};
// Compile error - the second case is dominated by a preceding case label
String formatted = switch (o) {
case Integer i -> String.format("a small Integer %d", i);
case Integer i && i > 10 -> String.format("a large Integer %d", i);
default -> "something else";
};
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.