Java18都快推出了,还有很多的小伙伴可能还在用Java8,可能还不知道很多高版本提供的一些好用的特性,下面我整理了一下从Java9~Java17中我个人觉得一些比较实用的特性,让大家在推动公司Java版本的升级时有更多的理由。
可能很多公司会为了“稳定性”而拒绝升级高版本的JDK,就和以前Java8刚推出时,还在用JDK1.6的人拒绝升级一样。但实际上是有多少是真的为了稳定性,还有多少只是公司单纯的拒绝新的技术,这谁知道呢?^ _ ^
一些特性
JEP 286: Local-Variable Type Inference(Java11)
变量类型推导,可以让我们在使用局部变量时,使用var
关键字来定义一个变量:
var list = new ArrayList<String>();
复制代码
这个算是一个我比较喜欢且最常用的一个特性了,可以显著的简化代码,但是也会带来一个问题:可读性下降。该如何使用,看个人权衡了。
JEP 358: Helpful NullPointerExceptions(Java14)
我相信大部分人的处理NPE时都会遇到的一个令人头疼的问题,那就是报NPE的那行是一个链式调用,比如:
class Main {
public static void main(String[] args) {
var x = a().b().i.j;
}
}
Exception in thread "main" java.lang.NullPointerException:
at Main.main(Main.java:3)
复制代码
这样的NPE异常毫无指向性,你可能根本不知道是哪个方法或变量报的异常。而在Java14及以后,NPE将会变得更加有的指向性,还是同样的代码,在Java14中的异常则会是:
Exception in thread "main" java.lang.NullPointerException:
Cannot read field "j" because "i" is null
at Main.main(Main.java:3)
复制代码
这可以让我们在排查问题时,可以更清晰的定位问题。
扫VX 领Java资料,前端,测试,python等等资料都有
JEP 361: Switch Expressions(Java14)
更优雅、更简洁的Switch表达式,再也不用写一堆的CASE/BREAK 了
switch(month) {
case 1,3,5,7,8,10,12 -> System.out.println("31");
case 4,6,9,11 -> System.out.println("30");
case 2 -> System.out.println("28");
};
复制代码
switch也可以当作表达式使用,即可以提供返回值:
var x = switch(condition) {
case A -> "a";
case B -> "b";
default -> "none";
};
复制代码
新增的yield关键字还可以让你在表达式中处理一大段的代码块:
var x = switch(condition) {
case A -> "a";
case B -> "b";
default -> {
Sytem.out.println("in default");
//do something else
var result = doSomething();
yield result;
}
};
复制代码
JEP 378: Text Blocks(Java15)
终于终于终于可以不用在字符串中处理一堆的反斜杆,\n, +号拼接了!!
简单的看一个对比:
var script = "function hello() {\n" +
" print('\"Hello, world\"');\n" +
"}\n" +
"\n" +
"hello();\n"
复制代码
Java15及以后:
var script = """
function hello() {
print('"Hello, world"');
}
hello();
"""
复制代码
可读性相差太多了,如此简单、常用的基础能力,早就应该提供了!你也可以像和普通字符串一样,使用占位符和format方法。
JEP 395: Records(Java16) 数据类,有点类似于Kotlin中的data object. 可以使用下面的方式来定义一个数据类:
public record Data(int x, int y) {
}
复制代码
这个类会默认实现hashCode()、equals()和toString()方法
,并且这个类被被定义为final,并且不可变:字段初始化后即不可再修改。在类里面也可以定义普通或静态方法。
在我们需要返回一些不可变的数据给外部使用时,可以使用record来定义数据格式,在SpringBoot 2.5.0-M1之后(或者升级jackson版本),也提供对Java16的支持,这意味着你可以在spring部分场景中使用,比如controller接收参数时,参数可以是一个record类。
JEP 394: Pattern Matching for instanceof(Java16)
模式匹配,或者说是类型匹配,这也是在Kotlin中已有的功能,可以让我们在编码时少做一些强制类型转换的工作:
if (obj instanceof String) {
String s = (String) obj;
s.toLowerCase();
}
复制代码
Java16及以后:
if (obj instanceof String) {
//自动类型推导
obj.toLowerCase();
}
//甚至可以这么用
var isGood = obj instanceof String && obj.trim().toLowerCase().equals("good");
复制代码
怎么说呢,在一些场景下,一直做类型强制转换也是挺麻烦的一件事情,这个特性起码可以减少一些编码的工作量。
JEP 409: Sealed Classes(Java17)
提供一种新的约束条件给类或接口,这个一般场景下可能用不上,但是让我们需要写一个框架,或者需要写一个SDK给其它部门或者用户使用的时候,这个特性就可以派上用场了。举个栗子:
public abstract sealed InnerAbsClass permits InnerClassA, InnerClassB {
}
复制代码
当一个类被sealed修饰之后,那么只有被permits关键字指定的名可以继承这个抽象类,其它的类如果尝试继承这个抽象类则会在编译时就会报异常。
还有一种方式是不使permits关键字,那么就只能在同个文件内定义子类:
public abstract sealed InnerAbsClass {
}
public final InnerClassA extends InnerAbsClass {}
public final InnerClassB extends InnerAbsClass {}
复制代码
接口也可以使用同样的方式来定义。
为什么不使用“包访问权限”来限制继承或实现?有些场景我们虽然希望父类不被外部使用者所继承,但是同时也希望他可以被访问,比如提供了一些静态的变量、静态方法可以被外部使用。
总结
上面并不是所有的特性,但是是我个人实际使用过,也觉得确实好用,提高了生产力的一些功能,也分享给大家,说不定还在使用低版本Java的人看到了之后,也决定推动公司升级了呢。 而且Java17是Oracle提供的一个长期支持版本,最多可以支持到 2029年9月份,长达8年的支持起码也可以保障它的稳定性。
最后,追求稳定没有任何问题,但是拥抱新的变化也不错~
扫VX 领Java资料,前端,测试,python等等资料都有