女同事:哈哥,咱们之前用的java8,现在改用Java11,相比是有哪些新特性么?
我:哦,咱们是为了提高和其他子系统程序的兼容性稳定,才提高了JDK版本。
女同事:喔?那就是说你不知道有哪些新特性呗。
我:(😳😳??你们00后思路都这么清晰吗)咳咳,嗯,我只略知一二;
女同事:那你给我说说呗,我可想知道了呢~
我:没问题~走,先干饭去。(独门绝技:饭遁
)
一提到JDK8的新特性,你可能就会联想到Lambda、函数式接口、Stream、Optional以及日期时间API增强等等;
诚然,这些都是我们日常在使用的特性了。然而你没在意的是JDK已经默默更新到JDK16了(平均6个月一版),并且在过几个月就JDK17了哈哈哈!唉,真是菜的令人头大。
前两天粉丝群的同学在分享面试题时,我看到面试题中包括:请说一下JDK8到JDK11都有哪些新特性?
。我一惊,都卷成这吊样子了?
虽然我也在用JDK11然而我对新特性还停留在JDK8。。到底是我们老了还是卷不过年轻人了?😳😳下面是我总结各版本JDK的一些特性,看看你有哪些不知道吧。
本文我们梳理一些在日常编程中能用到的一些新特性,或许你会发现新大陆,赶快更新自己的代码吧~~当然,上线维护的代码尽量就别改了,你懂得。
车票
- JDK9(2017年9月)
-
- 9-1、实例工厂方法
- 9-2、接口里可以添加私有方法
- 9-3、改进 try-with-resources
- 9-4、多版本兼容 jar 包
- 9-5、增强了钻石操作符 `<>`,可以在匿名内部类中使用了。
- 9-6、增强了JDK8特性
- JDK10(2018年3月)
-
- 10-1、局部变量的自动类型推断(var)
- 10-2、增强版Optional
- Java 11(2018年9月)
-
- 11-1、Lambda 中使用` var`
- 11-2、字符串 API 增强
- 11-3、javac + java 命令归一化
- JDK 12(2019年3月)
-
- 12-1、Switch表达式
- 12-2、instanceof + 类型强转一步到位
- JDK 13(2019年9月)
-
- 13-1、文本块(Text Block)的支持
- 13-2、增强 switch 语法:
- JDK14(2020年3月)
-
- 14-1、新增 Record 类
- 14.2、新增 jpackage 打包工具
- JDK15(2020年9月)
-
- 15-1、ZGC 和 Shenandoah 两款垃圾回收器正式登陆
- 15-2、封闭(Sealed)类
- JAVA 16(2021年3月)
- 小结
JDK9(2017年9月)
9-1、实例工厂方法
借助Java 9的一项新功能,即集合工厂方法,我们可以轻松地使用预定义的数据创建不可变的集合。只需要在特定集合类型上使用of
方法。
List<String> fruits = List.of("apple", "xiaomi", "13xiang");
Map<Integer, String> numbers = Map.of(1, "one", 2,"two", 3, "three");
Set<String> colors = Set.of("yellow", "red", "baoqiang");
在Java 9之前,我们想要创建一个不可变的集合,需要先创建一个可变集合,然后使用 unmodifiableSet 创建不可变集合,显然是很麻烦的。
public List<String> fruits() {
List<String> fruitsTmp = new ArrayList<>();
fruitsTmp.add("apple");
fruitsTmp.add("xiaomi");
fruitsTmp.add("13xiang");
return Collections.unmodifiableList(fruitsTmp);
}
public Map<Integer, String> numbers() {
Map<Integer, String> numbersTmp = new HashMap<>();
numbersTmp.put(1, "one");
numbersTmp.put(2, "two");
numbersTmp.put(3, "three");
return Collections.unmodifiableMap(numbersTmp);
}
public Set<String> setStr() {
Set<String> colors = new HashSet<>();
colorsTmp.add("A");
colorsTmp.add("B");
colorsTmp.add("C");
colorsTmp = Collections.unmodifiableSet(colors);
System.out.println(colorsTmp);
return colorsTmp;
}
同样,仅从ArrayList对象表创建即可使用Arrays.asList(…)method。
public List<String> fruitsFromArray() {
String[] fruitsArray = {"apple", "xiaomi", "13xiang"};
return Arrays.asList(fruitsArray);
}
9-2、接口里可以添加私有方法
基于JAVA 8 对接口增加了默认方法的支持,在 JAVA 9 中对该功能进一步升级,可以在接口里定义私有方法
,并且可以在默认方法里调用接口的私有方法。
ublic interface testInterface {
String test();
// 接口默认方法
default String defaultTest() {
privateTest();
return "default";
}
private String privateTest() {
System.out.println("private method privateTest");
return "private";
}
}
9-3、改进 try-with-resources
Java 9 中不需要在 try 中额外定义一个变量。Java 9 之前需要这样使用 try-with-resources:
InputStream inputStream = new StringBufferInputStream("陈哈哈");
try (InputStream in = inputStream) {
in.read();
} catch (IOException e) {
e.printStackTrace();
}
在 Java 9 中可以在try中直接使用 inputStream 变量
,不需要再额外定义新的变量了。
InputStream inputStream = new StringBufferInputStream("陈哈哈");
try (inputStream) {
inputStream.read();
} catch (IOException e) {
e.printStackTrace();
}
9-4、多版本兼容 jar 包
Java 9 中支持在同一个 JAR 中维护不同版本的 Java 类和资源。
9-5、增强了钻石操作符 <>
,可以在匿名内部类中使用了。
其实JAVA 5 就引入了泛型(generic),到了 JAVA 7 开始支持钻石(diamond)运算符,
<>,可以自动推断泛型的类型
。
在 Java 9 之前,内部匿名类需要指定泛型类型,如下:
List<Integer> numbers = new ArrayList<Integer>() {
}
而在 Java 9 中,可以自动做类型推导,如下:
List<Integer> numbers = new ArrayList<>() {
}
9-6、增强了JDK8特性
增强了JDK8的特性如:Stream,Optional,Process API
JDK10(2018年3月)
10-1、局部变量的自动类型推断(var)
var a = "Hello, Java 10";
System.out.println(a);
JAVA 10 带来了一个很有意思的语法var
,它可以自动推断局部变量的类型
,以后再也不用写类型了,也不用靠 lombok 的 var 注解增强了
不过这个只是语法糖,编译后变量还是有类型的,使用时还是考虑下可维护性的问题。强调一下,var 关键字目前只能用于局部变量以及 for 循环变量声明中。
10-2、增强版Optional
从Java 9和Java 10开始,有几种用于Optional的有用方法。其中最有趣的两个是orElseThrow和ifPresentOrElse。
如果没有值,则使用该orElseThrow方法抛出NoSuchElementException。否则,它返回一个值。
public Person getPersonById(Long id) {
Optional<Person> personOpt = repository.findById(id);
return personOpt.orElseThrow();
}
因此,我们可以避免将带参数的if语句与isPresentmethod一起使用。
public Person getPersonByIdOldWay(Long id) {
Optional<Person> personOpt = repository.findById(id);
if (personOpt.isPresent())
return personOpt.get();
else
throw new NoSuchElementException();
}
第二种有趣的方法是ifPresentOrElse。如果存在一个值,它将使用该值执行给定的操作。否则,它将执行给定的基于空的操作。
public void printPersonById(Long id) {
Optional<Person> personOpt = repository.findById(id);
personOpt.ifPresentOrElse(
System.out::println,
() -> System.out.println("Person not found")
);
}
在Java 8中,我们可以if-else直接与isPresent方法一起使用。
public void printPersonByIdOldWay(Long id) {
Optional<Person> personOpt = repository.findById(id);
if (personOpt.isPresent())
System.out.println(personOpt.get());
else
System.out.println("Person not found");
}
Java 11(2018年9月)
11-1、Lambda 中使用var
public String sumOfString() {
BiFunction<String, String, String> func = (var x, var y) -> x + y;
return func.apply("abc", "efg");
}
11-2、字符串 API 增强
Java 11 新增了 一系列字符串处理方法,例如:
// 判断字符串是否为空白、以及判空
" ".isBlank();
"".isEmpty();
" Java11 ".stripTrailing(); // " Java11"
" Java11 ".stripLeading(); // "Java11 "
11-3、javac + java 命令归一化
以前编译一个 java 文件时,需要先 javac 编译为 class,然后再用 java 执行,现在可以放到一起执行了,香啊!
$ java HelloWorld.java
Hello Java 11!
JDK 12(2019年3月)
12-1、Switch表达式
我们可以定义多个case标签并使用箭头返回值,称之为:使用case L ->
箭头标签的switch。
此功能自JDK 12起可用。它使Switch表达式真正更易于访问。
public String newSwitch(int day) {
return switch (day) {
case 1, 2, 3 -> "摸鱼工作日";
case 4 -> "小周五";
case 5, 6, 7 -> "休息日";
default -> "invalid";
};
}
如下代码:
newSwitch(1);
newSwitch(4);
newSwitch(6);
结果如下:
摸鱼工作日
小周五
休息日
而对于低于12的Java,相同的示例要复杂得多。
public String oldSwitch(int day) {
switch (day) {
case 1:
case 2:
case 3:
return "摸鱼工作日";
case 4:
return "小周五";
case 5:
case 6:
case 7:
return "休息日";
default:
return "invalid";
}
}
对于我们
休息日
和小周五
的定义是不是感觉阵阵蒙圈?别意外,在我们的SQL大腿群
中,小周五
和周五
已经成为了每周的传统节日,值的庆祝~~
12-2、instanceof + 类型强转一步到位
之前处理动态类型碰上要强转时,需要先 instanceof 判断一下,然后再强转为该类型处理:
Object obj = "Hello Java 12!";
if (obj instanceof String) {
String s = (String) obj;
int length = s.length();
}
现在 instanceof 支持直接类型转换了,不需要再来一次额外的强转:
Object obj = "Hello Java 12!";
if (obj instanceof String str) {
int length = str.length();
}
JDK 13(2019年9月)
13-1、文本块(Text Block)的支持
文本块是多行字符串文字,它避免使用转义序列,并以可预测的方式自动设置字符串格式。它还使开发人员可以控制字符串的格式。从Java 13开始,文本块可用作预览功能。它们以三个双引号("""
)开头。让我们看看我们如何轻松地创建和格式化JSON消息。
public String getNewPrettyPrintJson() {
return """
{
"name": "chenhaha",
"sex": "undefined"
}
""";
}
创建Java 13之前的相同JSON字符串要复杂得多。
public String getOldPrettyPrintJson() {
return "{\n" +
" \"name\": \"chenhaha\",\n" +
" \"sex\": \"undefined\"\n" +
"}";
}
13-2、增强 switch 语法:
JAVA 12 中虽然增强了 swtich 语法,但并不能在 -> 之后写复杂的逻辑,JAVA 13 带来了 swtich更完美的体验,就像 lambda一样,可以写逻辑,然后再返回:
int j = newSwitch (day) {
case 1, 2, 3 -> "摸鱼工作日";
case 4 -> "小周五";
case 5, 6, 7 -> "休息日";
default -> {
var k = day.toString().length();
var res = f(k);
yield res;
}
};
JDK14(2020年3月)
14-1、新增 Record 类
从Java 14开始,引入了新的Record类。我们定义Record类时,使用关键字record;
使用Records可以定义不可变的纯数据类
(仅限getter),也叫记录类
。它会自动创建toString,equals和hashCode方法。实际上,只需要定义如下所示的字段即可。
public record Person(String name, int age) {}
具有类似功能的类如record包含字段
,构造函数
,getter
和toString
,equals
以及hashCode
方法。
public class PersonOld {
private final String name;
private final int age;
public PersonOld(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PersonOld personOld = (PersonOld) o;
return age == personOld.age && name.equals(personOld.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "PersonOld{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
14.2、新增 jpackage 打包工具
新增 jpackage 打包工具,直接打包二进制程序,再也不用装 JRE 了!
之前如果想构建一个可执行的程序,还需要借助三方工具,将 JRE 一起打包,或者让客户电脑也装一个 JRE 才可以运行我们的 JAVA 程序。
现在 JAVA 直接内置了 jpackage 打包工具,帮助你一键打包二进制程序包,不用再乱折腾了。
JDK15(2020年9月)
15-1、ZGC 和 Shenandoah 两款垃圾回收器正式登陆
在 JAVA 15中,ZGC 和 Shenandoah 再也不是实验功能,正式登陆了(不过 G1 仍然是默认的)。如果你升级到 JAVA 15 以后的版本,就赶快试试吧,性能更强,延迟更低。
15-2、封闭(Sealed)类
使用密封类功能,可以限制超类的使用
。使用new关键字,sealed
可以定义哪些其他类或接口可以扩展或实现当前类。
public abstract sealed class Pet permits Cat, Dog {}
允许的子类必须定义一个修饰符。如果不想允许任何其他扩展名,则需要使用final关键字。
public final class Cat extends Pet {}
另一方面,你可以打开扩展类。在这种情况下,应使用non-sealed修饰符。
public non-sealed class Dog extends Pet {}
当然,下面的可见声明是不允许的。
public final class Tiger extends Pet {}
JAVA 16(2021年3月)
JAVA 16 在用户可见的地方变化并不多,基本都是 14/15 的实验性内容;
小结
那么开发人员为什么还坚持用Java8呢?
或许是因为升级在代码和维护层面或多或少会出现意想不到的BUG,很多企业求稳,领导们不愿随意改动。升级没问题的话领导倒是能多拿点儿提成,万一出了问题,领导能给你背锅??
说回来程序员本身也不一定有时间去学习新特性和深入理解新的技术特性,有点时间多看看面试题它不香么?
Java每三年会有一个长期支持的版本
(Long Term Support release,简称LTS),该版本会提供为期三年的支持。Java 8是一个LTS,所以值得信赖。并且已基本被业内广泛使用,而下一个LTS分别是Java11和Java17
,Mark一下吧。
但是,目前1线的java开发者中,应该还是大部分在使用Java8的版本。有数据统计 80% 的人还在用JDK8,甚至有的公司还在使用JDK7。
问题来了?大家所用的Java版本是多少呢?