上两天阿海升级了一下JDK环境,IDE版本更新成了Eclipse Neon。
不得不说,这个版本的Eclipse确实很不错,用起来页比较舒服多了。尤其是这个Eclipse的暗黑主题“Drack”改进了不少,这也是我一直用的主题。且看我先秀一下图:
这个主题中,改进最大的就是JSP编辑器,各种高亮很完全哦!JS块中的语法高亮也很带感,然而美中不足的事,仍然没有JS的语法提示。各种图片我就不再秀了,这篇文章主要介绍JDK8的一些新的特性。
哦对了,假如喜欢这个版本的Eclipse的话,下面我给出了下载直连,想体验又不想翻墙的朋友们可以在下面的链接中下载(这个版本是64位的,需配合64位JDK使用。)
Eclipse:
http://aiyiupload.oss-cn-beijing.aliyuncs.com/blog/file/default/eclipse-jee-neon-R-win32-x86_64.zip
JDK8:
http://aiyiupload.oss-cn-beijing.aliyuncs.com/blog/file/default/jdk-8u91-windows-x64.exe
特性一,接口的默认方法
在JDK8中,允许给接口本身添加一个默认的实现。用“default”进行修饰。下面我创建一个MyCompute接口,并给他的sum方法一个默认的实现。
package com.aiyi.jdk.testinterface;/**
我的 Compute 类
@author 郭胜凯
@emai 719348277@qq.com
@time 2016 年 7 月 4 日 下午 1:07:42
*/
public interface MyCompute {/**
定义加法运算并给他默认实现方法
@param i1 加数
@param i2 加数
@return 和
/
default int sum(int i1, int i2){
return i1 + i2;
}
/**
定义减法运算接口
@param i1 减数
@param i2 被减数
@return 差
/
int subtraction(int i1, int i2);
}
那么我来测试一下这个 MyCompute,因为我已经给了他 sum 的默认实现,所以我只需要再实现 subtraction() 方法就行了:
public static void main(String[] args) {
MyCompute c = new MyCompute() {
@Override
public int subtraction(int i1, int i2) {
// TODO Auto-generated method stub
return i1 - i2;
}
};
//Test sum function, result = 2
int result = c.sum(1, 1);
//Test subtraction function, result2 = 0
int result2 = c.subtraction(1, -1);
}
特性二,静态方法与构造函数的引用
这一点儿类似在 C++ 中的《函数指针》的概念。我们可以这么理解(至少我是这么认为的),在 Java 中,方法和构造方法都看作是对象的一种,那么你要引用它(不是调用),则可以用::来引用。用来存储这个引用的类型用 @FunctionlaInterface 注解来标识。
方法引用对象的创建
我继续以上面的例子进行解释,加入我要引用 MyCompute 中的 sun(int, int); 这个方法,可以就如下代码:
创建一个存储方法的对象:
package com.aiyi.jdk.testinterface;/**
方法引用类
@author 郭胜凯
@emai 719348277@qq.com
@time 2016 年 7 月 4 日 下午 1:32:23
*/
public class MyFunction {/**
指向某个 Function 的方法指针
@author 郭胜凯
@emai 719348277@qq.com
@time 2016 年 7 月 4 日 下午 1:31:07
@param <F> 传值类型
@param <T> 结果类型
*/
@FunctionalInterface
interface Fun<F, T> {
T run(F from);
}
}
方法引用对象的使用
接下来我用这个 Fun 来引用一个方法。并执行他。(“Main::myMethod”表示 Main 类中的 myMethod() 方法)
输出结果是"This is arg"
public static void main(String[] args) {
Fun<String, String> fun = Main::myMethod;
String result = fun.run("This is arg");
System.out.println(result);
}
/**
* 指向某个 Function 的方法指针
* @author 郭胜凯
* @emai 719348277@qq.com
* @time 2016 年 7 月 4 日 下午 1:31:07
* @param <F> 传值类型
* @param <T> 结果类型
*/
@FunctionalInterface
interface Fun<F, T> {
T run(F from);
}
public static String myMethod(String arg){
return arg;
}
构造函数引用对象的创建及使用
大同小异,就不多说了,输出结果“Creating”
public Main(String arg){
System.out.println(arg);
}
@FunctionalInterface
interface mainFactory<M extends Main>{
M run(String arg);
}
public static void main(String[] args) {
mainFactory<Main> mainFun = Main::new;
mainFun.run("Creating");
}
特性三,玩死你不偿命的 Lambda 表达式
在 JDK8 中,引入了 Lambda(读:了母的)表达式的概念,不得不说这 Lambda 实在是太强大了,强大了不止一点儿半点儿。看我一点儿一点儿慢慢的介绍它~
入门
(破天荒地用了一段儿英文注释,因为我在锻炼英文)
我来用一个简单的例子来讲解 lambda 有多么逆天。在 JDK7 中如果要对一个 list 进行排序的话,或许你是这样做的:
/**
* This is the sorting of ‘JDK7’
*/
public static void testSort1(){
List<String> list = Arrays.asList("asd","dweas","aw","trs");
//Create a comparator
Comparator<String> mySort = new Comparator<String>() {
@Override
public int compare(String srt1, String str2) {
// TODO Auto-generated method stub
return srt1.compareTo(str2);
}
};
//Sorting...
Collections.sort(list, mySort);
}
假如用 lambda 来完成上面这个排序的话,你可以一行代码搞定:
/**
* This is the sorting of ‘JDK8' s lambda’(了母的)
*/
public static void testSort2(){
List<String> list = Arrays.asList("asd","dweas","aw","trs");
//Sorting...
Collections.sort(list, (str1, str2) -> str1.compareTo(str2));
}
没错,就是这么嗨!仔细看“->”后面是一个 Integer 值得表达式,假如我的运算有点儿难度,那么我还可以把他变成一个代码块:
/**
* This is the sorting of ‘JDK8' s lambda’(了母的)
*/
public static void testSort2(){
List<String> list = Arrays.asList("asd","dweas","aw","trs");
//Sorting...
Collections.sort(list, (str1, str2) -> {
return str1.compareTo(str2);
});
}
如果你以为 lambda 表达式就这么点儿含量的话,说出去会被人笑话的,上面只是举一个简单易懂的例子,这个例子运用的只是 lambda 表达式中的冰山一角!
Lambda 表达式在 Java 中的定义
每一个 lambda 表达式都对应一个类型,通常是接口类型。每一个 Lambda 表达式,都会匹配一个对应“函数式接口”的东西。
“函数式接口”是指仅仅只包含一个抽象方法的接口,即标注 @FunctionalInterface 的接口类。在上面刚刚就介绍了“函数式接口”了,请参阅《特性二》的说明
并且,你还可以给你的函数式接口添加默认方法。
我们重新创建一个 MyFunction 这个类
/**
* 方法类
* @author 郭胜凯
* @emai 719348277@qq.com
* @time 2016 年 7 月 4 日 下午 1:32:23
*/
public class MyFunction {public static int sum(int i1, int i2){
return i1 + i2;
}
}
回到 Main 类中,我用 Lambda 表达式调用一下他,输出结果:2,即 1+1 的和
public static void main(String[] args) {
Fun<Integer, Integer> fun = (i1, i2) -> MyFunction.sum(i1, i2);
System.out.println(fun.run(1, 1));
}
/**
指向某个 Function 的方法指针
@author 郭胜凯
@emai 719348277@qq.com
@time 2016 年 7 月 4 日 下午 1:31:07
@param <T> 参数 1 类型
@param <U> 参数 2 类型
*/
@FunctionalInterface
interface Fun<T, U> {
int run(T arg1, U arg2);
}
深入理解 lambda
为了深入理解 lambda,继续用之前的 list 排序案例来说,这次我们手动建立一个支持 lambda 的排序工具类。
1、new 一个抽象的比较器
package com.aiyi.jdk.testilambda;public interface MyComparator {
int sort(String str1, String str2);
}
2、new 一个排序工具类
package com.aiyi.jdk.testilambda;import java.util.List;
public class MySortUtil {
public static <T> void sortList (List<T> list, MyComparator<T> comparator){
for (int i = 0; i < list.size(); i++) {
for (int j = i + 1; j < list.size(); j++) {
T temp = list.get(i);
if (comparator.sort(list.get(i), list.get(j)) > 0) {
list.set(i, list.get(j));
list.set(j, temp);
}
}
}
}
}
3、使用 lambda 调用这个排序工具类进行排序
public static void testSort3(){
List<String> list = Arrays.asList("asd","dweas","aw","trs");
//Sorting...
MySortUtil.sortList(list, (str1, str2) -> {
return str1.compareTo(str2);
});
}
搞定,是不是呢上面那个入门代码超级像?
lambda 的作用域
lambda 的作用域和内部类的作用于差不多,访问局部变量必须由 final 进行修饰。即不可更改。访问成员变量或者静态字段的话,则不需要 final 修饰,并且可以对其进行修改。
JDK8 中 lambda 支持的其他接口
Predicate
predicate 接收一个变量,并返回一个 boolean 值,predicate 接口是一个简单的比进行较运算操作的接口,但一般不使用。例如:
Predicate<String> p = (str) -> str.equls("321aiyi.com");
sysout(p.test("321aiyi.com")); //true
sysout(p.test("321aiyi.com").negate()) //false
Function
这个接口其实就是一个单纯的最简单的实现了 @FunctionalInterface 的接口类,一般对一个方法进行引用时,可以直接使用该接口。
Function<String, Integer> fun = String::valueOf;
String str = fun.apply(1);
Supplier
这个就更简单了,他就相当于类的 Factory,用它获取任意一个带有空参构造的类。
Supplier<StringBuffer> s = StringBuffer::new;
StringBuffer sb = s.get();
Consumer
给定一个参数,并对其进行操作
Consumer<String> c = (str) -> System.out.pringln(str + "~~~");
c.accept("woca"); //"woca~~~"
另外,还有好多之前的接口也做了对 lambda 的支持,比如之前所说的 Comparator 接口等等,这里就不一一诉说了,喜欢钻研的朋友们可以拔源码或者阅读相关文档慢慢看。
特性四、反射的加强
JDK8 加强了反射,它允许你直接通过反射获取参数的名字
很长一段时间里,Java 程序员一直在发明不同的方式使得方法参数的名字能保留在 Java 字节码中,并且能够在运行时获取它们(比如,Paranamer 类库)。最终,在 Java 8 中把这个强烈要求的功能添加到语言层面(通过反射 API 与 Parameter.getName()方法)与字节码文件(通过新版的 javac 的–parameters 选项)中。
package com.javacodegeeks.java8.parameter.names;import java.lang.reflect.Method;
import java.lang.reflect.Parameter;public class ParameterNames {
public static void main(String[] args) throws Exception {
Method method = ParameterNames.class.getMethod( "main", String[].class );
for( final Parameter parameter: method.getParameters() ) {
System.out.println( "Parameter: " + parameter.getName() );
}
}
}
如果不使用–parameters 参数来编译这个类,然后运行这个类,会得到下面的输出:
Parameter: arg0
如果使用–parameters 参数来编译这个类,程序的结构会有所不同(参数的真实名字将会显示出来):
Parameter: args
对于有经验的 Maven 用户,通过 maven-compiler-plugin 的配置可以将-parameters 参数添加到编译器中去。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerArgument>-parameters</compilerArgument>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
针对 Java 8 最新发布的Eclipse Kepler SR2(请检查这里的下载说明)提供了非常实用的配置选项,可以通过下图的配置方式来控制编译器行为
图 1. 配置 Eclipse 工程使之支持 Java 8 编译器的新特性——parameters 参数
此外,Parameter 类有一个很方便的方法 isNamePresent()来验证是否可以获取参数的名字。
下面的是我从网络上 copy 的,需要记得东西太多,copy 下来慢慢消化。。。
4. Java 类库的新特性(copy)
Java 8 通过增加大量新类,扩展已有类的功能的方式来改善对并发编程、函数式编程、日期/时间相关操作以及其他更多方面的支持。
4.1 Optional
到目前为止,臭名昭著的空指针异常是导致 Java 应用程序失败的最常见原因。以前,为了解决空指针异常,Google 公司著名的 Guava 项目引入了 Optional 类,Guava 通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional 类已经成为 Java 8 类库的一部分。
Optional 实际上是个容器:它可以保存类型 T 的值,或者仅仅保存 null。Optional 提供很多有用的方法,这样我们就不用显式进行空值检测。更多详情请参考官方文档。
我们下面用两个小例子来演示如何使用 Optional 类:一个允许为空值,一个不允许为空值。
Optional< String > fullName = Optional.ofNullable( null );
System.out.println( "Full Name is set? " + fullName.isPresent() );
System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) );
System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
如果 Optional 类的实例为非空值的话,isPresent()返回 true,否从返回 false。为了防止 Optional 为空值,orElseGet()方法通过回调函数来产生一个默认值。map()函数对当前 Optional 的值进行转化,然后返回一个新的 Optional 实例。orElse()方法和 orElseGet()方法类似,但是 orElse 接受一个默认值而不是一个回调函数。下面是这个程序的输出:
Full Name is set? false
Full Name: [none]
Hey Stranger!
让我们来看看另一个例子:
Optional< String > firstName = Optional.of( "Tom" );
System.out.println( "First Name is set? " + firstName.isPresent() );
System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) );
System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
System.out.println();
下面是程序的输出:
First Name is set? true
First Name: Tom
Hey Tom!
更多详情请参考官方文档
4.2 Stream
最新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到 Java 中。这是目前为止对 Java 类库最好的补充,因为 Stream API 可以极大提供 Java 程序员的生产力,让程序员写出高效率、干净、简洁的代码。
Stream API 极大简化了集合框架的处理(但它的处理的范围不仅仅限于集合框架的处理,这点后面我们会看到)。让我们以一个简单的 Task 类为例进行介绍:
public class Streams {
private enum Status {
OPEN, CLOSED
};
private static final class Task {
private final Status status;
private final Integer points;Task( final Status status, final Integer points ) {
this.status = status;
this.points = points;
}
public Integer getPoints() {
return points;
}
public Status getStatus() {
return status;
}
@Override
public String toString() {
return String.format( "[%s, %d]", status, points );
}
}
}
Task 类有一个分数的概念(或者说是伪复杂度),其次是还有一个值可以为 OPEN 或 CLOSED 的状态.让我们引入一个 Task 的小集合作为演示例子:
final Collection< Task > tasks = Arrays.asList(
new Task( Status.OPEN, 5 ),
new Task( Status.OPEN, 13 ),
new Task( Status.CLOSED, 8 )
);
我们下面要讨论的第一个问题是所有状态为 OPEN 的任务一共有多少分数?在 Java 8 以前,一般的解决方式用 foreach 循环,但是在 Java 8 里面我们可以使用 stream:一串支持连续、并行聚集操作的元素。
// Calculate total points of all active tasks using sum()
final long totalPointsOfOpenTasks = tasks
.stream()
.filter( task -> task.getStatus() == Status.OPEN )
.mapToInt( Task::getPoints )
.sum();
System.out.println( "Total points: " + totalPointsOfOpenTasks );
程序在控制台上的输出如下:
Total points: 18
这里有几个注意事项。第一,task 集合被转换化为其相应的 stream 表示。然后,filter 操作过滤掉状态为 CLOSED 的 task。下一步,mapToInt 操作通过 Task::getPoints 这种方式调用每个 task 实例的 getPoints 方法把 Task 的 stream 转化为 Integer 的 stream。最后,用 sum 函数把所有的分数加起来,得到最终的结果。
在继续讲解下面的例子之前,关于 stream 有一些需要注意的地方(详情在这里).stream 操作被分成了中间操作与最终操作这两种。
中间操作返回一个新的 stream 对象。中间操作总是采用惰性求值方式,运行一个像 filter 这样的中间操作实际上没有进行任何过滤,相反它在遍历元素时会产生了一个新的 stream 对象,这个新的 stream 对象包含原始 stream
中符合给定谓词的所有元素。
像 forEach、sum 这样的最终操作可能直接遍历 stream,产生一个结果或副作用。当最终操作执行结束之后,stream 管道被认为已经被消耗了,没有可能再被使用了。在大多数情况下,最终操作都是采用及早求值方式,及早完成底层数据源的遍历。
stream 另一个有价值的地方是能够原生支持并行处理。让我们来看看这个算 task 分数和的例子。
// Calculate total points of all tasks
final double totalPoints = tasks
.stream()
.parallel()
.map( task -> task.getPoints() ) // or map( Task::getPoints )
.reduce( 0, Integer::sum );
System.out.println( "Total points (all tasks): " + totalPoints );
这个例子和第一个例子很相似,但这个例子的不同之处在于这个程序是并行运行的,其次使用 reduce 方法来算最终的结果。
下面是这个例子在控制台的输出:
Total points (all tasks): 26.0
经常会有这个一个需求:我们需要按照某种准则来对集合中的元素进行分组。Stream 也可以处理这样的需求,下面是一个例子:
// Group tasks by their status
final Map< Status, List< Task > > map = tasks
.stream()
.collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map );
这个例子的控制台输出如下:
{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}
让我们来计算整个集合中每个 task 分数(或权重)的平均值来结束 task 的例子。
// Calculate the weight of each tasks (as percent of total points)
final Collection< String > result = tasks
.stream() // Stream< String >
.mapToInt( Task::getPoints ) // IntStream
.asLongStream() // LongStream
.mapToDouble( points -> points / totalPoints ) // DoubleStream
.boxed() // Stream< Double >
.mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream
.mapToObj( percentage -> percentage + "%" ) // Stream< String>
.collect( Collectors.toList() ); // List< String >
System.out.println( result );
下面是这个例子的控制台输出:
[19%, 50%, 30%]
最后,就像前面提到的,Stream API 不仅仅处理 Java 集合框架。像从文本文件中逐行读取数据这样典型的 I/O 操作也很适合用 Stream API 来处理。下面用一个例子来应证这一点。
final Path path = new File( filename ).toPath();
try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {
lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );
}
对一个 stream 对象调用 onClose 方法会返回一个在原有功能基础上新增了关闭功能的 stream 对象,当对 stream 对象调用 close()方法时,与关闭相关的处理器就会执行。
Stream API、Lambda 表达式与方法引用在接口默认方法与静态方法的配合下是 Java 8 对现代软件开发范式的回应。更多详情请参考官方文档。
4.3 Date/Time API (JSR 310)
Java 8 通过发布新的 Date-Time API (JSR 310)来进一步加强对日期与时间的处理。对日期与时间的操作一直是 Java 程序员最痛苦的地方之一。标准的 java.util.Date 以及后来的 java.util.Calendar 一点没有改善这种情况(可以这么说,它们一定程度上更加复杂)。
这种情况直接导致了Joda-Time——一个可替换标准日期/时间处理且功能非常强大的 Java API 的诞生。Java 8新的 Date-Time API (JSR 310)在很大程度上受到Joda-Time的影响,并且吸取了其精髓。新的 java.time 包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。在设计新版 API 时,十分注重与旧版 API 的兼容性:不允许有任何的改变(从 java.util.Calendar 中得到的深刻教训)。如果需要修改,会返回这个类的一个新实例。
让我们用例子来看一下新版 API 主要类的使用方法。第一个是 Clock 类,它通过指定一个时区,然后就可以获取到当前的时刻,日期与时间。Clock 可以替换 System.currentTimeMillis()与 TimeZone.getDefault()。
// Get the system clock as UTC offset
final Clock clock = Clock.systemUTC();
System.out.println( clock.instant() );
System.out.println( clock.millis() );
下面是程序在控制台上的输出:
2014-04-12T15:19:29.282Z
1397315969360
我们需要关注的其他类是 LocaleDate 与 LocalTime。LocaleDate 只持有 ISO-8601 格式且无时区信息的日期部分。相应的,LocaleTime 只持有 ISO-8601 格式且无时区信息的时间部分。LocaleDate 与 LocalTime 都可以从 Clock 中得到。
// Get the local date and local time
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now( clock );
System.out.println( date );
System.out.println( dateFromClock );
// Get the local date and local time
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now( clock );
System.out.println( time );
System.out.println( timeFromClock );
下面是程序在控制台上的输出:
2014-04-12
2014-04-12
11:25:54.568
15:25:54.568
LocaleDateTime 把 LocaleDate 与 LocaleTime 的功能合并起来,它持有的是 ISO-8601 格式无时区信息的日期与时间。下面是一个快速入门的例子。
// Get the local date/time
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );
System.out.println( datetime );
System.out.println( datetimeFromClock );
下面是程序在控制台上的输出:
2014-04-12T11:37:52.309
2014-04-12T15:37:52.309
如果你需要特定时区的日期/时间,那么 ZonedDateTime 是你的选择。它持有 ISO-8601 格式具具有时区信息的日期与时间。下面是一些不同时区的例子:
// Get the zoned date/time
final ZonedDateTime zonedDatetime = ZonedDateTime.now();
final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );
System.out.println( zonedDatetime );
System.out.println( zonedDatetimeFromClock );
System.out.println( zonedDatetimeFromZone );
下面是程序在控制台上的输出:
2014-04-12T11:47:01.017-04:00[America/New_York]
2014-04-12T15:47:01.017Z
2014-04-12T08:47:01.017-07:00[America/Los_Angeles]
最后,让我们看一下 Duration 类:在秒与纳秒级别上的一段时间。Duration 使计算两个日期间的不同变的十分简单。下面让我们看一个这方面的例子。
// Get duration between two dates
final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );final Duration duration = Duration.between( from, to );
System.out.println( "Duration in days: " + duration.toDays() );
System.out.println( "Duration in hours: " + duration.toHours() );
上面的例子计算了两个日期 2014 年 4 月 16 号与 2014 年 4 月 16 号之间的过程。下面是程序在控制台上的输出:
Duration in days: 365
Duration in hours: 8783
对 Java 8 在日期/时间 API 的改进整体印象是非常非常好的。一部分原因是因为它建立在“久战杀场”的Joda-Time基础上,另一方面是因为用来大量的时间来设计它,并且这次程序员的声音得到了认可。更多详情请参考官方文档。
Java 新工具章节会再次谈到 Nashorn。
4.5 Base64
在 Java 8 中,Base64 编码已经成为 Java 类库的标准。它的使用十分简单,下面让我们看一个例子:
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!
Base64 类同时还提供了对 URL、MIME 友好的编码器与解码器(Base64.getUrlEncoder() / Base64.getUrlDecoder(), Base64.getMimeEncoder() / Base64.getMimeDecoder())。
4.6 并行(parallel)数组
Java 8 增加了大量的新方法来对数组进行并行处理。可以说,最重要的是 parallelSort()方法,因为它可以在多核机器上极大提高数组排序的速度。下面的例子展示了新方法(parallelXxx)的使用。
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
4.7 并发(Concurrency)
在新增 Stream 机制与 lambda 的基础之上,在 java.util.concurrent.ConcurrentHashMap 中加入了一些新方法来支持聚集操作。同时也在 java.util.concurrent.ForkJoinPool 类中加入了一些新方法来支持共有资源池(common pool)(请查看我们关于 Java 并发的免费课程)。
新增的 java.util.concurrent.locks.StampedLock 类提供一直基于容量的锁,这种锁有三个模型来控制读写操作(它被认为是不太有名的 java.util.concurrent.locks.ReadWriteLock 类的替代者)。
在 java.util.concurrent.atomic 包中还增加了下面这些类:
DoubleAccumulator
DoubleAdder
LongAccumulator
LongAdder
5. 新的 Java 工具
Java 8 也带来了一些新的命令行工具。在这节里我们将会介绍它们中最有趣的部分。
5.1 Nashorn 引擎: jjs
jjs 是个基于 Nashorn 引擎的命令行工具。它接受一些 JavaScript 源代码为参数,并且执行这些源代码。例如,我们创建一个具有如下内容的 func.js 文件:
function f() {
return 1;
};print( f() + 1 );
我们可以把这个文件作为参数传递给 jjs 使得这个文件可以在命令行中执行:
jjs func.js
下面是程序在控制台上的输出:
2
更多详情请参考官方文档
5.2 类依赖分析器 jdeps
jdeps 是一个很有用的命令行工具。它可以显示 Java 类的包级别或类级别的依赖。它接受一个.class 文件,一个目录,或者一个 jar 文件作为输入。jdeps 默认把结果输出到系统输出(控制台)上。
下面我们查看现阶段较流行的Spring 框架类库的依赖报告,为了简化这个例子,我们只分析一个 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
更多详情请参考官方文档
6. Java 虚拟机(JVM)的新特性
PermGen 空间被移除了,取而代之的是 Metaspace(JEP 122)。JVM 选项-XX:PermSize与-XX:MaxPermSize分别被-XX:MetaSpaceSize与-XX:MaxMetaspaceSize所代替。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于