Java新特性介绍(9~17)

Java 9新特性

Java9的主要特性有JShell、接口的private方法等。

JShell

Java 9为提供一种交互式编程工具JShell,类似于Python的Shell,安装java并完成环境配置后,只需要输入jshell命令即可开启交互式编程

示例

C:\Users\71947>jshell
| 欢迎使用 JShell -- 版本 17.0.9
| 要大致了解该版本, 请键入: /help intro

jshell> int a = 10
a ==> 10

jshell> int b = 15
b ==> 15

jshell> int c = a + b
c ==> 25

jshell> public int max(int a, int b){
...> return a >= b ? a : b;
...> }
| 已创建 方法 max(int,int)

jshell> int d = max(a, b)
d ==> 15

jshell> /vars
| int a = 10
| int b = 15
| int c = 25
| int d = 15

jshell> /exit
| 再见

更多口令可以通过输入/help了解

在接口内定义private方法

在Jdk8中,接口中方法支持添加default关键字来默认实现方法:

public interface Test {
default void test(){
// ...
System.out.println("默认实现");
}
}

Java9中,接口中可以存在私有方法:

public interface Test {
default void test(){
// ...
System.out.println("默认实现");
this.inner(); //接口中方法的默认实现可以直接调用接口中的私有方法
}

private void inner(){ //声明一个私有方法
System.out.println("私有方法");
}
}

私有方法和默认方法一样,要提供方法体,并且此方法只能被接口中的其他私有方法或是默认实现调用

集合类新增工厂方法

如果我们想要快速创建一个Map:

public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("AAA", 1);
map.put("BBB", 2);
}

在Java 9之后,可以通过of方法快速创建:

public static void main(String[] args) {
Map<String, Integer> map = Map.of("AAA", 1, "BBB", 2);

System.out.println(map);
}

通过这种方式创建的Map和通过Arrays创建的List类似,无法进行修改。

除了Map之外,其他的集合类都有相应的of方法:

public static void main(String[] args) {
Set<String> set = Set.of("A""B", "C");
List<String> list = List.of("A", "B", "C");
}

改进的Stream

public static void main(String[] args) {
Stream.of("A", "B", "B", "C") //将一些元素封装到Stream中
.filter(s -> s.equals("B")) //通过过滤器过滤
.distinct() //去重
.forEach(System.out::println);
}

Stream在Java9得到了进一步的增强:

public static void main(String[] args) {
Stream.of(null) //如果传入null会报错
.forEach(System.out::println);

Stream.ofNullable(null) //使用新增的ofNullable方法,不会报错
.forEach(System.out::println);
}

可以通过迭代快速生成一组数据:

public static void main(String[] args) {
Stream.iterate(0, i -> i < 20, i -> i + 1) //快速生成一组0~19的int数据,中间可以添加一个断言,表示什么时候结束生成
.forEach(System.out::println);
}

Stream还新增了对数据的截断操作:

public static void main(String[] args) {
Stream.iterate(0, i -> i + 1)
.limit(20)
.takeWhile(i -> i < 10)
.forEach(System.out::println);
}

dropWhile和takeWhile相反,满足条件的才会通过

public static void main(String[] args) {
Stream.iterate(0, i -> i + 1)
.limit(20)
.dropWhile(i -> i < 10)
.forEach(System.out::println);
}

Java 10新特性

Java 10的改变不是很多,比较突出的是局部变量类型推断

局部变量类型推断var

在Java10中,可以使用自动类型推断:

public static void main(String[] args) {
// String a = "Hello World!"; //之前定义变量必须指定类型
var a = "Hello World!"; //现在使用var关键字来自动进行类型推断,因为完全可以从后面的值来判断是什么类型
}

var关键字必须位于有初始值设定的变量上,且仅适用于局部变量,没办法在其他地方使用

Java 11新特性

Java 11中比较关键的是用于Lambda的形参局部变量语法。

用于Lambda的形参局部变量语法

在Java 11中支持了在Lambda中使用var关键字

public static void main(String[] args){
Consumer<String> consumer = (var str) -> {};
}

针对于String类的方法增强

在Java 11为String新增一些更加方便的操作:

public static void main(String[] args) {
var str = "A\nB\nD";
System.out.println(str.isBlank()); //isBlank方法用于判断是否字符串为空或者是仅包含空格
str.lines() //根据字符串中的\n换行符进行切割,分为多个字符串,并转换为Stream进行操作
.forEach(System.out::println);
}

可以快速地进行空格去除操作:

public static void main(String[] args) {
String str = " A B C D ";
System.out.println(str.strip()); //去除首尾空格
System.out.println(str.stripLeading()); //去除首部空格
System.out.println(str.stripTrailing()); //去除尾部空格
}

Java 12~16 新特性

Java12-16并非长期支持版本,很多特性都是实验性的功能。

改进的switch语法

Java 12中引入改进的switch语法,之前的switch写法为:

public static String grade(int score){
String res = null;
switch (score) {
case 10:
case 9:
res = "优秀";
break;
case 8:
case 7:
res = "良好";
break;
case 6:
res = "及格";
break;
default:
res = "不及格";
break;
}
return res;
}

使用新特性:

public static String grade(int score){
return switch (score) { //增强版switch语法
case 10, 9 -> "优秀";
case 8, 7 -> "良好";
case 6 -> "及格";
default -> "不及格";
};
}

这种全新的switch语法称为switch表达式,具体语法为:

var res = switch (obj) {
case [匹配值, ...] -> "优秀";
case ...
default -> "不及格";
};
  • case后添加匹配值,匹配值可以存在多个,需要使用逗号隔开,使用 -> 来返回如果匹配此case语句的结果
  • 根据不同的分支,可以存在多个case

那么如果我们并不是能够马上返回,而是需要做点什么其他的工作才能返回结果呢?

var res = switch (obj) {
case [匹配值, ...] -> "";
default -> { //使用花括号来将整套逻辑括起来
//...
yield "不及格"; //注意处理完成后使用yield关键字返回最终结果
}
};

文本块

Java 13引入文本块,通过三引号将字符串括起来,在其中不需要使用转义字符

public static void main(String[] args){
var str = """
weriodfghhj
fgghjkkkkwjdjwid
<h1>
<div class="a" id="b"></div>
""";
}

新instanceof语法

我们之前要重写一个类的equals方法:

public class Student {
private String name;

public Student(String name) {
this.name = name;
}

@Override
public boolean equals(Object obj) {
if(obj instanceof Student) {
Student student = (Student) obj;
return student.name.equals(this.name);
}
return false;
}
}

之前一直都是采用这种先判断类型,然后类型转换,最后才能使用的方式,但instanceof加强之后,可以直接将student替换为模式变量:

public class Student {
private final String name;

public Student(String name) {
this.name = name;
}

@Override
public boolean equals(Object obj) {
if(obj instanceof Student student) {
return student.name.equals(this.name);
}
return false;
}
}

Java 17新特性

密封类型

在Java中,可以通过继承(extends关键字)来实现类的能力复用、扩展与增强。但有的时候,可能并不是所有的类我们都希望能够被继承。所以,我们需要对继承关系有一些限制的控制手段,而密封类的作用就是限制类的继承

实际上在之前我们如果不希望别人继承我们的类,可以直接添加final关键字:public final class A{}

这样有一个缺点,如果添加了final关键字,那么无论是谁,包括我们自己也是没办法实现继承的,但是现在我们有一个需求,只允许我们自己写的类继承A,但是不允许别人写的类继承A,就可以使用密封类型来实现这个功能:

public sealed class A permits B{}   //在class关键字前添加sealed关键字,表示此类为密封类型,permits后面跟上允许继承的类型,多个子类使用逗号隔开

密封类型有以下要求:

  • 可以基于普通类、抽象类、接口,也可以是继承自其他接抽象类的子类或是实现其他接口的类等。
  • 必须有子类继承,且不能是匿名内部类或是lambda的形式。
  • sealed写在原来final的位置,但是不能和finalnon-sealed关键字同时出现,只能选择其一。
  • 继承的子类必须显式标记为finalsealed或是non-sealed类型。

标准的声明格式为:

public sealed [abstract] [class/interface] 类名 [extends 父类] [implements 接口, ...] permits [子类, ...]{
}

子类格式为:

public [final/sealed/non-sealed] class 子类 extends 父类 {   //必须继承自父类
//final类型:任何类不能再继承当前类
//sealed类型:同父类,需要指定由哪些类继承
//non-sealed类型:解封,重新开放为普通类,任何类都可以继承
}

可以通过反射来获取类是否为密封类型:

public static void main(String[] args) {
Class<A> a = A.class;
System.out.println(a.isSealed());
}