JDK9新特性
#一、模块化系统
##1、背景
Java 已经发展超过 20 年(95 年最初发布),Java 和相关生态在不断丰富的同时也越来越暴露出一些问题:
- Java 运行环境的膨胀和臃肿。每次JVM启动的时候,至少会有30~60MB的内存加载,主要原因是JVM需要加载rt.jar,不管其中的类是否被classloader加载,第一步整个jar都会被JVM加载到内存当中去(而模块化可以根据模块的需要加载程序运行需要的class)
- 当代码库越来越大,创建复杂,盘根错节的“意大利面条式代码”的几率呈指数级的增长。不同版本的类库交叉依赖导致让人头疼的问题,这些都阻碍了 Java 开发和运行效率的提升。
- 很难真正地对代码进行封装, 而系统并没有对不同部分(也就是 JAR 文件)之间的依赖关系有个明确的概念。每一个公共类都可以被类路径之下任何其它的公共类所访问到,这样就会导致无意中使用了并不想被公开访问的 API。
- 类路径本身也存在问题: 你怎么知晓所有需要的 JAR 都已经有了, 或者是不是会有重复的项呢?
本质上讲,模块(module)的概念,其实就是package外再裹一层,也就是说,用模块来管理各个package,通过声明某个package暴露,不声明默认就是隐藏。因此,模块化使得代码组织上更安全,因为它可以指定哪些部分可以暴露,哪些部分隐藏。
##2、设计理念
模块独立、化繁为简
模块化(以 Java 平台模块系统的形式)将 JDK 分成一组模块,可以在编译时,运行时或者构建时进行组合
##3、实现目标
- 主要目的在于减少内存的开销
- 只须必要模块,而非全部jdk模块,可简化各种类库和大型应用的开发和维护
- 改进 Java SE 平台,使其可以适应不同大小的计算设备
- 改进其安全性,可维护性,提高性能
4、示例
4.1、创建maven工程
创建一个名为java9和java9test的子工程
4.2、添加model类:Person
/**
* @Description: Person
* @Author: vesus
* @CreateDate: 2019/1/9 下午2:23
* @Version: 1.0
*/
public class Person {
private static final Logger logger = Logger.getLogger("Person");
private String name ;
private int age ;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
###4.3、在java下创建module-info.java文件,通过exports将对应路径下的类暴露出去。
/**
* @Description: module java9
* @Author: vesus
* @CreateDate: 2019/1/9 下午2:27
* @Version: 1.0
*/
module java9 {
exports com.vesus.model ;
requires java.logging;
}
4.4、java9test添加java9的依赖
<dependencies>
<dependency>
<groupId>com.vesus</groupId>
<artifactId>java9</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
4.5、在java9test创建测试类
/**
* @Description: 测试类
* @Author: vesus
* @CreateDate: 2019/1/9 下午2:24
* @Version: 1.0
*/
public class PersonTest {
public static void main(String[] args) {
Person person = new Person("vesus",18);
System.out.println(person);
}
}
4.6、在java下创建module-info.java文件,通过requires将对应类引入进来。
/**
* @Description: module java9test
* @Author: vesus
* @CreateDate: 2019/1/9 下午2:25
* @Version: 1.0
*/
module java9test {
requires java9 ;
}
#二、jShell命令
1、进入jshell
wndows中输入cmd进入命令行,再输入jshell命令进入jshell,mac的终端输入jshell命令进入jshell
2、基本使用
#三、多版本兼容jar包
##1、在java文件夹下创建Generator类
public class Generator {
public String toString() {
return "hello java 8!";
}
}
2、在java文件夹下创建App类
public class App {
public static void main(String[] args) {
Generator generator = new Generator();
System.out.println(generator.toString());
}
}
3、在java9文件加下面创建Generator类
public class Generator {
public String toString() {
return "hello java 9!";
}
}
4、打包编译
javac -d build --release 8 src/main/java/com/vesus/*.java
javac -d build9 --release 9 src/main/java9/com/vesus/*.java
jar --create --main-class=App --file app.jar -C build . --release 9 -C build9 .
##5、执行
java -cp app.jar com.vesus.App
执行结果:
hello java 9!
#四、接口的私有方法
##1、简介
Java 8中规定接口中的方法除了抽象方法之外,还可以定义静态方法和默认的方法。一定程度上,扩展了接口的功能,此时的接口更像是一个抽象类。
在Java 9中,接口更加的灵活和强大,连方法的访问权限修饰符都可以声明为private的了,此时方法将不会成为你对外暴露的API的一部分。
2、示例
###2.1、声明接口
/**
* @Description: 声明带有private方法的接口
* @Author: vesus
* @CreateDate: 2019/1/9 下午5:33
* @Version: 1.0
*/
public interface MyInterface {
void sayHello();
default String hello(){
init();
return "hello" ;
}
static String world (){
return "world" ;
}
private void init(){
System.out.println("--------init------------");
}
}
###2.2、实现MyInterface接口
public class HelloWorld implements MyInterface {
@Override
public void sayHello() {
hello();
System.out.println("hello world");
}
}
#五、钻石操作符的使用升级
我们将能够与匿名实现类共同使用钻石操作符(diamond operator)
在java 8中如下的操作是会报错的:
public void getValue(){
Map<String,String> map = new HashMap<>(){};
}
编译报错信息:’<>’ cannot be used with anonymous classes
Java 9中使用
public void getValue(){
Map<String,String> map = new HashMap<>(){
@Override
public boolean isEmpty() {
return true;
}
};
}
#六、语法改进:try语句
在java 8 之前,我们习惯于这样处理资源的关闭:
public void test1(){
InputStreamReader reader = null ;
try {
reader = new InputStreamReader(System.in);
reader.read();
}catch (Exception e){
e.printStackTrace();
}finally {
if (reader!=null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
java 8 中,可以实现资源的自动关闭,但是要求执行后必须关闭的所有资源必须在try子句中初始化,否则编译不通过。
public void test2(){
try (InputStreamReader reader = new InputStreamReader(System.in)){
reader.read();
}catch (Exception e){
e.printStackTrace();
}
}
java 9 中,用资源语句编写try将更容易,我们可以在try子句中使用已经初始化过的资源,此时的资源是final的
public void test3(){
InputStreamReader reader = new InputStreamReader(System.in) ;
try (reader){
reader.read();
}catch (Exception e){
e.printStackTrace();
}
}
#七、下划线使用限制
在java 8 中,标识符可以独立使用“_”来命名
String _ = "hello";
System.out.println(_);
但是,在java 9 中规定“_”不再可以单独命名标识符了
#八、String存储结构变更
String 再也不用 char[] 来存储啦,改成了 byte[] 加上编码标记,节约了一些空间。
private final byte[] value;
#九、便利的集合特性:of()
Java 9增加了List.of()、Set.of()、Map.of()和Map.ofEntries()等工厂方法来创建不可变集合
List strs = List.of("Hello", "World");
List strs List.of(1, 2, 3);
Set strs = Set.of("Hello", "World");
Set ints = Set.of(1, 2, 3);
Map maps = Map.of("Hello", 1, "World", 2);
#十、增强的Stream API
Stream 接口中添加了 4 个新的方法:dropWhile, takeWhile, ofNullable。还有个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代
List<String> strings = List.of("abc", "bc", "efg", "gg", "abcd", "jkl");
//输出集合元素直到遇到不符合条件的字符串
strings.stream().takeWhile(s -> !s.equals("gg")).forEach(System.out::println);
System.out.println();
//从不符合条件的字符串开始输出集合元素
strings.stream().dropWhile(s -> !s.equals("gg")).forEach(System.out::println);
//指定初始值创建顺序流,设置停止条件与每次迭代的操作
IntStream.iterate(0, x -> x <= 10, x -> x + 2).forEach(System.out::println);
#十一、全新的HTTP客户端API
HTTP/1.1和HTTP/2的主要区别是如何在客户端和服务器之间构建和传输数据。HTTP/1.1 依赖于请求/响应周期。 HTTP/2 允许服务器“push”数据:它可以发送比客户端请求更多的数据。 这使得它可以优先处理并发送对于首先加载网页至关重要的数据。
JDK9 中有新的方式来处理 HTTP 调用。它提供了一个新的HTTP客户端(HttpClient),它将替代仅适用于blocking模式的HttpURLConnection(HttpURLConnection是在HTTP 1.0的时代创建的,并使用了协议无关的方法),并提供对 WebSocket 和 HTTP/2 的支持。
此外,HTTP客户端还提供 API 来处理 HTTP/2 的特性,比如流和服务器推送等功能。
全新的 HTTP 客户端 API 可以从 jdk.incubator.httpclient 模块中获取。因为在默认情况下,这个模块是不能根据 classpath 获取的,需要使用 add modules 命令选项配置这个模块,将这个模块添加到 classpath中。
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://www.baidu.com/"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandler.asString());
System.out.println(response.statusCode());
System.out.println(response.body());