文章目录
  1. 1. var关键字
  2. 2. 集合字面量
  3. 3. Deprecated
  4. 4. 接口的private方法
  5. 5. Optional的新方法
  6. 6. HttpClient
  7. 7. 可过期的CompletableFuture
  8. 8. Process API

还在用着Java 8吗?09月26日Oracle的长期支持版Java 11已经出炉,将一直支持到2026年9月。对广大的程序员们来说,从Java 9~11,日常的编码都有什么变化呢?一起来看看吧。

var关键字

Java 10引进了var关键字来指代任意类型,让它朝C#又迈进了一步。以下是两个例子:

1
2
3
var abc = new ArrayList<String>();
for (var character : abc) {
}

var并不仅仅能类型推断,它还能完成以前做不到的事——引用匿名类的变量:

1
2
3
4
var o = new Object() {
int a = 1;
};
System.out.println(o.a);

在所有可能的地方都用上var也许并不是一个好实践。一种做法是:如果后面的表达式一眼就能看出来是啥类型,我们就用var;否则,就还是老老实实地写声明,一眼扫过就能明白的代码更具可读性。

更详细的用法请参考Oracle官方文档。Java 11更是允许对lambda的参数使用var

集合字面量

一直以来我们都是老老实实地用以下这些方法来初始化一个已知的不可变集合:

1
2
3
4
5
6
7
8
// 这样挺方便,就是语义上稍欠优雅
List<String> abc1 = Arrays.asList("A", "B", "C");
// 这样构建的是Collection,可以传入可变集合
Collection<String> abc2 = Collections.unmodifiableCollection(abc1);
// 更优雅的Guava方案
List<String> abc3 = ImmutableList.of("A", "B", "C");

可变集合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 最基本的方法,就是有点长,令人不爽
List<String> abc4 = new ArrayList<>();
abc4.add("A");
abc4.add("B");
abc4.add("C");
// 构造函数
List<String> abc5 = new ArrayList<>(Arrays.asList("A", "B", "C"));
// 匿名内部类
List<String> abc6 = new ArrayList<>(){{ add("A");add("B");add("C"); }}
// 利用Java 8的Stream
List<String> abc7 = Stream.of("A", "B", "C").collect(Collectors.toList());
// 更优雅的Guava方案
List<String> abc8 = Lists.newArrayList("A", "B", "C");

对于不可变集合,Java 9引入了集合的工厂方法of,这回终于可以用上原装的了:

1
2
3
4
5
6
List<String> abc1 = List.of("A", "B", "C");
Set<String> abc2 = Set.of("A", "B", "C");
Map<String, Integer> abc3 = Map.of("A", 1, "B", 2, "C", 3);
Map<String, Integer> abc4 = Map.ofEntries(Map.entry("A", 1),
Map.entry("B", 2),
Map.entry("C", 3));

值得注意的是,List不允许通过of传入nullMap不允许传入相同的键。

1
2
List.of("A", null); // NullPointerException
Map.of("A", 1, "A", 2); // IllegalArgumentException: duplicate key: A

Java 10引入了copyOf方法,也能方便地从可变集合中创建出不可变集合:

1
2
var abcMutable = new ArrayList<>(List.of("A", "B", "C"))
var abcImmutable = List.copyOf(abcMutable);

Java 10还在Collectors类中增加了toUnmodifiableList/toUnmodifiableMap/toUnmodifiableSet方法,可以直接从Stream创建一个不可变集合:

1
List<String> abc = Stream.of("A", "B", "C").collect(Collectors.toUnmodifiableList());

Deprecated

这个相对简单,就是给@Deprecated注解增加了两个字段:

1
@Deprecated(since="1.1", forRemoval = true)

前者表示从哪个版本起不建议使用,后者表示未来是否会将其删除。

接口的private方法

从Java 8起,允许给接口增加default方法。从Java 9起,接口又增加了一项能力:可以定义private方法了。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface Dog {
default void bark() {
System.out.println("bowwow");
wagTail();
}
default void walk() {
System.out.println("walk");
wagTail();
}
private void wagTail() {
System.out.println("wag");
}
}

既然如此,它跟抽象类还有什么区别吗?接口的优势就是允许子类实现多个接口,而抽象类因为可以拥有可变字段(接口的字段是final的)而更为强大。我们在接口定义方法时,也应当让其尽可能简洁。

Optional的新方法

从Java 9起,Optional终于可以通过stream()方法返回一个Stream了,这样它就可以用上Stream提供的许多API了。原来只能这么做:

1
2
3
4
5
6
7
Optional<String> optional = Optional.of("ggg");
// java 8
Stream<String> texts = optional.map(Stream::of).orElseGet(Stream::empty);
// java 9
Stream<String> texts = optional.stream();

另一个可以耍耍的方法是ifPresentOrElse

1
2
3
4
5
6
7
8
9
// java 8
if (optional.isPresent()) {
System.out.print(optional.get());
} else {
System.out.println();
}
// java 9
optional.ifPresentOrElse(System.out::print, System.out::println);

Java 9还新增了一个方法or,与原来的orElse类似,但是返回的是一个Optional

1
2
3
4
5
// java 8
String name = optional.orElse("n/a");
// java 9
Optional<String> name = optional.or(() -> Optional.of("n/a"));

HttpClient

Java 11引进了HttpClient,http请求变得简单了:

1
2
3
4
5
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(uri)).build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());

异步也很简单,返回一个CompletableFuture

1
2
3
4
5
6
7
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(uri)).build();
CompletableFuture<Void> futureResponse = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);
futureResponse.get();

原生的HttpURLConnection可以抛弃了。Apache的HttpClient也许也可以雪藏了。

可过期的CompletableFuture

上一小节中,我们可以得到一个CompletableFuture。从Java 9起,它可以设置过期时间了。可以把上面的futureResponse.get()替换如下:

1
2
CompletableFuture<Void> timeoutFuture = futureResponse.orTimeout(1, TimeUnit.SECONDS);
timeoutFuture.get();

如果没有在设置的时间内获得结果,便会抛出java.util.concurrent.ExecutionException: java.util.concurrent.TimeoutException。如果想在过期时不抛异常而是设一个默认值,可以这样做:

1
2
3
4
CompletableFuture<String> futureResponse = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
CompletableFuture<String> timeoutFuture = futureResponse.completeOnTimeout("default value", 1, TimeUnit.MILLISECONDS);
timeoutFuture.get();

Process API

Process API是元老级的API了,但是功能一直不够完整,无法获取进程的PID、用户、命令等。Java 9引入了ProcessHandle,可以查询进程,甚至允许在进程退出时执行方法。它提供了获取当前进程的current方法,以及获取全部进程的allProcesses方法。用法如下:

1
2
3
4
Optional<String> currentUser = ProcessHandle.current().info().user();
ProcessHandle.allProcesses()
.filter(p -> p.info().user().equals(currentUser))
.forEach(p -> System.out.println(String.valueOf(p.pid()) + " " + p.info().command()));

如果在Mac中打开了一个TextEdit,便可以看到类似这样的输出结果:1234 Optional[/Applications/TextEdit.app/Contents/MacOS/TextEdit]。Windows的话可以打开notepad.exe,也能看到:1234 Optional[C:\Windows\System32\notepad.exe]。可以用以下方法在该进程退出时打印一些信息:

1
2
3
4
Optional<ProcessHandle> optionalProcessHandle = ProcessHandle.of(1234);
CompletableFuture<Void> future = optionalProcessHandle.get()
.onExit().thenAccept(x -> System.out.println(x.pid()));
future.get();

甚至还能用optionalProcessHandle.get().destroy()来摧毁进程。如此这般,Java外部打开的TextEditnotepad.exe都会被退出。由于其它用户的进程无法使用ProcessHandle.of来获取,所以只能杀掉自己的进程。对安全方面感兴趣的话可以参考一下官方文档

文章目录
  1. 1. var关键字
  2. 2. 集合字面量
  3. 3. Deprecated
  4. 4. 接口的private方法
  5. 5. Optional的新方法
  6. 6. HttpClient
  7. 7. 可过期的CompletableFuture
  8. 8. Process API