注: IntelliJ IDEA 2017.3 支持模块化特性,这里选择此开发环境。
经过4次跳票,历经曲折的java 9 终于终于在2017年9月21日发布。
java 9 提供了超过 150 项新功能特性,包括备受期待的模块化系统、 可交互的 REPL 工具:jshell,JDK 编译工具,Java 公共 API 和私有代码,以及安全增强、扩展提升、性能管理改善等。可以说 Java 9 是 一个庞大的系统工程,完全做了一个整体改变。
具体来讲:
- 模块化系统
- jShell 命令
- 多版本兼容 jar 包
- 接口的私有方法
- 钻石操作符的使用升级
- 语法改进:try 语句
- 下划线使用限制
- String 存储结构变更
- 便利的集合特性:of()
- 增强的 Stream API
- 多分辨率图像 API
- 全新的 HTTP 客户端 API
- Deprecated 的相关 API
- 智能 Java 编译工具
- 统一的 JVM 日志系统
- javadoc 的 HTML 5 支持
- Javascript 引擎升级:Nashorn
- java 的动态编译器
Java 更快的发布周期意味着开发者将不需要像以前一样为主要发布版本望眼欲穿。这也意味着开发者将可能跳过 Java 9 和它的不成熟的模块化功能,只需要再等待 6 个月就可以迎来新版本,这将可能解决开发者的纠结。
oracle 理念 与 小步快跑,快速迭代
https://www.oracle.com/technetwork/java/javase/downloads/jdk9-downloads-3848520.html
下载安装完毕,需要配置环境变量:
① 新建 JAVA_HOME 的环境变量,变量值为 jdk 路径。如下:
② 将 JAVA_HOME 配置到 path 环境变量下:
之后在命令行校验是否配置成功。成功,则显示如下:
http://www.oracle.com/technetwork/java/javase/documentation/jdk9-d oc-downloads-3850606.html
https://docs.oracle.com/javase/9/
官方提供的新特性列表:
https://docs.oracle.com/javase/9/whatsnew/toc.htm#JSNEW-GUID-C23 AFD78-C777-460B-8ACE-58BE5EA681F6
或参考 Open JDK: https://openjdk.java.net/projects/jdk9/
http://openjdk.java.net/jeps/0
小组:对特定技术内容,比如安全、网络、HotSpot 等有共同 兴趣的组织和个人
项目:编写代码、文档以及其他工作,至少由一个小组赞助和 支持,比如最近的 Lambda 计划,JigSaw 计划等
JDK : Java Development Kit (Java 开发工具包)
JRE : Java Runtime Environment (Java 运行环境)
说明:
JDK = JRE + 开发工具集(例如 Javac 编译工具等)
JRE = JVM + Java SE 标准类库
说明:
bin 目录 | 包含命令行开发和调试工具,如 javac ,jar 和 javadoc。 |
include目录 | 包含在编译本地代码时使用的C/C++头文件 |
lib 目录 | 包含JDK工具的几个JAR和其他类型的文件。它有一个tools.jar 文件,其中包含 Javac 编译器的 Java 类 |
jre/bin 日录 | 包含基本命令,如 java 命令。在 Windows平台上,它包含系统的运行时动态链接库(DLL)。 |
jre/lib 目录 | 包含用户可编辑的配置文件,如 .properties和 .policy文件。包含几个JAR ,rt.jar文件包含运行时的 Java 类和资源文件。 |
没有名为 jre 的子目录 | |
bin 目录 | 包含所有命令。 在 Windows 平台上,它继续包含系 统的运行时动态链接库。 |
conf 目录 | 包含用户可编辑的配置文件,例如以前位于 jre\lib 目 录中的.properties 和.policy 文件 |
include 目录 | 包含要在以前编译本地代码时使用的 C/C++头文件。 它只存在于 JDK 中 |
jmods 目录 | 包含 JMOD 格式的平台模块。 创建自定义运行时映像 时需要它。 它只存在于 JDK 中 |
legal 目录 | 包含法律声明 |
lib 目录 | 包含非 Windows 平台上的动态链接本地库。 其子目 录和文件不应由开发人员直接编辑或使用 |
200: The Modular JDK
201: Modular Source Code
220: Modular Run-Time Images
260: Encapsulate Most Internal APIs
261: Module System
282: jlink: The Java Linker
>> Java 运行环境的膨胀和臃肿。每次JVM启动的时候,至少会有 30~60MB的内存加载,主要原因是JVM需要加载rt.jar,不管 其中的类是否被 classloader 加载,第一步整个jar都会被JVM加载到内存当中去(而模块化可以根据模块的需要加载程序运行需要的class)
>> 当代码库越来越大,创建复杂,盘根错节的“意大利面条式代 码”的几率呈指数级的增长。不同版本的类库交叉依赖导致让 人头疼的问题,这些都阻碍了 Java 开发和运行效率的提升。
>> 很难真正地对代码进行封装, 而系统并没有对不同部分(也就 是 JAR 文件)之间的依赖关系有个明确的概念。每一个公共类都可以被类路径之下任何其它的公共类所访问到,这样就会导致无意中使用了并不想被公开访问的 API。
>> 类路径本身也存在问题: 你怎么知晓所有需要的 JAR 都已经有了, 或者是不是会有重复的项呢?
模块独立、化繁为简
模块化(以 Java 平台模块系统的形式)将 JDK 分成一组模块,可 以在编译时,运行时或者构建时进行组合。
模块将由通常的类和新的模块声明文件(module-info.java)组成。 该文件是位于 java 代码结构的顶层,该模块描述符明确地定义了我们的模块需要什么依赖关系,以及哪些模块被外部使用。在 exports 子 句中未提及的所有包默认情况下将封装在模块中,不能在外部使用。
java 9demo 模块中的 ModuleTest 类使用如下:
package com.atguigu.java;
import com.atguigu.bean.Person;
import org.junit.Test;
import java.util.logging.Logger;
public class ModuleTest {
private static final Logger LOGGER = Logger.getLogger("java9test");
public static void main(String[] args){
Person p = new Person("马云",40);
System.out.println(p);
LOGGER.info("测试");
// User u = new User(); 无法访问
}
@Test
public void test1(){
System.out.println("hello");
}
}
对应在 java 9demo 模块的 src 下创建 module-info.java 文件:
注:没找到 module-info.java 选项,可以在桌面创建一个 module-info.java 文件在复制到 idea 中。在idea中创建 java 文件是不可以取这个名字的。(命名不符合)
module java9demo{
requires java9test;
requires java.logging;
requires junit;
}
requires:指明对其它模块的依赖。
在 java9test 模块的指定包下提供类 Person:
package com.atguigu.bean;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
要想在 java9demo 模块中调用 java9test 模块下包中的结构,需要在java9test 的 module-info.java 中声明:
module java9test{
//导出我们想要导出的包
exports com.atguigu.bean;
}
exports:控制着哪些包可以被其它模块访问到。所有不被导出的包 默认都被封装在模块里面。
在 ModuleTest 就访问不了 User
package com.atguigu.entity;
public class User {
private String name;
private int age;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", 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;
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public User() {
}
}
关于更多 Java 9 模块编程的内容请参考一本书:《Java 9 Modularity》 里面讲的比较详细,介绍了当前 Java 对 jar 之间以来的管理是多么的 混乱,引入 modularity 之后的改变会是很明显的差别。
222: jshell: The Java Shell (Read-Eval-Print Loop)
像 Python 和 Scala 之类的语言早就有交互式编程环境 REPL (read - evaluate - print - loop)了,以交互式的方式对语句和表达式进行求值。 开发者只需要输入一些代码,就可以在编译前获得对程序的反馈。而之前的 Java 版本要想执行代码,必须创建文件、声明类、提供测试 方法方可实现。
即写即得、快速运行
jshell> System.out.println("你好!world");
你好!worldjshell> int i = 10;
i ==> 10jshell> int j = 20;
j ==> 20jshell> int k = i + j;
k ==> 30jshell> System.out.print(k);
30
jshell> public int add(int m, int n){
...> return m+n;
...> }
| 已创建 方法 add(int,int)jshell> int k = add(1,2);
k ==> 3jshell> System.out.print(k);
3
Tips:在 JShell 环境下,语句末尾的“;” 是可选的。但推荐还是最好加上。提 高代码可读性。
jshell> import java.util.*;
默认已经导入如下的所有包:(包含 java.lang 包)
jshell> /imports
| import java.io.*
| import java.math.*
| import java.net.*
| import java.nio.file.*
| import java.util.concurrent.*
| import java.util.function.*
| import java.util.prefs.*
| import java.util.regex.*
| import java.util.stream.*
| import java.util.*jshell>
jshell> Sy
SyncFailedException SynchronousQueue Systemjshell> System.out
outjshell> System.out.
append( checkError() close() equals( flush()
format( getClass() hashCode() notify() notifyAll()
print( printf( println( toString() wait(
write(jshell> System.out.
Tips:我们还可以重新定义相同方法名和参数列表的方法,即为对现有方法的修 改(或覆盖)。
从外部文件加载源代码
F盘创建一个HelloWorld.java 文件:
//注意这个和以前写的java 程序还是有点差别的
public void printHello(){
System.out.println("HelloWorld");
}
printHello();
使用/open 命令调用:
jshell> /open F:\HelloWorld.java
HelloWorld
jshell> URL url = new URL("https://music.163.com");
url ==> https://music.163.com
说明:本来应该强迫我们捕获一个 IOException,但却没有出现。因为 jShell 在后台为我们隐藏了。
//java 程序里面 要么抛出 要么 try
@Test
public void test(){
try {
URL url = new URL("https://music.163.com");
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
jshell> /exit
| 再见
jshell> /
/! /? /drop /edit /env /exit
/help /history /imports /list /methods /open
/reload /reset /save /set /types /vars
238: Multi-Release JAR Files
当一个新版本的 Java 出现的时候,你的库用户要花费数年时间才会切换到这个新的版本。这就意味着库得去向后兼容你想要支持的最老的 Java 版本(许多情况下就是 Java 6 或者 Java7)。这实际上意味着 未来的很长一段时间,你都不能在库中运用 Java 9 所提供的新特性。 幸运的是,多版本兼容 jar 功能能让你创建仅在特定版本的 Java 环境 中运行库程序选择使用的 class 版本。
举例 1:
jar root
- A.class
- B.class
- C.class
- D.class
- META-INF
- versions
- 9
- A.class
- B.class
说明:
在上述场景中, root.jar 可以在 Java 9 中使用, 不过 A 或 B 类使用的不是顶层的 root.A 或 root.B 这两个 class, 而是处在 “META-INF/versions/9”下面的这两个。这是特别为 Java 9 准备的 class 版本,可以运用 Java 9 所提供的特性和库。同时,在早期的 Java 诸版本中使用这个 JAR 也是能运行的,因为较老版本的 Java 只会看 到顶层的 A 类或 B 类。
举例 2:
jar root
- A.class
- B.class
- C.class
- D.class
- META-INF
- versions
- 9
- A.class
- B.class
-10
- A.class
- B.class
官方说明:
By this scheme, it is possible for versions of a class designed for a later Java platform release to override the version of that same class designed for an earlier Java platform release.
通过这样的一个规定,这是可能的,我们最近的一个 Java 稳定版的平台,它会取覆盖我们之前 Java平台中(jdk)中同名的类,说白了就是一个完全覆盖的关系。
文件的目录结构:
multijar
└─src
└─main
├─java
│ └─com
│ └─atguigu
│ Application.java
│ Generator.java
│
└─java-9
└─com
└─atguigu
Generator.java
在指定目录(...\multijar\src\main\java\com\atguigu)下创建 Generator.java
package com.atguigu;
import java.util.Set;
import java.util.HashSet;
/**
* Created by songhongkang on 2017/12/28 0028.
*/
public class Generator {
public Set createStrings() {
Set strings = new HashSet();
strings.add("Java");
strings.add("8");
return strings;
}
}
创建 Application.java
package com.atguigu;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
/**
* Created by songhongkang on 2017/12/28 0028.
*/
public class Application {
public static void testMultiJar(){
Generator gen = new Generator();
System.out.println("Generated strings: " + gen.createStrings());
}
}
在如下目录(....\multijar\src\main\java-9\com\atguigu) 下提供同名的类: Generator.java
package com.atguigu;
import java.util.Set;
/**
* Created by songhongkang on 2017/12/28 0028.
*/
public class Generator {
public Set createStrings() {
return Set.of("Java", "9");//java9 新增加的
}
}
指令如下:
javac -d build --release 8 src/main/java/com/atguigu/*.java |
javac -d build9 --release 9 src/main/java-9/com/atguigu/*.java |
jar --create --main-class=Application --file multijar.jar -C build . |
进入 multijar 目录里面
java8环境:
public class TestDemo {
public static void main(String[] args){
Application.testMultiJar();
}
}
只需要打开项目结构(Ctrl+Alt+Shift+S),修改模块即可切换到java9
java9:
213: Milling Project Coin
Support for private methods in interfaces was briefly in consideration for inclusion in Java SE 8 as part of the effort to add support for Lambda Expressions, but was withdrawn to enable better focus on higher priority tasks for Java SE 8. It is now proposed that support for private interface methods be undertaken thereby enabling non abstract methods of an interface to share code between them.
在接口中对私有方法的支持被简单地考虑为包含在Java SE 8中,作为增加对lambda表达式的支持的一部分,但是被撤回以更好地关注Java SE 8的更高优先级的任务。现在建议对私有接口方法进行支持,从而使接口的非抽象方法能够在它们之间共享代码。
Java 8 中规定接口中的方法除了抽象方法之外,还可以定义静态 方法和默认的方法。一定程度上,扩展了接口的功能,此时的接口更 像是一个抽象类。
在 Java 9 中,接口更加的灵活和强大,连方法的访问权限修饰符 都可以声明为 private 的了,此时方法将不会成为你对外暴露的 API 的一部分。
//Java8之前,接口中只支持常量和抽象接口:
public interface TestInterface{
void test();
abstract void test2();
}
//接口中的方法默认是abstract,可写可不写。
//从Java8开始支持default和static方法:
public interface TestInterface2{
default void test3(){
System.out.println("this is a default method");
}
static void test4(){
System.out.println("this is a static method");
}
}
//default方法可以被重写,而static方法不能被重写。
//具体要不要重写default方法,可以根据个人需求来。
//Java 9中除了支持default和static方法,还多了另外两个——private 和private static:
public interface PrivateInterface {
void test();
abstract void test2();
default void test3() {
System.out.println("default method");
test5();
}
static void test4() {
System.out.println("static method");
test6();
}
private void test5() {
System.out.println("private method");
test6();
}
private static void test6() {
System.out.println("private static method");
}
}
//private和private static方法都不能override,出现它们是为了提高代码利用率,更美观。
//区别就是private方法是为default方法服务的,private static 则是为static方法服务的。
//default方法可以调用abstract/private/static/private static方法。
//而static方法只能调用static/private static方法。
/**
* Created by songhongkang on 2017/12/29 0029.
* 1.
* 类:人
* 接口:兽 --->半兽人
*
* 2. 面试题:抽象类 和 接口的异同?
* ①二者的定义: a.声明的方式 b.内部的结构(jdk 7 ;jdk 8 ; jdk 9)
* ②共同点:不能实例化;以多态的方式使用
* ③不同点:单继承 ; 多实现
*/
interface MyInterface{
//jdk 7 : 只能声明全局常量(public static final)和抽象方法(public abstract)
abstract void method1();
// jdk 8 : 增加了 声明静态方法 和 默认方法
public static void method2(){
System.out.println("method2");
}
default void method3(){
System.out.println("method3");
}
//前面的方法 虽然没有显示加上 public修饰符,但是他们默认是有的 全都是公共属性对外暴露
//jdk9 增加了 方法可以私有化
// jdk 9 : 声明私有方法
private void method4(){
System.out.println("私有方法");
}
}
public class MyInterfaceImpl implements MyInterface {
//这个必须实现
@Override
public void method1() {
System.out.println("实现接口中的抽象方法 method1()");
}
public static void main(String[] args){
MyInterfaceImpl imp = new MyInterfaceImpl();
imp.method1();//调用抽象方法
MyInterface.method2();//调用静态方法 静态方法不可以通过对象来调用了
imp.method3();//调用默认方法
// imp.method4(); 接口的私有方法 不可以调用
}
}
实现接口中的抽象方法 method1()
method2
method3
我们将能够与匿名实现类共同使用钻石操作符(diamond operator) 在 java 8 中如下的操作是会报错的:
在 jdk1.8 的情况下是不可以的:
编译报错信息:'<>' cannot be used with anonymous classes
jdk1.9:
import org.junit.Test;
import java.util.HashSet;
import java.util.Set;
public class DiamondOperatorTest {
@Test
public void testDiamondOperator(){
diamondOperator();
}
public void diamondOperator(){
//创建一个继承于HashSet的匿名子类的对象
Set set = new HashSet<>(){};//编译通过
set.add("MM");
set.add("JJ");
set.add("GG");
set.add("DD");
for(String s : set){
System.out.println(s);
}
}
}
在 java 8 之前,我们习惯于这样处理资源的关闭:
//举例1:
@Test
public void testTry1(){
InputStreamReader reader = null;
try{
reader = new InputStreamReader(System.in);
//读取数据的过程:略
reader.read();
}catch (IOException e){
e.printStackTrace();
}finally{
//资源的关闭操作
if(reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
java 8 中,可以实现资源的自动关闭,但是要求执行后必须关闭的所 有资源必须在 try 子句中初始化,否则编译不通过。如下例所示:
//举例2:在举例1基础上,升级的写法。不用显式的处理资源的关闭
//java 8 中:要求资源对象的实例化,必须放在try的一对()内完成。
@Test
public void testTry2(){
try(InputStreamReader reader = new InputStreamReader(System.in)){
//读取数据的过程:略
reader.read();
}catch(IOException e){
e.printStackTrace();
}
}
java 9 中,用资源语句编写 try 将更容易,我们可以在 try 子句中使用 已经初始化过的资源,此时的资源是 final 的:
//如下的操作不可以在jdk 8 及之前的版本中使用
//java 9 中:可以在try()中调用已经实例化的资源对象
@Test
public void testTry3(){
InputStreamReader reader = new InputStreamReader(System.in);
OutputStreamWriter writer = new OutputStreamWriter(System.out);
try(reader;writer){//多个资源用 ; 隔开即可
//此时的reader是final的,不可再被赋值
// reader = null;
//读取数据的过程:略
reader.read();
}catch(IOException e){
e.printStackTrace();
}
}
在 java 8 中,标识符可以独立使用“_”来命名:
//在 java 8 中,
// 标识符可以独立使用“_”来命名:
@Test
public void test(){
String _ = "hello";
System.out.println(_);
}
但是,在 java 9 中规定“_”不再可以单独命名标识符了,如果使用, 会报错:
JEP 254: Compact Strings
Motivation
The current implementation of the String class stores characters in a char array, using two bytes (sixteen bits) for each character. Data gathered from many different applications indicates that strings are a major component of heap usage and, moreover, that most String objects contain only Latin-1 characters. Such characters require only one byte of storage, hence half of the space in the internal char arrays of such String objects is going unused.
动机
string类的当前实现将字符存储在char数组中,每个字符使用两个字节(16位)。从许多不同的应用程序收集的数据表明,字符串是堆使用的主要组成部分,而且,大多数字符串对象只包含拉丁-1字符。这样的字符只需要一个字节的存储空间,因此这样的字符串对象的内部字符数组中的一半空间将被闲置。
Description
We propose to change the internal representation of the String class from a UTF-16 char array to a byte array plus an encoding-flag field. The new String class will store characters encoded either as ISO-8859-1/Latin-1 (one byte per character), or as UTF-16 (two bytes per character), based upon the contents of the string. The encoding flag will indicate which encoding is used.
描述
我们建议将字符串类的内部表示形式从UTF-16字符数组更改为字节数组加上编码标志字段。新的字符串类将根据字符串的内容存储编码为ISO-8859-1/Latin-1(每个字符一个字节)或UTF-16(每个字符两个字节)的字符。编码标志将指示使用哪种编码。
结论:String 再也不用 char[] 来存储啦,改成了 byte[] 加上编码标 记,节约了一些空间。
那 StringBuffer 和 StringBuilder 是否仍无动于衷呢?
String-related classes such as AbstractStringBuilder, StringBuilder, and StringBuffer will be updated to use the same representation, as will the HotSpot VM's intrinsic string operations.
与字符串相关的类(如abstractStringBuilder、StringBuilder和StringBuffer)将更新为使用相同的表示,Hotspot VM的内部字符串操作也将更新。
他们也是一样的变化的
String:jdk 8 及之前:底层使用char[]存储;jdk 9 : 底层使用byte[] (encoding flag) StringBuffer:jdk 8 及之前:底层使用char[]存储;jdk 9 : 底层使用byte[] StringBuilder:jdk 8 及之前:底层使用char[]存储;jdk 9 : 底层使用byte[] String:不可变的字符序列; StringBuffer:可变的字符序列;线程安全的,效率低; StringBuilder:可变的字符序列;线程不安全的,效率高(jdk 5.0)
269: Convenience Factory Methods for Collections
要创建一个只读、不可改变的集合,必须构造和分配它,然后添加元 素,最后包装成一个不可修改的集合。
比如:
//jdk 8 以及之前:创建一个只读特点的集合
@Test
public void test1(){
List list = new ArrayList<>();
list.add("Tom");
list.add("Jerry");
list.add("Lilei");
list.add("HanMeimei");
//调用Collections中的方法,将list变为只读的
List newList = Collections.unmodifiableList(list);
// newList.add("Tim");//不能执行,否则报异常
//遍历:jdk 8
newList.forEach(System.out::println);
}
@Test
public void test(){
List list = Arrays.asList(1, 2, 3); //创建的是一个只读的集合
list.add(4);
}
缺点:我们一下写了五行。即:它不能表达为单个表达式。
当然,我们也可以稍微简单点处理:
//jdk 8 以及之前:创建一个只读特点的集合
@Test
public void test2(){
//List:
List list = Collections.unmodifiableList(Arrays.asList(1, 2, 3));
// list.add(4);
//Set:
Set set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(1, 2, 3)));
// set.add(4);
// set.forEach(System.out::println);
//Map:
//如下操作不适用于jk8及之前版本,适用于jdk9
Map
Java 9 因此引入了方便的方法,这使得类似的事情更容易表达。
package java.util;
import java.util.function.UnaryOperator;
public interface List extends Collection {
//------------省略--------
static List of() {
return ImmutableCollections.List0.instance();
}
static List of(E e1) {
return new ImmutableCollections.List1<>(e1);
}
static List of(E e1, E e2) {
return new ImmutableCollections.List2<>(e1, e2);
}
static List of(E e1, E e2, E e3) {
return new ImmutableCollections.ListN<>(e1, e2, e3);
}
static List of(E e1, E e2, E e3, E e4) {
return new ImmutableCollections.ListN<>(e1, e2, e3, e4);
}
static List of(E e1, E e2, E e3, E e4, E e5) {
return new ImmutableCollections.ListN<>(e1, e2, e3, e4, e5);
}
static List of(E e1, E e2, E e3, E e4, E e5, E e6) {
return new ImmutableCollections.ListN<>(e1, e2, e3, e4, e5,
e6);
}
static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) {
return new ImmutableCollections.ListN<>(e1, e2, e3, e4, e5,
e6, e7);
}
static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) {
return new ImmutableCollections.ListN<>(e1, e2, e3, e4, e5,
e6, e7, e8);
}
static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) {
return new ImmutableCollections.ListN<>(e1, e2, e3, e4, e5,
e6, e7, e8, e9);
}
static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) {
return new ImmutableCollections.ListN<>(e1, e2, e3, e4, e5,
e6, e7, e8, e9, e10);
}
}
List firsnamesList = List.of(“Joe”,”Bob”,”Bill”);
调用集合中静态方法 of(),可以将不同数量的参数传输到此工厂方法中。此功能可用于 Set 和 List,也可用于 Map 的类似形式。此时得到 的集合,是不可变的:在创建后,继续添加元素到这些集合会导致 “UnsupportedOperationException”
由于 Java 8 中接口方法的实现,可以直接在 List,Set 和 Map 的接口 内定义这些方法,便于调用。
//jdk 9 中:创建一个只读特点的集合
@Test
public void test3(){
//List:
List list = List.of(1, 2, 3);
// list.add(4);
list.forEach(System.out::println);
//Set:
Set set = Set.of(2, 3, 4);
// set.add(6);
//Map:
//创建只读集合的方式一:
Map map = Map.of("Tom", 23, "Jerry", 22, "Lilei", 12, "HanMeimei", 18);
// map.put("Tim",33);
//创建只读集合的方式二:
Map map1 = Map.ofEntries(Map.entry("Tom", 23), Map.entry("Jerry", 21));
// map1.put("Tim",33);
System.out.println(map1);
}
Java 的 Steam API 是java标准库最好的改进之一,让开发者能够 快速运算,从而能够有效的利用数据并行计算。Java 8 提供的 Steam 能够利用多核架构实现声明式的数据处理。
在 Java 9 中,Stream API 变得更好,Stream 接口中添加了 4 个 新的方法:dropWhile, takeWhile, ofNullable,还有个 iterate 方法的 新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时 候结束迭代。(见下例)
除了对 Stream 本身的扩展,Optional 和 Stream 之间的结合也 得到了改进。现在可以通过 Optional 的新方法 stream() 将一个 Optional 对象转换为一个(可能是空的) Stream 对象。(见下例)
用于从 Stream 中获取一部分数据,接收一个 Predicate 来进行选择。在有序的 Stream 中,takeWhile 返回从开头开始的尽量多的元素。
//1.takeWhile()
@Test
public void test1(){
List list = Arrays.asList(45,56,33,77,44,98,76,78,33);
Stream stream = list.stream();
stream.takeWhile(x -> x < 70).forEach((x)->{System.out.print(x+" ");});
System.out.println("\n--------------------------");
List list1 = Arrays.asList(1,2,3,4,5,6,7,8);
list1.stream().takeWhile(x -> x < 5).forEach((x)->{System.out.print(x+" ");});
}
输出:
45 56 33
--------------------------
1 2 3 4
注意:这个和 filter 是不同的,他是从开头找,凡是有一个不满足就结束了。
dropWhile 的行为与 takeWhile 相反,返回剩余的元素。
//2.dropWhile():与 takeWhile() 整好相反
@Test
public void test2(){
List list = Arrays.asList(45,56,33,77,44,98,76,78,33);
Stream stream = list.stream();
stream.dropWhile(x -> x < 70).forEach((x)->{System.out.print(x+" ");});
System.out.println("\n--------------------------");
List list1 = Arrays.asList(1,2,3,4,5,6,7,8);
list1.stream().dropWhile(x -> x < 5).forEach((x)->{System.out.print(x+" ");});
}
77 44 98 76 78 33
--------------------------
5 6 7 8
takeWhile() + dropWhile() 就是一个完整的流
Java 8 中 Stream 不能完全为 null(一个元素不能为 null 多个元素是可以存在 null ),否则会报空指针异常。而 Java 9 中的 ofNullable 方法允许我们创建一个单元素 Stream,可以包含一个非空元素,也可 以创建一个空 Stream。
//3.ofNullable(T t): t可以为null
@Test
public void test3(){
// 不报异常,允许通过
Stream stream1 = Stream.of(1, 2, 3, null);
stream1.forEach(System.out::println);
// 不报异常,允许通过
List list = new ArrayList<>();
list.add("AA");
list.add(null);
System.out.println(list.stream().count());//2
System.out.println("------------------------------");
//如果只有单个元素,此元素不能为null.否则报NullPointerException
// Stream
/**
* Returns a sequential ordered {@code Stream} produced by iterative
* application of the given {@code next} function to an initial element,
* conditioned on satisfying the given {@code hasNext} predicate. The
* stream terminates as soon as the {@code hasNext} predicate returns false.
*
* {@code Stream.iterate} should produce the same sequence of elements as
* produced by the corresponding for-loop:
*
{@code
* for (T index=seed; hasNext.test(index); index = next.apply(index)) {
* ...
* }
* }
*
* The resulting sequence may be empty if the {@code hasNext} predicate
* does not hold on the seed value. Otherwise the first element will be the
* supplied {@code seed} value, the next element (if present) will be the
* result of applying the {@code next} function to the {@code seed} value,
* and so on iteratively until the {@code hasNext} predicate indicates that
* the stream should terminate.
*
*
The action of applying the {@code hasNext} predicate to an element
* happens-before
* the action of applying the {@code next} function to that element. The
* action of applying the {@code next} function for one element
* happens-before the action of applying the {@code hasNext}
* predicate for subsequent elements. For any given element an action may
* be performed in whatever thread the library chooses.
*
* @param the type of stream elements
* @param seed the initial element
* @param hasNext a predicate to apply to elements to determine when the
* stream must terminate.
* @param next a function to be applied to the previous element to produce
* a new element
* @return a new sequential {@code Stream}
* @since 9
*/
public static Stream iterate(T seed, Predicate super T> hasNext, UnaryOperator next) {
Objects.requireNonNull(next);
Objects.requireNonNull(hasNext);
Spliterator spliterator = new Spliterators.AbstractSpliterator<>(Long.MAX_VALUE,
Spliterator.ORDERED | Spliterator.IMMUTABLE) {
T prev;
boolean started, finished;
@Override
public boolean tryAdvance(Consumer super T> action) {
Objects.requireNonNull(action);
if (finished)
return false;
T t;
if (started)
t = next.apply(prev);
else {
t = seed;
started = true;
}
if (!hasNext.test(t)) {
prev = null;
finished = true;
return false;
}
action.accept(prev = t);
return true;
}
@Override
public void forEachRemaining(Consumer super T> action) {
Objects.requireNonNull(action);
if (finished)
return;
finished = true;
T t = started ? next.apply(prev) : seed;
prev = null;
while (hasNext.test(t)) {
action.accept(t);
t = next.apply(t);
}
}
};
return StreamSupport.stream(spliterator, false);
}
//4.iterator()重载的方法
@Test
public void test4(){
//复习:Stream的实例化:①通过集合的stream() ②通过数组工具类:Arrays ③ Stream中静态方法:of() ④iterator() / generate()
Stream.iterate(0,x -> x + 1).limit(10).forEach(System.out::println);//原来的控制终止方式
System.out.println();
Stream.iterate(0,x -> x < 10,x -> x + 1).forEach(System.out::println);//现在的终止方式
}
//Optional类中提供了转换为Stream的方法:stream()
@Test
public void test1() {
List list = new ArrayList<>();
list.add("Tom");
list.add("Jerry");
list.add("Tim");
Optional> optional = Optional.ofNullable(list);
// Stream> stream = optional.stream();
// System.out.println(stream.count());//返回 1 当成了 list 集合
Stream stream = optional.stream().flatMap(x -> x.stream());
// System.out.println(stream.count()); //返回 3
stream.forEach(System.out::println); //有终止操作 前面注释的是不可以同时使用的
}
Tom
Jerry
Tim
251: Multi-Resolution Images
263: HiDPI Graphics on Windows and Linux
在Mac 上,JDK已经支持视网膜显示,但在Linux 和Windows上, 它并没有。在那里,Java 程序在当前的高分辨率屏幕上可能看起来很 小,不能使用它们。这是因为像素用于这些系统的大小计算(无论像 素实际有多大)。毕竟,高分辨率显示器的有效部分是像素非常小。
JEP 263 以这样的方式扩展了 JDK,即 Windows 和 Linux 也考虑到 像素的大小。为此,使用比现在更多的现代 API:Direct2D for Windows 和 GTK +,而不是 Xlib for Linux。图形,窗口和文本由此自动缩放。
JEP 251 还提供处理多分辨率图像的能力,即包含不同分辨率的 相同图像的文件。根据相应屏幕的 DPI 度量,然后以适当的分辨率使用图像。
110: HTTP 2 Client
HTTP,用于传输网页的协议,早在 1997 年就被采用在目前的 1.1 版本中。直到 2015 年,HTTP2 才成为标准。
HTTP/1.1和HTTP/2的主要区别是如何在客户端和服务器之间构建 和传输数据。HTTP/1.1 依赖于请求/响应周期。 HTTP/2 允许服务器 “push”数据:它可以发送比客户端请求更多的数据。 这使得它可 以优先处理并发送对于首先加载网页至关重要的数据。
Java 9 中有新的方式来处理 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 中。
requires jdk.incubator.httpclient;
举例:
import jdk.incubator.http.HttpClient;
import jdk.incubator.http.HttpRequest;
import jdk.incubator.http.HttpResponse;
import java.io.IOException;
import java.net.URI;
public class HttpClientTest {
public static void main(String[] args) {
//jdk 9 中 使用 HttpClient替换原有的HttpURLConnection
try {
HttpClient client = HttpClient.newHttpClient();
HttpRequest req = HttpRequest.newBuilder(URI.create("https://www.baidu.com/")).GET().build();
HttpResponse response = null;
response = client.send(req, HttpResponse.BodyHandler.asString());
System.out.println(response.statusCode());
System.out.println(response.version().name());
System.out.println(response.body());
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
211: Elide Deprecation Warnings on Import Statements
214: Remove GC Combinations Deprecated in JDK 8
277: Enhanced Deprecation 289: Deprecate the Applet API
291: Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector
Java 9 废弃或者移除了几个不常用的功能。其中最主要的是 Applet API,现在是标记为废弃的。随着对安全要求的提高,主流浏 览器已经取消对 Java 浏览器插件的支持。HTML5 的出现也进一步加速了它的消亡。开发者现在可以使用像 Java Web Start 这样的技术来代替 Applet,它可以实现从浏览器启动应用程序或者安装应用程序。
同时,appletviewer 工具也被标记为废弃。
139: Enhance javac to Improve Build Speed.
199: Smart Java Compilation, Phase Two
智能 java 编译工具( sjavac )的第一个阶段始于 JEP139 这个项目, 用于在多核处理器情况下提升 JDK 的编译速度。如今,这个项目已经 进入第二阶段,即 JEP199,其目的是改进 Java 编译工具,并取代目 前 JDK 编译工具 javac,继而成为 Java 环境默认的通用的智能编译工具。
JDK 9 还更新了 javac 编译器以便能够将 java 9 代码编译运行在低版本 Java 中。
158: Unified JVM Logging
271: Unified GC Logging271谈到了统一一下 GC(垃圾回收) 的日志,jdk9 把 G1(垃圾回收器,G1只是其中一个垃圾回收器) 作为了默认的垃圾回收机制
日志是解决问题的唯一有效途径:曾经很难知道导致 JVM 性能问题和导致 JVM 崩溃的根本原因。不同的 JVM 日志的碎片化和日志选项(例如:JVM 组件对于日志使用的是不同的机制和规则) ,这使得 JVM 难以进行调试。
解决该问题最佳方法:对所有的JVM组件引入一个单一的系统, 这些 JVM 组件支持细粒度的和易配置的 JVM 日志。
224: HTML5 Javadoc
225: Javadoc Search
jdk 8 :生成的 java 帮助文档是在 HTML 4 中,而 HTML 4 已经是很久的标准了。
jdk 9 :javadoc 的输出,现在符合兼容 HTML 5 标准。
下图是 java8 中生成的 html 页面,如果想要找到一些类文档,必须 在 google 中搜索。
下图是在 java 9 中,添加了一个搜索框。
236: Parser API for Nashorn
292: Implement Selected ECMAScript 6 Features in Nashorn
Nashorn 项目在 JDK 9 中得到改进,它为 Java 提供轻量级的 Javascript 运行时。Nashorn 项目跟随 Netscape 的 Rhino 项目,目 的是为了在 Java 中实现一个高性能但轻量级的 Javascript 运行时。 Nashorn 项目使得 Java 应用能够嵌入 Javascript。它在 JDK 8 中为 Java 提供一个 Javascript 引擎。
JDK 9 包含一个用来解析 Nashorn 的 ECMAScript 语法树的 API。这个 API 使得 IDE 和服务端框架不需要依赖 Nashorn 项目的 内部实现类,就能够分析 ECMAScript 代码。
243: Java-Level JVM Compiler Interface
295: Ahead-of-Time Compilation
Oracle 一直在努力提高 Java 启动和运行时性能,希望其能够在 更广泛的场景达到或接近本地语言的性能。但是,直到今天,谈到 Java,很多 C/C++ 开发者还是会不屑地评价为启动慢,吃内存。
简单说,这主要是因为 Java 编译产生的类文件是 Java 虚拟机 可以理解的二进制代码,而不是真正的可执行的本地代码,需要 Java 虚拟机进行解释和编译,这带来了额外的开销。
JIT(Just-in-time)编译器可以在运行时将热点编译成本地代码, 速度很快。但是 Java 项目现在变得很大很复杂,因此 JIT 编译器需 要花费较长时间才能热身完,而且有些 Java 方法还没法编译,性能 方面也会下降。AoT 编译就是为了解决这些问题而生的。
在 JDK 9 中, AOT(JEP 295: Ahead-of-Time Compilation)作为实 验特性被引入进来,开发者可以利用新的 jaotc 工具将重点代码转换 成类似类库一样的文件。虽然仍处于试验阶段,但这个功能使得 Java 应用在被虚拟机启动之前能够先将 Java 类编译为原生代码。此功能旨在改进小型和大型应用程序的启动时间,同时对峰值性能的影响很 小。
但是 Java 技术供应商 Excelsior 的营销总监 Dmitry Leskov 担心 AoT 编译技术不够成熟,希望 Oracle 能够等到 Java 10 时有个更 稳定版本才发布。
另外 JVMCI (JEP 243: Java-Level JVM Compiler Interface)等特性, 对于整个编程语言的发展,可能都具有非常重要的意义,虽然未必引 起了广泛关注。目前 Graal Core API 已经被集成进入 Java 9,虽然还 只是初始一小步,但是完全用 Java 语言来实现的可靠的、高性能的 动态编译器,似乎不再是遥不可及,这是 Java 虚拟机开发工程师的 福音。
与此同时,随着 Truffle 框架和 Substrate VM 的发展,已经让个 别信心满满的工程师高呼“One VM to Rule Them All!”, 也许就在不 远的将来 Ploygot 以一种另类的方式成为现实。
一个标准化和轻量级的 JSON API 被许多 java 开发人员所青睐。但是 由于资金问题无法在 Java 9 中见到,但并不会削减掉。Java 平台首席 架构师 Mark Reinhold 在 JDK 9 邮件列中说:“这个 JEP 将是平台上的 一个有用的补充,但是在计划中,它并不像 Oracle 资助的其他功能那 么重要,可能会重新考虑 JDK 10 或更高版本中实现。 ”
对许多应用而言货币价值都是一个关键的特性,但 JDK 对此却几 乎没有任何支持。严格来讲,现有的 java.util.Currency 类只是代表了当前 ISO 4217 货币的一个数据结构,但并没有关联的值或者自定义货币。JDK 对货币的运算及转换也没有内建的支持,更别说有一个能够 代表货币值的标准类型了。
此前,Oracle 公布的 JSR 354 定义了一套新的 Java 货币 API: JavaMoney,计划会在 Java 9 中正式引入。但是目前没有出现在 JDK 9 中。
不过,如果你用的是 Maven 的话,可以做如下的添加,即可使用相 关的 API 处理货币:
org.javamoney
moneta
0.9
代码参考,可以访问 https://github.com/JavaMoney,里面已经给出 了使用说明和示例。