Java 9-15最接近我们的特性

抛去各种各样特别牛的特性不谈,我们就说一下我们肯定会经常用到的几个特性。其实就几个,switch表达式,ZGC,var关键字,文本块,record,instanceof,还有更实用的nullpointer报错。

从简单的开始看起。

注:本文描述特性的时候包含很多直接抄来的样例,有的特性需要编译的时候开启特定开关,本文并不关注这些细节,而是关注特性本身。

1、更人性化的NullPointer报错

例如,如下代码:

var name = user.getLocation().getCity().getName();

在Java 14之前,你可能会得到如下的错误:

Exception in thread "main" java.lang.NullPointerException
  at NullPointerExample.main(NullPointerExample.java:5)    

不幸的是,如果在第5行是一个包含了多个方法调用的赋值语句(如getLocation()和getCity()),那么任何一个都可能会返回null。实际上,变量user也可能是null。因此,无法判断是谁导致了NullPointerException。

在Java 14中,新的JVM特性可以显示更详细的诊断信息:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Location.getCity()" because the return value of "User.getLocation()" is null
  at NullPointerExample.main(NullPointerExample.java:5)

该消息包含两个明确的组成部分:

  • 后果:Location.getCity()无法被调用

  • 原因:User.getLocation()的返回值为null

这个特性简直太实用了,看到的时候我简直拍手叫好。不知道多少次线上环境报nullpointer的时候要各种排查到底哪个变量是null,曾经还碰到过函数调用的时候检查各个变量是否为null,结果最终证明是Service注入失败导致Service本身是null,让人吐血。

2、record类

这个类的唯一目的就是将数据整合在一起。Record会提供equals、hashCode和toString方法的实现。

public record BankTransaction(LocalDate date,
               double amount,
               String description) {}

通过record,可以“自动”地得到equals,hashCode和toString的实现,还有构造器和getter方法。

实用程度一般,替代了部分lombok的作用,能少写一部分代码。

3、文本块

这个小特性在很多现代化的语言都早就有了,java终于引入了。

下面的代码演示了一个HTML的例子:

String html = "<HTML>" +
"\n\t" + "<BODY>" +
"\n\t\t" + "<H1>\"Java 14 is here!\"</H1>" +
"\n\t" + "</BODY>" +
"\n" + "</HTML>";

有了文本块,就可以简化这一过程,只需使用三引号作为文本块的起始和结束标记,就能编写出更优雅的代码:

String html = """
<HTML>
 <BODY>
  <H1>"Java 14 is here!"</H1>
 </BODY>
</HTML>""";

与普通字符串字面量相比,文本块的表达性更好。

4、升级过的instanceof关键字

Java 14引入了一个预览特性,有了它就不再需要编写先通过instanceof判断再强制转换的代码了。例如,下面的代码:

if (obj instanceof Group) {
 Group group = (Group) obj;
 *// use group specific methods*
 var entries = group.getEntries();
}

利用这个预览特性可以重构为:

if (obj instanceof Group group) {
 var entries = group.getEntries();
}

由于条件检查要求obj为Group类型,为什么还要像第一段代码那样在条件代码块中指明obj为Group类型呢?这可能会引发错误。

这种更简洁的语法可以去掉Java程序里的大多数强制类型转换。

5、var关键字

几乎任何局部变量以后都可以用var声明了,太方便了

public static void main(String[] args) {
 //局部变量初始化
 var list = new ArrayList<String>();
   //for循环内部索引变量
   for (var s : list) {
     System.out.println(s);
   }
   //传统的for循环声明变量
   for (var i = 0; i < list.size(); i++) {
     System.out.println(i);
   }
 }

6、switch表达式

switch语句的写法改变倒不是主要的,关键是switch现在既可以作为语句,又可以作为表达式了。

新的switch表达式的优点是,不再有缺省跳过行为(fall-through),更全面,而且表达式和组合形式更容易编写,因此出现bug的可能性就更低。例如,switch表达式现在可以使用箭头语法,如下所示:

var log = switch (event) {
  case PLAY -> "User has triggered the play button";
  case STOP, PAUSE -> "User needs a break";
  default -> {
    String message = event.toString();
    LocalDateTime now = LocalDateTime.now();
    yield "Unknown event " + message +
       " logged on " + now;
  }
};

有点期待把if语句也改造成表达式,三元运算符虽然好用,但还不够强大,希望能早日用上if表达式。

7、ZGC

我看过一些从java转go或者其它语言的案例,都是因为GC带来的延迟不可接受,ZGC大幅优化了这一问题。反正看过ZGC的整体设计之后我还是感觉挺厉害的。

ZGC (The Z Garbage Collector)是 JDK 11 中推出的一款低延迟垃圾回收器,在JDK15中将正式作为默认垃圾回收器。它的设计目标包括:

  • 停顿时间不超过 10ms;

  • 停顿时间不会随着堆的大小,或者活跃对象的大小而增加;

  • 支持 8MB~4TB 级别的堆(未来支持 16TB)。

以上就是我觉得最常用的java9-15中的新特性,其实也就是我最期待的新特性。其它特性像什么模块化之类的,虽然也很牛,跟普通用户距离有点远。java当前的LTS版本是11,而明年下半年推出的java 17是下一个LTS版本,上述feature大部分是在java11之后引入的。因此,我还是非常期待能早日用上java17的。

ZGC的参考资料:

https://mp.weixin.qq.com/s?__biz=MzAxODcyNjEzNQ==&mid=2247491327&idx=3&sn=6a125b9dcf6e82427fde174f73bc443b&chksm=9bd0b367aca73a71aa199f56bf802d7779fece5f9612dca568d76ce7defd44ddd7f55111b70c&scene=27#wechat_redirect

https://www.infoq.cn/article/HA7LLvSEyuaVp6NZpEXr

发表评论

电子邮件地址不会被公开。 必填项已用*标注