提示
本文主要讲解 Java 9 的新特性。@ermo
# Java 9 新特性
# 模块系统(Module System)
Java 9 最大的亮点就是新增 模块系统,模块系统是对 Java 项目组织结构的一次重要变革,可以让 Java 构建的项目更加安全可靠。
# 接口可以添加私有方法(Private Interface Methods)
Java 9 允许接口定义私有方法,包括普通的私有成员方法和静态类型的私有方法。
public interface HelloService {
default void sayDefault() {
sayPrivate();
}
private void sayPrivate() {
System.out.println("sayPrivate");
}
}
public class HelloServiceImpl implements HelloService {
public static void main(String[] args) {
HelloService helloService = new HelloServiceImpl();
helloService.sayDefault();
}
}
执行 main
方法后输出一下内容。
sayDefault
sayPrivate
sayStatic
# Try-With-Resource 增强
Try With Resource
是 Java 7 的新特性,用于解决 IO 操作结束后繁琐的流关闭代码,前提是流的声明必须在 try
的代码块中。
Java 9 对这一功能进行了增强,流的声明可以放到 try
的代码块外部,但是流不可被多次赋值,推荐在声明一个流变量时加上 final
关键字。
import java.io.FileOutputStream;
public class ResourcesDemo {
public static void main(String[] args) throws FileNotFoundException {
String data = "Some text.";
final FileOutputStream out = new FileOutputStream("ermo.txt");
try(out) {
out.write(data.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
}
}
上例执行 main
方法后会在项目的根目录生成一个 ermo.txt 文件,文件内容为。
Some text.
如果对流变量重复赋值,编译时会提示如下错误。
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
public class ResourcesDemo {
public static void main(String[] args) throws FileNotFoundException {
String data = "Some text.";
FileOutputStream out = new FileOutputStream("ermo.txt");
out = new FileOutputStream("ermocc.txt");
// 编译失败
// Variable used as a try-with-resources resource should be final or effectively final
try(out) {
out.write(data.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
}
}
# 匿名内部类增强
java 9 之前创建一个内部类,在创建一个泛型内部类时,后面的尖括号必须填写实际类型。
public abstract class AbstractPair<T> {
abstract T opt(T a, T b);
}
public class InnerClassExample {
public static void main(String[] args) {
// new AbstractPair<String>() 这里的 <> 内必须声明类型 String,否则编译不通过
AbstractPair<String> pair = new AbstractPair<String>() {
@Override
String opt(String a, String b) {
return a.concat(b);
}
};
System.out.println(pair.opt("ermo", ".cc"));
}
}
java 9 允许后面尖括号内无需填写实际类型值,编译器会自动推断实际的类型。
public class InnerClassExample {
public static void main(String[] args) {
// new AbstractPair<String> 可以省略 String 类型
AbstractPair<String> pair = new AbstractPair<>() {
@Override
String opt(String a, String b) {
return a.concat(b);
}
};
System.out.println(pair.opt("ermo", ".cc"));
}
}
输出
ermo.cc
# @SafeVarargs 增强
@SafeVarargs
注解是 Java 7 引入的一个注解,用于抑制在方法形参中定义了可变参数后编译器提示的警告。
在 Java 7 时期,@SafeVarargs
只能作用于构造器、final
修饰的成员方法、静态方法上。
Java 9 允许将 @SafeVarargs
注解添加到私有的成员方法上。
import java.util.List;
public class SafeVarExample {
// 编译器提示:Possible heap pollution from parameterized vararg type
// @SafeVarargs
public SafeVarExample(List<String>... names) {
}
// 编译器提示:Possible heap pollution from parameterized vararg type
// @SafeVarargs
public static void displayNameStatic(List<String>... names) {
}
// 编译器提示:Possible heap pollution from parameterized vararg type
// @SafeVarargs
public final void displayName(List<String>... names) {
}
}
上例中的三个方法在为添加 @SafeVarargs
注解时,编译器会提示一下内容,添加 @SafeVarargs
后警告内容就会消失。
Possible heap pollution from parameterized vararg type
在 Java 9 之前,如果将 @SafeVarargs
添加加到私有的成员方法上,编译会报错,并提示以下内容。
// Java 9 之前的版本,此处编译错误,并提示错误信息:@SafeVarargs is not allowed on non-final instance methods
@SafeVarargs
private void display(List<String>... names) {
for (List<String> name : names) {
System.out.println(name);
}
}
使用 Java 9 执行下例代码,可以成功编译通过。
import java.util.Arrays;
import java.util.List;
public class SafeVarExample {
@SafeVarargs
private void display(List<String>... names) {
for (List<String> name : names) {
System.out.println(name);
}
}
public static void main(String[] args) {
SafeVarExample safeVar = new SafeVarExample();
List<String> names = Arrays.asList("Java", "Python");
safeVar.display(names);
}
}
输出
[Java, Python]
# 集合工厂方法(Collection Factory Methods)
Java 9 引入了很多重载的集合工厂方法,用于创建不可变的集合实例。
public static void main(String[] args) {
// Java 9 之前的版本
List<String> names = Arrays.asList("Java", "Python", "C");
System.out.println(names);
Map<String, String> oldMap = new HashMap<>();
oldMap.put("K1", "V1");
oldMap.put("K2", "V2");
oldMap.put("K3", "V3");
Collections.unmodifiableMap(new HashMap<String, String>() {{
put("K1", "V1");
put("K2", "V2");
put("K2", "V2");
}});
// Java 9 之后的版本
List<String> languages = List.of("Java", "Python", "C");
Set<Integer> numbers = Set.of(1, 2, 3, 4, 5, 6, 6);
Map<String, String> pairMap = Map.of("k1", "V1", "K2", "V2");
List<String> emptyLit = List.of();
Set<String> emptySet = Set.of();
Map<String, String> emptyMap = Map.of();
}
# JShell-Java9 REPL
在 Java 9 引入 JShell,JShell 即 Java Shell,是一款 REPL(Read Eval Print Loop)交互解释器。
简单来说就是可以在 JShell 中输入一些简短的代码片段并立即获取到返回结果。比如 Chrome 浏览器开发者控制台和 Node 都提供了 REPL 这一功能。
# JShell 的优势
Java 属于编译类型的语言,开发完代码后需要进行编译,然后运行并验证结果,
如果是在开发和测试阶段,这个流程就比较冗长。比如开发人员想验证一个函数的语法、一行代码或者只想输出一个 hello world
类型的教学代码。
JShell 就是为解决这个问题,输入就可以立即获取到结果,免去编译工具、代码编辑工具这些不必要的因素。
# 环境变量
使用 JShell 前同样需要配置环境变量。
# 进入 JShell
$ jshell
| 欢迎使用 JShell -- 版本 19.0.1
| 要大致了解该版本, 请键入: /help intro
jshell>
# 退出 JShell
$ /exit
| 再见
# hello world
进入 jshell 后,可以输入各种代码片段,连分号 ;
也可以不用加。
$ System.out.println("Hello World")
Hello World
# 创建变量
$ int i = 10
i ==> 10
$ BigDecimal amount = BigDecimal.ZERO;
amount ==> 0
# 表达式
$ int x = 8;int y = 9;x+y
x ==> 8
y ==> 9
$6 ==> 17
$ (2+3)*2+1
$7 ==> 11
# 方法
JShell 同样可以定义方法,如果代码过长可以键入 //
,然后敲回车符进行换行。
$ public int add(int x,int y){return x+y;}
| 已创建 方法 add(int,int)
$ add(1,1)
$11 ==> 2
# 类
$ class Order{//
...> public void sum(int x,int y){//
...> System.out.println("Sum:" + (x+y));}}
| 已创建 类 Order
$ Order order = new Order();order.sum(1,1);
order ==> Order@1ddc4ec2
Sum:2
# 常用命令
命令 | 作用 |
---|---|
/list | 列出所有输入的记录 |
/vars | 列出所有声明的变量 |
/imports | 列出所有导入的包 |
/help | 帮助命令 |
# 压缩字符串(Compact String)
之前 Java 版本对于字符串 String
类存储使用的是 char[]
,Java 9 改成了 byte[]
字节类数组。
private final char value[]; // java 9 之前的版本
private final byte[] value; // java 9 版本