对于Java 8的新日期时间的总体印象还是比较积极的,一部分是因为Joda-Time的积极影响,另一部分是因为官方终于听取了开发人员的需求。如果希望了解更多细节,可以参考[官方文档](https://bbs.csdn.net/topics/618545628)。
#### Nashorn JavaScript引擎
Java 8提供了新的[Nashorn JavaScript](https://bbs.csdn.net/topics/618545628)引擎,使得我们可以在JVM上开发和运行JS应用。Nashorn JavaScript引擎是javax.script.ScriptEngine的另一个实现版本,这类Script引擎遵循相同的规则,允许Java和JavaScript交互使用,例子代码如下:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName( “JavaScript” );
System.out.println( engine.getClass().getName() );
System.out.println( “Result:” + engine.eval( “function f() { return 1; }; f() + 1;” ) );
这个代码的输出结果如下:
jdk.nashorn.api.scripting.NashornScriptEngine
Result: 2
#### Base64
[对Base64编码的支持](https://bbs.csdn.net/topics/618545628)已经被加入到Java 8官方库中,这样不需要使用第三方库就可以进行Base64编码,例子代码如下:
package com.javacodegeeks.java8.base64;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class Base64s {
public static void main(String[] args) {
final String text = “Base64 finally in Java 8!”;
final String encoded = Base64
.getEncoder()
.encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
System.out.println( encoded );
final String decoded = new String(
Base64.getDecoder().decode( encoded ),
StandardCharsets.UTF_8 );
System.out.println( decoded );
}
}
这个例子的输出结果如下:
QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==
Base64 finally in Java 8!
新的Base64API也支持URL和MINE的编码解码。
(**Base64.*getUrlEncoder*()** / **Base64.*getUrlDecoder*()**, **Base64.*getMimeEncoder*()** / **Base64.*getMimeDecoder*()**)。
#### 并行数组
Java8版本新增了很多新的方法,用于支持并行数组处理。最重要的方法是 **parallelSort()** ,可以显著加快多核机器上的数组排序。下面的例子论证了**parallexXxx** 系列的方法:
package com.javacodegeeks.java8.parallel.arrays;
import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;
public class ParallelArrays {
public static void main( String[] args ) {
long[] arrayOfLong = new long [ 20000 ];
Arrays.parallelSetAll( arrayOfLong,
index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + " " ) );
System.out.println();
Arrays.parallelSort( arrayOfLong );
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + " " ) );
System.out.println();
}
}
上述这些代码使用\*\*parallelSetAll()**方法生成20000个随机数,然后使用**parallelSort()\*\*方法进行排序。这个程序会输出乱序数组和排序数组的前10个元素。上述例子的代码输出的结果是:
Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378
Sorted: 39 220 263 268 325 607 655 678 723 793
#### 并发性
基于新增的lambda表达式和steam特性,为Java 8中为**java.util.concurrent.ConcurrentHashMap** 类添加了新的方法来支持聚焦操作;另外,也为**java.util.concurrentForkJoinPool **类添加了新的方法来支持通用线程池操作(更多内容可以参考[并发编程](https://bbs.csdn.net/topics/618545628))。
Java 8还添加了新的**java.util.concurrent.locks.StampedLock** 类,用于支持基于容量的锁——该锁有三个模型用于支持读写操作(可以把这个锁当做是**java.util.concurrent.locks.ReadWriteLock** 的替代者)。
在\*\*java.util.concurrent.atomic \*\*包中也新增了不少工具类,列举如下:
* DoubleAccumulator
* DoubleAdder
* LongAccumulator
* LongAdder
### 新的Java工具
#### Nashorn引擎:jjs
**jjs**是一个基于标准Nashorn引擎的命令行工具,可以接受js源码并执行。例如,我们写一个**func.js**文件,内容如下:
function f() {
return 1;
};
print( f() + 1 );
可以在命令行中执行这个命令:jjs func.js,控制台输出结果是:
2
如果需要了解细节,可以参考[官方文档](https://bbs.csdn.net/topics/618545628)。
#### 类依赖分析器:jdeps
**jdeps**是一个相当棒的命令行工具,它可以展示包层级和类层级的Java类依赖关系,它以\*\*.class\*\*文件、目录或者Jar文件为输入,然后会把依赖关系输出到控制台。
我们可以利用jedps分析下Spring Framework库,为了让结果少一点,仅仅分析一个JAR文件:org.springframework.core-3.0.5.RELEASE.jar。
jdeps org.springframework.core-3.0.5.RELEASE.jar
这个命令会输出很多结果,我们仅看下其中的一部分:依赖关系按照包分组,如果在classpath上找不到依赖,则显示"not found".
org.springframework.core-3.0.5.RELEASE.jar -> C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar
org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)
-> java.io
-> java.lang
-> java.lang.annotation
-> java.lang.ref
-> java.lang.reflect
-> java.util
-> java.util.concurrent
-> org.apache.commons.logging not found
-> org.springframework.asm not found
-> org.springframework.asm.commons not found
org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)
-> java.lang
-> java.lang.annotation
-> java.lang.reflect
-> java.util
更多的细节可以参考[官方文档](https://bbs.csdn.net/topics/618545628)。
### JVM的新特性
使用 Metaspace ([JEP 122](https://bbs.csdn.net/topics/618545628))代替持久代(PermGen space)。
在JVM参数方面,使用 **-XX:MetaSpaceSize**和 **-XX:MaxMetaspaceSize** 代替原来的 **-XX:PermSize** 和 **-XX:MaxPermSize**。
##
## Java 9 新特性
Java9 是Java8后一个比较大的更新,包含新特性比较多,此篇文章只总结下Java 9 版本的一些重要的新特性。并不完全。
Java 9 全部的新特性,请看官网:[Java 平台,标准版 Oracle JDK 9 中的新增功能](https://bbs.csdn.net/topics/618545628)
Java各个版本的文档入口:[Java平台,标准版文档](https://bbs.csdn.net/topics/618545628)
Java各个版本下载:<https://jdk.java.net/archive/>
### 模块化
Java 9 中的模块化是对 Java 的一次重大改进。但是模块化并不是最近才提出来的,我们经常使用的 maven 构建工具,就是典型的模块化构建工具。模块化不仅让模块命名清晰,写出高内聚低耦合的代码,更可以方便处理模块之间的调用关系。
module 是新增的Java代码访问权限级别,每个module可以包含多个package。
通过**module-info.java**文件来声明该文件夹及其子文件夹为一个模块,\*\*exports \*\*关键字可以用来控制模块内哪些包对外暴露。
module store.api{
exports com.dingtalk.store.api;
}
使用module后,即使包中的类是public的,如果未通过exports显式导出其程序包,则外部模块是不可调用的。
如果一个模块想要使用被另一个模块导出的package包中的类,可以用**requires**关键字在其module-info.java文件中来导入(读取)目标模块的package包。
module store.service {
requires com.dingtalk.store.api;
}
Java9 module 与Maven module 很像,但功能完全不一样,后者是作为依赖构建来方便管理应用代码,而Java Module是在于安全性、访问性控制,通过exports/requires 控制模块内需要暴露和依赖的具体包。
### 接口支持定义私有方法
在 Java 8 中增加了默认方法,在 Java 9 中又增加了私有方法,这时开始接口中不仅仅有了定义,还具有了行为。我想这是出于代码构造上的考虑,如果没有私有方法,那么当多个默认方法的行为一样时,就要写多个相同的代码。而有了私有方法,事情就变得不一样了。
举个例子:
public class Jdk9Interface {
public static void main(String[] args) {
ChinaPeople chinaPeople = new ChinaPeople();
chinaPeople.sleep();
chinaPeople.eat();
chinaPeople.doXxx();
}
}
class ChinaPeople implements People {
@Override
public void sleep() {
System.out.println(“躺着睡”);
}
}
interface People {
void sleep();
default void eat() {
drink();
}
default void doXxx() {
drink();
}
private void drink() {
System.out.println("喝水");
}
}
例子中的接口 people 中的 eat() 和 doXxx() 默认行为一致,使用私有方法可以方便的抽取一个方法出来。
输出结果:
躺着睡
喝水
喝水
### 集合工厂方法
在 Java 9 中为集合的创建增加了静态工厂创建方式,也就是 of 方法,通过静态工厂 of 方法创建的集合是**只读集合**,里面的对象**不可改变**。并且**不能存在 null 值**,对于 set 和 map 集合,也**不能存在 key 值重复**。这样不仅**线程安全**,而且**消耗的内存也更小**。
// 工厂方法创建集合
List stringList = List.of(“a”, “b”, “c”, “d”);
Set stringSet = Set.of(“a”, “b”, “c”, “d”);
Map<String, Integer> stringIntegerMap = Map.of(“key1”, 1, “key2”, 2, “key3”, 3);
Map<String, Integer> stringIntegerMap2 = Map.ofEntries(Map.entry(“key1”, 1), Map.entry(“key2”, 2));
// 集合输出
System.out.println(stringList);
System.out.println(stringSet);
System.out.println(stringIntegerMap);
System.out.println(stringIntegerMap2);
输出:
[a, b, c, d]
[d, a, c, b]
{key2=2, key1=1, key3=3}
{key2=2, key1=1}
这种只读集合在 Java 9 之前创建是通过 Collections.unmodifiableList 修改集合操作权限实现的。
List arrayList = new ArrayList<>();
arrayList.add(“CSDN”);
arrayList.add(“阿提说说”);
// 设置为只读集合
arrayList = Collections.unmodifiableList(arrayList);
静态工厂 of 方法创建的集合还有一个特性,就是**工厂内部会自由复用已有实例或者创建新的实例**,所以应该避免对 of 创建的集合进行判等或者 haseCode 比较等操作。
// 工厂可以自由创建新的实例或者复用现有实例,所以 使用 of 创建的集合,避免 == 或者 hashCode 判断操作
List stringList = List.of(“a”, “b”, “c”, “d”);
List stringList2 = List.of(“a”, “b”, “c”, “d”);
System.out.println(stringList.hashCode());
System.out.println(stringList2.hashCode());
// 输出结果
// 3910595
// 3910595
### 增强流(Stream)API
Stream 流操作自从 Java 8 引入以来,一直广受好评。
当然,学习 Stream 之前要先学习 Lambda ,也是Java 8的内容。
在 Java 9 中,又对 Stream 进行了增强,主要增加了 4 个新的操作方法:dropWhile,takeWhile,ofNullable,iterate。
1、takeWhile: 从头开始**筛选**,遇到不满足的就结束
// takeWhile ,从头开始筛选,遇到不满足的就结束了
List list1 = List.of(1, 2, 3, 4, 5);
List listResult = list1.stream().takeWhile(x -> x < 3).collect(Collectors.toList());
System.out.println(listResult);
// takeWhile ,从头开始筛选,遇到不满足的就结束
List list2 = List.of(1, 2, 3, 4, 3, 0);
List listResult2 = list2.stream().takeWhile(x -> x < 3).collect(Collectors.toList());
System.out.println(listResult2);
输出结果:
[1, 2]
[1, 2]
2、dropWhile: 从头开始**删除**,遇到不满足的就结束
// dropWhile ,从头开始删除,遇到不满足的就结束
List list1 = List.of(1, 2, 3, 4, 5);
List listResult = list1.stream().dropWhile(x -> x < 3).collect(Collectors.toList());
System.out.println(listResult);
// dropWhile ,从头开始删除,遇到不满足的就结束
List list2 = List.of(1, 2, 3, 4, 3, 0);
List listResult2 = list2.stream().dropWhile(x -> x < 3).collect(Collectors.toList());
System.out.println(listResult2);
输出结果:
[3, 4, 5]
[3, 4, 3, 0]
3、ofNullable: 创建支持全 null 的 Stream
Stream stream = Stream.of(1, 2, null);
stream.forEach(System.out::print);
System.out.println();
// 空指针异常
// stream = Stream.of(null);
stream = Stream.ofNullable(null);
stream.forEach(System.out::print);
输出结果:
12null
4、iterate: 可以重载迭代器。
IntStream.iterate(0, x -> x < 10, x -> x + 1).forEach(System.out::print);
输出结果:
0123456789
在 Stream 增强之外,还增强了 Optional ,Optional 增加了可以转换成 Stream 的方法。
Stream s = Optional.of(1).stream();
s.forEach(System.out::print);
### HTTP / 2 Client
Java 9 内置了新的 HTTP/2 客户端,请求更加方便。
随便访问一个不存在的网页。
HttpClient client = HttpClient.newHttpClient();
URI uri = URI.create(“http://www.baidu.com”);
HttpRequest req = HttpRequest.newBuilder(uri).header(“User-Agent”, “Java”).GET().build();
HttpResponse resp = client.send(req, HttpResponse.BodyHandler.asString());
String body = resp.body();
System.out.println(body);
输出得到的结果,这里是这个网站的报错信息。
There is no method xxxAction in ApiController
### Java REPL - JShell
交互式的编程环境在其他语言如 Python 上早就有了,而 Java 上的交互式语言只到 Java 9 才出现。交互式的编程可以让开发者在输入代码的时候就获取到程序的运行结果,而不用像之前一样新建文件、创建类、导包、测试一系列流程。
JShell 中支持 tab 补全代码以及自动添加分号,下面通过一个例子演示 JShell 的使用。
1、进入 JShell. 查看帮助文档
C:\Users>jshell
| 欢迎使用 JShell – 版本 9
| 要大致了解该版本, 请键入: /help intro
jshell> /help
| 键入 Java 语言表达式, 语句或声明。
| 或者键入以下命令之一:
| /list [<名称或 id>|-all|-start]
| 列出您键入的源
| /edit <名称或 id>
| 编辑按名称或 id 引用的源条目
| /drop <名称或 id>
| 删除按名称或 id 引用的源条目
| /save [-all|-history|-start] <文件>
| 将片段源保存到文件。
| /open
| 打开文件作为源输入
| /vars [<名称或 id>|-all|-start]
| 列出已声明变量及其值
| /methods [<名称或 id>|-all|-start]
| 列出已声明方法及其签名
| /types [<名称或 id>|-all|-start]
| 列出已声明的类型
| /imports
| 列出导入的项
| /exit
| 退出 jshell
| /env [-class-path <路径>] [-module-path <路径>] [-add-modules <模块>] …
| 查看或更改评估上下文
| /reset [-class-path <路径>] [-module-path <路径>] [-add-modules <模块>]…
| 重启 jshell
| /reload [-restore] [-quiet] [-class-path <路径>] [-module-path <路径>]…
| 重置和重放相关历史记录 – 当前历史记录或上一个历史记录 (-restore)
| /history
| 您键入的内容的历史记录
| /help [|]
| 获取 jshell 的相关信息
| /set editor|start|feedback|mode|prompt|truncation|format …
| 设置 jshell 配置信息
| /? [|]
| 获取 jshell 的相关信息
| /!
| 重新运行上一个片段
| /
| 按 id 重新运行片段
| /-
| 重新运行前面的第 n 个片段
|
| 有关详细信息, 请键入 ‘/help’, 后跟
| 命令或主题的名称。
| 例如 ‘/help /list’ 或 ‘/help intro’。主题:
|
| intro
| jshell 工具的简介
| shortcuts
| 片段和命令输入提示, 信息访问以及
| 自动代码生成的按键说明
| context
| /env /reload 和 /reset 的评估上下文选项
jshell>
2、定义一个变量:a = 10,遍历从 0 到 a 的数字
jshell> int a =10;
a ==> 10
jshell> for(int i=0;i<a;i++){System.out.println(i);}
0
1
2
3
4
5
6
7
8
9
3、定义一个集合,赋值 1,2,3,4,5。然后输出集合
jshell> List list = List.of(1,2,3,4,5);
list ==> [1, 2, 3, 4, 5]
jshell> list
list ==> [1, 2, 3, 4, 5]
4、查看输入过的代码
jshell> /list
1 : int a =10;
2 : for(int i=0;i<a;i++){System.out.println(i);}
3 : List list = List.of(1,2,3,4,5);
4 : list
5、列出导入的包
jshell> /imports
| import java.io.*
| import java.math.*
| import java.net.*
| import java.nio.file.*
| import java.util.*
| import java.util.concurrent.*
| import java.util.function.*
| import java.util.prefs.*
| import java.util.regex.*
| import java.util.stream.*
6、将代码保存到文件并退出
jshell> /save d:/JShell.java
jshell> /exit
再见
在 D 盘看到的保存的代码片段。
### JVM 调优的新特性
第一个:删除 JDK 8 中已弃用的垃圾收集器 (GC) 组合
* 这意味着以下 GC 组合不再存在:
+ DefNew + CMS
+ ParNew + SerialOld
+ 增量CMS
* 并发标记扫描 (CMS) 的“前台”模式也已被删除。以下命令行标志已被删除:
+ -Xincgc
+ -XX:+CMSIncrementalMode
+ -XX:+UseCMSCompactAtFullCollection
+ -XX:+CMSFullGCsBeforeCompaction
+ -XX:+UseCMSCollectionPassing
* 命令行标志-XX:+UseParNewGC不再有效。ParNew 只能与 CMS 一起使用,而 CMS 需要 ParNew。因此,该-XX:+UseParNewGC标志已被弃用,并且可能会在未来的版本