编者注:在本文中,我们提供了全面的Java 8功能教程。 自Java 8公开发布以来已经有一段时间了,所有迹象都表明这是一个非常重要的版本。

我们在Java Code Geeks处提供了大量教程,例如“ 玩Java 8 – Lambda和并发” ,“ Java 8 Date Time API教程: JDK 8时代的 LocalDateTime和抽象类与接口” 。

我们还引用了其他来源的15篇必读Java 8教程 。 当然,我们还研究了一些不足之处,例如Java 8的黑暗面 。

现在,是时候将所有Java 8的主要功能集中到一本参考文章中,以使您阅读愉快。 请享用!

目录

1.简介 2. Java语言的新功能
2.1。 Lambda和功能接口 2.2。 接口默认方法和静态方法 2.3。 方法参考 2.4。 重复注释 2.5。 更好的类型推断 2.6。 扩展注释支持
3. Java编译器的新功能
3.1。 参数名称
4. Java库中的新功能
4.1。 可选的 4.2。 流 4.3。 日期/时间API(JSR 310) 4.4。 Nashorn JavaScript引擎 4.5。 Base64 4.6。 平行阵列 4.7。 并发
5.新的Java工具
5.1。 Nashorn引擎:jjs 5.2。 类依赖分析器:jdeps
6. Java运行时(JVM)的新功能 7.结论 8.资源

1.简介

毫无疑问, Java 8发行版是Java 5以来(Java发行版早在2004年发布)以来最伟大的事情。 它为Java带来了许多新功能,包括一种语言,其编译器,库,工具以及JVM(Java虚拟机)本身。 在本教程中,我们将研究所有这些更改,并在实际示例中演示不同的使用场景。

本教程由几个部分组成,每个部分都涉及平台的特定方面:

  • 语言
  • 编译器
  • 图书馆
  • 工具
  • 运行时(JVM)

2. Java语言的新功能

Java 8无论如何都是主要版本。 可能有人说,要实现每个Java开发人员正在寻找的功能,最终确定都花了很长时间。 在本节中,我们将介绍其中的大部分内容。

Lambda和功能接口

Lambda(也称为闭包)是整个Java 8版本中最大,最期待的语言更改。 它们使我们可以将功能视为方法参数(传递函数),或将代码视为数据:每个功能开发人员都非常熟悉的概念。 从第一天开始,JVM平台上的许多语言(Groovy, Scala等等)都具有lambda,但是Java开发人员别无选择,只能用样板匿名类来破坏lambda。

Lambdas设计讨论花费了大量时间和社区精力。 但是最后,我们找到了折衷方案,从而导致了新的简洁紧凑的语言结构。 以最简单的形式,lambda可以表示为以逗号分隔的参数列表,即–>符号和主体。 例如:

 Arrays.asList( "a" , "b" , "d" ).forEach( e -> System.out.println( e ) ); 

请注意,参数e的类型是由编译器推断的。 或者,您可以显式提供参数的类型,并将定义包装在方括号中。 例如:

 Arrays.asList( "a" , "b" , "d" ).forEach( ( String e ) -> System.out.println( e ) ); 

如果lambda的主体更复杂,则可以将其包装在方括号中,就像Java中常用的函数定义一样。 例如:

 Arrays.asList( "a" , "b" , "d" ).forEach( e -> { System.out.print( e ); System.out.print( e );  } ); 

Lambda可以引用类成员和局部变量(如果没有,则暗指使它们有效地成为最终变量)。 例如,这两个片段是等效的:

 String separator = "," ;  Arrays.asList( "a" , "b" , "d" ).forEach( ( String e ) -> System.out.print( e + separator ) ); 

和:

 final String separator = "," ;  Arrays.asList( "a" , "b" , "d" ).forEach( ( String e ) -> System.out.print( e + separator ) ); 

Lambda可能会返回一个值。 返回值的类型将由编译器推断。 如果lambda主体是单线的,则不需要return语句。 以下两个代码段是等效的:

 Arrays.asList( "a" , "b" , "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) ); 

和:

 Arrays.asList( "a" , "b" , "d" ).sort( ( e1, e2 ) -> { int result = e1.compareTo( e2 ); return result;  } ); 

语言设计师对如何使已经存在的功能实现lambda友好性进行了大量思考。 结果,出现了功能接口的概念。 函数接口是只有一种方法的接口。 这样,它可以隐式转换为lambda表达式。 java.lang.Runnablejava.util.concurrent.Callable是功能接口的两个很好的例子。 实际上,功能接口是易碎的:如果有人在接口定义中仅添加了另一种方法,它将不再起作用,并且编译过程将失败。 为了克服这种脆弱性并明确声明接口的作用,Java 8添加了特殊的注解@FunctionalInterface(Java库中所有现有的接口也已使用@FunctionalInterface进行注解)。 让我们看一下这个简单的功能接口定义:

 @FunctionalInterface  public interface Functional { void method();  } 

要记住的一件事: 默认方法和静态方法不会破坏功能接口协定,可以声明为:

 @FunctionalInterface  public interface FunctionalDefaultMethods { void method();         default void defaultMethod() { }  } 

Lambda是Java 8的最大卖点。它有潜力吸引越来越多的开发人员使用这个强大的平台,并为纯Java中的函数式编程概念提供最新的支持。 有关更多详细信息,请参阅官方文档 。

接口的默认方法和静态方法

Java 8用两个新概念扩展了接口声明:默认方法和静态方法。 默认方法使接口在某种程度上类似于特征,但目标有所不同。 它们允许向现有接口添加新方法,而不会破坏与为这些接口的较早版本编写的代码的二进制兼容性。

默认方法和抽象方法之间的区别在于,需要实现抽象方法。 但是默认方法不是。 相反,每个接口都必须提供所谓的默认实现,并且所有实现者都将默认继承它(可以在需要时覆盖此默认实现)。 让我们看下面的例子。

 private interface Defaulable { // Interfaces now allow default methods, the implementer may or // may not implement (override) them. default String notRequired() { return "Default implementation" ; }  }          private static class DefaultableImpl implements Defaulable {  }      private static class OverridableImpl implements Defaulable { @Override public String notRequired() { return "Overridden implementation" ; }  } 

接口Defaulable使用关键字default作为方法定义的一部分来声明默认方法notRequired() 。 其中一个类DefaultableImpl实现了此接口,而保留了默认方法的实现。 另一个OverridableImpl重写默认实现并提供自己的实现。

Java 8提供的另一个有趣的功能是接口可以声明(并提供实现)静态方法。 这是一个例子。

 private interface DefaulableFactory { // Interfaces now allow static methods static Defaulable create( Supplier< Defaulable > supplier ) { return supplier.get(); }  } 

下面的小代码段将上面示例中的默认方法和静态方法粘合在一起。

 public static void main( String[] args ) { Defaulable defaulable = DefaulableFactory.create( DefaultableImpl:: new ); System.out.println( defaulable.notRequired() );         defaulable = DefaulableFactory.create( OverridableImpl:: new ); System.out.println( defaulable.notRequired() );  } 

该程序的控制台输出如下所示:

 Default implementation  Overridden implementation 

JVM上的默认方法实现非常有效,并且方法调用的字节码指令支持该方法。 默认方法允许现有的Java接口发展而不会中断编译过程。 很好的例子是添加到java.util.Collection接口的大量方法: stream()parallelStream()forEach()removeIf() ……

尽管功能强大,但应谨慎使用默认方法:在将方法声明为默认方法之前,最好三思,如果确实需要,则可能会导致复杂层次结构中的歧义和编译错误。 有关更多详细信息,请参阅官方文档 。

方法参考

方法引用提供了有用的语法,可以直接引用现有的方法或Java类或对象(实例)的构造函数。 与Lambdas表达式结合使用时,方法引用使语言构造看起来紧凑而简洁,从而简化了样板。

下面,将Car类作为不同方法定义的示例,让我们区分四种受支持的方法引用类型。

 public static Car { class Car { public static Car create( final Supplier< Car > supplier ) { return supplier.get(); }         public static void collide( final Car car ) { System.out.println( "Collided " + car.toString() ); }         public void follow( final Car another ) { System.out.println( "Following the " + another.toString() ); }         public void repair() { System.out.println( "Repaired " + this .toString() ); }  } 

方法引用的第一类是语法为Class :: new的构造方法引用,或者对于泛型,为Class <T> :: new 。 请注意,构造函数没有参数。

 final Car car = Car.create( Car:: new );  final List< Car > cars = Arrays.asList( car ); 

第二种类型是使用语法Class :: static_method引用静态方法。 请注意,该方法仅接受Car类型的一个参数。

 cars.forEach( Car::collide ); 

第三种类型是使用语法Class :: method引用特定类型的任意对象的实例方法 。 请注意,该方法不接受任何参数。

 cars.forEach( Car::repair ); 

最后,第四种类型是对特定类实例的实例方法的引用,即语法instance :: method 。 请注意,该方法只接受Car类型的一个参数。

 final Car police = Car.create( Car:: new );  cars.forEach( police::follow ); 

将所有这些示例作为Java程序运行会在控制台上产生以下输出(实际的Car实例可能有所不同):

 Collided com.javacodegeeks.java8.method.references.MethodReferences$Car @7a81197d  Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car @7a81197d  Following the com.javacodegeeks.java8.method.references.MethodReferences$Car @7a81197d 

有关方法引用的更多示例和详细信息,请参考官方文档。

重复注释

自从Java 5引入了注释支持以来,此功能就非常流行并且得到了广泛的使用。 但是,注释使用的局限性之一是不能在同一位置多次声明同一注释。 Java 8违反了此规则,并引入了重复注释。 它允许相同的注释在声明的位置重复多次。

重复的注释本身应使用@Repeatable注释进行注释。 实际上,这不是语言更改,而是更多的编译器技巧,因为该技术的原理保持不变。 让我们看一个简单的例子:

 package com.javacodegeeks.java8.repeatable.annotations;  import java.lang.annotation.ElementType;  import java.lang.annotation.Repeatable;  import java.lang.annotation.Retention;  import java.lang.annotation.RetentionPolicy;  import java.lang.annotation.Target;  public class RepeatingAnnotations { @Target ( ElementType.TYPE ) @Retention ( RetentionPolicy.RUNTIME ) public @interface Filters { Filter[] value(); }     @Target ( ElementType.TYPE ) @Retention ( RetentionPolicy.RUNTIME ) @Repeatable ( Filters. class ) public @interface Filter { String value(); };     @Filter ( "filter1" ) @Filter ( "filter2" ) public interface Filterable { }     public static void main(String[] args) { for ( Filter filter: Filterable. class .getAnnotationsByType( Filter. class ) ) { System.out.println( filter.value() ); } }  } 

正如我们所看到的,是一个注释类过滤器与@Repeatable(过滤器。 )注解。 过滤器只是过滤器注释的持有者,但是Java编译器试图向开发人员隐藏其存在。 因此, Filterable接口具有两次定义的Filter注释(没有提及Filters )。

此外,反射API提供了新的方法getAnnotationsByType()返回某种类型的重复注释(请注意,筛选。 .getAnnotation(过滤器。 )将返回过滤器的编译器注入的情况下)。

程序输出如下所示:

 filter1  filter2 

有关更多详细信息,请参阅官方文档 。

更好的类型推断

Java 8编译器在类型推断方面进行了很多改进。 在许多情况下,可以通过使代码保持整洁的编译器来推断显式类型参数。 让我们看一个例子。

 package com.javacodegeeks.java8.type.inference;  public class Value< T > { public static < T > T defaultValue() { return null ; }     public T getOrDefault( T value, T defaultValue ) { return ( value != null ) ? value : defaultValue; ) ? value : defaultValue; }  } 

这是Value <String>类型的用法。

 package com.javacodegeeks.java8.type.inference;  public class TypeInference { public static void main(String[] args) { final Value< String > value = new Value<>(); value.getOrDefault( "22" , Value.defaultValue() ); }  } 

Value的类型参数 推断出defaultValue () ,不需要提供该值。 在Java 7中,相同的示例将无法编译,应将其重写为Value。<String> defaultValue ()

扩展注释支持

Java 8扩展了可以使用注释的上下文。 现在,几乎可以注释所有内容:局部变量,泛型类型,超类和实现接口,甚至是方法的异常声明。 下面是几个示例。

 package com.javacodegeeks.java8.annotations;  import java.lang.annotation.ElementType;  import java.lang.annotation.Retention;  import java.lang.annotation.RetentionPolicy;  import java.lang.annotation.Target;  import java.util.ArrayList;  import java.util.Collection;  public class Annotations { @Retention ( RetentionPolicy.RUNTIME ) @Target ( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } ) public @interface NonEmpty { }         public static class Holder< @NonEmpty T > extends @NonEmpty Object { public void method() throws @NonEmpty Exception { } }         @SuppressWarnings ( "unused" ) public static void main(String[] args) { final Holder< String > holder = new @NonEmpty Holder< String >(); @NonEmpty Collection< @NonEmpty String > strings = new ArrayList<>(); }  } 

ElementType。 TYPE_USEElementType。 TYPE_PARAMETER是两种新的元素类型,用于描述适用的注释上下文。 Annotation Processing API也进行了一些小的更改,以识别Java编程语言中的那些新类型的注释。

3. Java编译器的新功能

参数名称

从字面上看,Java开发人员已经发明了各种方法来将方法参数名称保留为Java字节码 ,并使它们在运行时可用(例如Paranamer库 )。 最后,Java 8将此要求苛刻的功能烘焙到语言中(使用Reflection 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的 configuration部分将–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()

4. Java库中的新功能

Java 8添加了许多新类并扩展了现有类,以便为现代并发,函数式编程,日期/时间等提供更好的支持。

可选的

迄今为止, 著名的NullPointerException是Java应用程序失败的最常见原因。 很久以前,伟大的Google Guava项目引入了Optional作为NullPointerException的解决方案,通过检查防止代码库污染,并鼓励开发人员编写更干净的代码。 受Google Guava的启发, Optional现在是Java 8库的一部分。

可选的只是一个容器:它可以保存某个T类型的 ,或者可以为null 。 它提供了许多有用的方法,因此显式null检查不再有任何借口。 请参阅Java 8官方文档以获取更多详细信息。

我们将看一下Optional用法的两个小例子:具有空的值和不允许为null的值。

 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的此实例具有非null值,则isPresent()方法返回true,否则返回false 。 如果Optional通过接受生成默认值的函数为null ,则orElseGet()方法提供了后备机制。 map()方法转换当前Optional的值,并返回新的Optional实例。 orElse()方法类似于orElseGet(),但它接受函数默认值而不是函数。 这是该程序的输出:

 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! 

有关更多详细信息,请参阅官方文档。

新添加的Stream API ( java.util.stream )在Java 引入了现实世界中的函数式编程。 到目前为止,这是Java库中最全面的扩展,旨在通过允许Java开发人员编写有效,简洁,简洁的代码来提高他们的生产力。

Stream API大大简化了集合的处理(但不仅限于Java集合,我们将在后面看到)。 让我们从名为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具有一些点(或伪复杂性)概念,可以为OPENCLOSED 。 然后,让我们介绍一些要处理的任务。

 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中,答案是流:支持顺序和并行聚合操作的一系列元素。

 // 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 

这里发生了几件事。 首先,将任务集合转换为其流表示形式。 然后,对流的过滤操作将所有已关闭的任务过滤掉。 上下一步骤中,mapToInt操作任务 S的整数 s使用每个任务实例的任务:: getPoints方法的流的流转换。 最后,使用求和方法对所有点求和,得出最终结果。

在继续下一个示例之前,需要注意一些有关流的注意事项( 此处有更多详细信息 )。 流操作分为中间操作和终端操作。

中间操作返回一个新的流。 它们总是很懒惰,执行诸如filter之类的中间操作实际上并不执行任何过滤,而是创建一个新的流,该新流在遍历时将包含与给定谓词匹配的初始流的元素。

终端操作(例如forEachsum )可能会遍历流以产生结果或副作用。 执行终端操作后,流管道被视为已消耗,无法再使用。 在几乎所有情况下,终端操作人员都很渴望完成对基础数据源的遍历。

流的另一个价值主张是开箱即用的并行处理支持。 让我们看一下这个示例,该示例确实总结了所有任务的要点。

 // 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 

通常,需要根据某些标准对收集元素进行分组。 流可以帮助实现这一点,下面的示例也将对此进行说明。

 // 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 ]]} 

为了完成任务示例,让我们基于任务集的点来计算每个任务在整个集合中的总体百分比(或权重)。

 // 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 * // LongStream )( 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操作非常适合从流处理中受益。 这是一个确认这一点的小例子。

 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 );  } 

在流上调用的onClose方法返回具有附加关闭处理程序的等效流。 在流上调用close()方法时,将运行关闭处理程序。

通过接口的默认方法和静态方法生成的 Stream API以及Lambda和方法引用是Java 8对软件开发中现代范例的响应。 有关更多详细信息,请参阅官方文档 。

日期/时间API(JSR 310)

Java 8通过提供新的Date-Time API(JSR 310),进一步进行了日期和时间管理。 对于Java开发人员而言,日期和时间操纵是最糟糕的痛苦之一。 紧随其后的java.util.Calendar标准java.util.Date根本没有改善这种情况(可以说,使情况更加混乱)。

这就是Joda-Time的诞生方式:Java的绝佳替代日期/时间API。 Java 8的新Date-Time API(JSR 310)受Joda-Time的影响很大,并充分利用了它的优势。 新的java.time包包含日期,时间,日期/时间,时区,瞬间,持续时间和时钟操作的所有类 。 在API的设计中,非常认真地考虑了不变性:不允许进行任何更改(从java.util.Calendar中吸取的艰巨教训)。 如果需要修改,将返回相应类的新实例。

让我们看一下关键类及其用法示例。 第一类是时钟 ,它使用时区提供对当前时刻,日期和时间的访问。 可以使用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 2014 -12T15: 19 : 29 .282Z  1397315969360 

我们将要查看的其他新类是LocaleDateLocalTime 。 在ISO- 8601日历系统中,LocaleDate仅保存日期部分,没有时区。 分别地, LocaleTime在ISO- 8601日历系统中仅保存没有时区的时间部分LocaleDateLocaleTime都可以从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 

LocalDateTimeLocaleDateLocalTime结合在一起,并在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 2014 -12T11: 37 : 52.309  2014 - 04 2014 -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 2014 -12T15: 47 : 01 .017Z  2014 - 04 -12T08: 47 : 01.017 - 07 : 00 [America/Los_Angeles] 

最后,让我们看一下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日至2015年 4月 16日这两个日期之间的持续时间(以天和小时为单位)。 这是控制台上的示例输出:

 Duration in days: 365  Duration in hours: 8783 

关于Java 8的新日期/时间API的总体印象是非常非常积极的。 部分是由于它经过了久经考验的基础( Joda-Time ),部分是因为这一次最终得到了认真解决,并听到了开发人员的声音。 有关更多详细信息,请参阅官方文档 。

Nashorn JavaScript引擎

Java 8附带了新的Nashorn JavaScript引擎 ,该引擎允许在JVM上开发和运行某些类型JavaScript应用程序。 Nashorn JavaScript引擎只是javax.script.ScriptEngine的另一种实现,并且遵循相同的规则集,从而允许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 

我们将在后面专门讨论新Java工具的部分中回到Nashorn。

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类( Base64。getUrlEncoder () / Base64。getUrlDecoder ()Base64。getMimeEncoder () / Base64。getMimeDecoder () )还提供URL友好的编码器/解码器和MIME友好的编码器/解码器。

平行阵列

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 

并发

新方法已添加到java.util.concurrent.ConcurrentHashMap类中,以支持基于新添加的流功能和lambda表达式的聚合操作。 另外,已经将新方法添加到java.util.concurrent.ForkJoinPool类中,以支持公用池(另请参见有关Java并发性的免费课程 )。

添加了新的java.util.concurrent.locks.StampedLock类,以提供具有三种模式的基于功能的锁,用于控制读/写访问(它可能被认为是臭名昭著的java.util.concurrent.locks.ReadWriteLock的替代方法。 )。

新类已添加到java.util.concurrent.atomic包中:

  • 双累加器
  • 双重加法器
  • 长累积器
  • 长加法器

5.新的Java工具

Java 8附带了一组新的命令行工具。 在本节中,我们将研究其中最有趣的部分。

Nashorn引擎:jjs

jjs是基于命令行的独立Nashorn引擎。 它接受JavaScript源代码文件列表作为参数并运行它们。 例如,让我们创建一个具有以下内容的文件func.js

 function f() { return 1 ;  };  print( f() + 1 ); 

要从命令执行此fie,让我们将其作为参数传递给jjs

 jjs func.js 

控制台上的输出将是:

 2 

有关更多详细信息,请参阅官方文档 。

类依赖分析器:jdeps

jdeps是一个非常出色的命令行工具。 它显示了Java类文件的包级别或类级别的依赖关系。 它接受.class文件, 目录JAR文件作为输入。 默认情况下, jdeps将依赖输出到系统输出(控制台)。

例如,让我们看一下流行的Spring Framework库的依赖项报告。 为了使示例简短,让我们仅分析一个JAR文件: org.springframework.core-3.0.5.RELEASE.jar

 jdeps org.springframework.core- 3.0 . 5 .RELEASE.jar 

该命令输出很多,因此我们将对其进行研究。 依赖项按程序包分组。 如果依赖项在类路径上不可用,则显示为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 

For more details please refer to official documentation .

6. New Features in Java runtime (JVM)

The PermGen space is gone and has been replaced with Metaspace ( JEP 122 ). The JVM options -XX:PermSize and – XX:MaxPermSize have been replaced by -XX:MetaSpaceSize and -XX:MaxMetaspaceSize respectively.

7.结论

The future is here: Java 8 moves this great platform forward by delivering the features to make developers much more productive. It is too early to move the production systems to Java 8 but in the next couples of months its adoption should slowly start growing. Nevertheless the time is right to start preparing your code bases to be compatible with Java 8 and to be ready to turn the switch once Java 8 proves to be safe and stable enough.

As a confirmation of community Java 8 acceptance, recently Pivotal released Spring Framework 4.0.3 with production-ready Java 8 support .

如果您喜欢此功能,请订阅我们的时事通讯,以享受每周更新和免费白皮书! 另外,请查看我们的课程以获得更高级的培训!

You are welcome to contribute with your comments about the exciting new Java 8 features!

8. Resources

Some additional resources which discuss in depth different aspects of Java 8 features:

  • Java 8 Tutorials on JCG Examples: https://examples.javacodegeeks.com/?s=java+8
  • What's New in JDK 8: http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html
  • The Java Tutorials: http://docs.oracle.com/javase/tutorial/
  • WildFly 8, JDK 8, NetBeans 8, Java EE 7: http://blog.arungupta.me/2014/03/wildfly8-jdk8-netbeans8-javaee7-excellent-combo-enterprise-java/
  • Java 8 Tutorial: http://winterbe.com/posts/2014/03/16/java-8-tutorial/
  • JDK 8 Command-line Static Dependency Checker: http://marxsoftware.blogspot.ca/2014/03/jdeps.html
  • The Illuminating Javadoc of JDK 8: http://marxsoftware.blogspot.ca/2014/03/illuminating-javadoc-of-jdk-8.html
  • The Dark Side of Java 8: http://blog.jooq.org/2014/04/04/java-8-friday-the-dark-side-of-java-8/
  • Installing Java™ 8 Support in Eclipse Kepler SR2: http://www.eclipse.org/downloads/java8/
  • Java 8: http://www.baeldung.com/java8
  • Oracle Nashorn. A Next-Generation JavaScript Engine for the JVM: http://www.oracle.com/technetwork/articles/java/jf14-nashorn-2126515.html

Java 8 Features Tutorial was last updated on Oct. 3, 2016

翻译自: https://www.javacodegeeks.com/java-8-features-tutorial.html

Java 8功能教程– ULTIMATE指南(PDF下载)相关推荐

  1. 谷歌搜索引擎优化初学者指南pdf下载

    "有哪些简单的方法可以提高我的网站在谷歌中的排名?" 对于这个问题,推荐SEO初学者阅读:谷歌搜索引擎优化初学者指南 谷歌搜索引擎优化初学者指南:涵 盖了网站管理员需要考虑优化的诸 ...

  2. Java Servlet教程– ULTIMATE指南(PDF下载)

    Java Servlets是一种基于Java的Web技术. Java Servlet技术为Web开发人员提供了一种简单,一致的机制,以扩展Web服务器的功能并访问现有的业务系统. 几乎可以将Servl ...

  3. jdbc pdf_JDBC教程– ULTIMATE指南(PDF下载)

    jdbc pdf 在本文中,我们提供了全面的JDBC教程(Java数据库连接性),这是Oracle提供的API,允许程序员处理Java应用程序中的不同数据库:它允许开发人员建立与数据库的连接,定义特定 ...

  4. JDBC教程– ULTIMATE指南(PDF下载)

    在本文中,我们提供了全面的JDBC教程(Java数据库连接性),这是Oracle提供的一种API,允许程序员处理Java应用程序中的不同数据库:它允许开发人员建立与数据库的连接,定义特定的客户端如何访 ...

  5. 5W字高质量java并发系列详解教程(上)-附PDF下载

    文章目录 第一章 java.util.concurrent简介 主要的组件 Executor ExecutorService ScheduledExecutorService Future Count ...

  6. python程序猿_python程序员指南 pdf下载

    python程序员指南 pdf是一本专为对python编程感兴趣的朋友准备的指导图书,作从最基本的基础知识到繁琐的运用,都进行的详细的解答,是你自学的最好教程了,感兴趣欢迎下载学习! python程序 ...

  7. 150个Java面试问答-最终清单(PDF下载)

    我们的Java面试问题和答案集合全都涉及可以在Java面试中使用的不同类型的问题,以使雇主可以测试您在Java和面向对象编程方面的技能. 在以下各节中,我们将讨论有关面向对象编程及其特性的Java面试 ...

  8. Hive编程指南.pdf下载

    好东西就是要免费共享的,尤其是知识,只有个人强大了,祖国才会强大,哈哈,有点扯了!为节省大家的时间,现将现成的资料奉上: Hive编程指南.pdf 链接:https://pan.baidu.com/s ...

  9. Java注释教程– ULTIMATE指南(PDF下载)

    编者注:在本文中,我们提供了全面的Java注释教程. Java中的注释是一项主要功能,每个Java开发人员都应该知道如何使用它们. 我们在Java Code Geeks上提供了许多教程,例如创建自己的 ...

最新文章

  1. 阿里巴巴开源项目 Druid 负责人温少访谈
  2. 继续- 管理百人研发团队的烦恼(下)
  3. Spring Boot入门(11)实现文件下载功能
  4. Oracle备份时发现空间不够,存储空间比较紧张的情况下,如何提升Oracle备份的去重率?...
  5. JavaScript实现graphBridges图桥算法(附完整源码)
  6. pythonencoding etf-8_etf iopv python 代码30个Python常用小技巧
  7. 编程软件python是什么意思_程序员Python编程必备5大工具,你用过几个?
  8. CentOS 7安装mysql
  9. window命令行启动Mysql并安装服务
  10. 代理模式---论坛权限控制代理
  11. 黄聪:css3实现图片划过一束光闪过效果(图片光影掠过效果)
  12. eudc计算机术语英文,汽车循环工况测试CLTC(CLTC-P和CLTC-C)、WLTC、FTP75、JC08、NEDC、ECE15、EUDC.xls...
  13. 安卓ios和java如何选_在android和iOS之间选择novi编程
  14. Python:Numpy—rand、randn
  15. 联想服务器AR系列,联想沉浸式AR游戏设备Mirage AR现场实拍图赏
  16. JQuery dataTable 扩展+Ajax Post,Get一些基本操作(二)
  17. 判定被7整除的简易方法
  18. 阻塞、非阻塞,同步、异步
  19. java 等待线程池结束_等待线程池中任务执行完毕做优雅关闭
  20. 深度学习论文: KeepAugment: A Simple Information-Preserving Data Augmentation Approach及其PyTorch实现

热门文章

  1. 服务器windows系统如何登陆,如何登陆windows云服务器
  2. github 公钥 私钥_github快速使用
  3. rocketmq java home,rocketmq 安装与配置以及遇到的问题
  4. Invalid bound statement (not found):出现的原因和解决方法
  5. HttpClient 4 API –获取状态代码-getStatusLine()。getStatusCode()示例
  6. java ssl发送邮件_通过SSL发送的Java邮件
  7. spring的bean范围_Spring Bean范围
  8. jpa映射json_如何使用JPA和Hibernate映射JSON集合
  9. flowable背压 取消_使用Flowable.generate()生成可感知背压的流– RxJava常见问题解答...
  10. java streams_使用JShell的Java 9 Streams API