Java8新特性教程 - 终极指南

转载来源:https://www.javacodegeeks.com/2014/05/java-8-features-tutorial.html

 

Java 8 新特性教程 - 终极指南

 

编者按:在这篇文章中,我们提供了一个全面的Java 8功能教程。自从Java 8出现在公众面前已经有一段时间了,所有事情都表明这是一个非常重要的版本。

我们在Java Code Geeks上提供了大量的教程,比如在Java 8中使用Java 8 - Lambdas和Concurrency,Java 8 Date Time API教程:LocalDateTime和Abstract Class Versus接口。

我们还从其他来源引用了15 Must Read Java 8 Tutorials。当然,我们也研究了一些不足之处,比如Java 8的黑暗面。

现在,是时候在一个参考文章下收集所有主要的Java 8功能,以获得阅读乐趣。请享用!

想成为Java 8 Ninja?

订阅我们的新闻通讯并立即下载Java 8功能终极指南

为了让您快速掌握主要的Java 8版本,我们编写了一个带有所有新功能和好东西的kick-ass指南!除了在线学习,您可以下载PDF格式的电子书!

目录

1.简介

2. Java语言的新功能

2.1。Lambdas和功能接口

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年发布,早在2004年)。它为Java作为一种语言,它的编译器,库,工具和JVM(Java虚拟机)本身带来了大量新功能。在本教程中,我们将了解所有这些更改,并演示实际示例中的不同使用方案。

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

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

 

2. Java语言的新功能

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

2.1。Lambdas和功能接口

Lambdas(也称为闭包)是整个Java 8版本中最大和最期待的语言变化。它们允许我们将功能视为方法参数(传递函数),或将代码视为数据:每个功能开发人员都非常熟悉的概念。JVM平台上的许多语言(Groovy,Scala,......)从第一天起就开始使用lambdas,但Java开发人员别无选择,只能使用样板匿名类来攻击lambda。

Lambdas的设计讨论花了很多时间和社区的努力。但最后,已经找到了权衡取舍,从而产生了新的简洁和紧凑的语言结构。在最简单的形式中,lambda可以表示为逗号分隔的参数列表,- >符号和正文。例如:

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

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

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

如果lambda的主体更复杂,它可能被包装成方括号,就像Java中常用的函数定义一样。例如:

1 Arrays.asList( "a""b""d" ).forEach( e -> {
2     System.out.print( e );
3     System.out.print( e );
4 } );

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

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

和:

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

Lambdas可能会返回一个值。返回值的类型将由编译器推断。在返回如果拉姆达身体只是一个班轮不需要声明。下面的两个代码段是等效的:

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

和:

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

语言设计者对如何使现有的lambda友好功能进行了大量的思考。结果,出现了功能接口的概念。功能界面是一个只有一个方法的界面。因此,它可以隐式转换为lambda表达式。在一个java.lang.Runnablejava.util.concurrent.Callable是功能接口的两个很好的例子。在实践中,功能接口是脆弱的:如果有人只在接口定义中添加另一个方法,它将不再起作用,编译过程将失败。为了克服这种脆弱性并明确声明接口的意图是正常的,Java 8添加了特殊的注释@FunctionalInterface(Java库中的所有现有接口也都使用@FunctionalInterface注释)。我们来看看这个简单的功能接口定义:

1 @FunctionalInterface
2 public interface Functional {
3     void method();
4 }

要记住一件事:默认和静态方法不会破坏功能接口合同,可能会声明:

1 @FunctionalInterface
2 public interface FunctionalDefaultMethods {
3     void method();
4         
5     default void defaultMethod() {           
6     }       
7 }

Lambda是Java 8的最大卖点。它有可能吸引越来越多的开发人员加入这个伟大的平台,并为纯Java中的函数式编程概念提供最先进的支持。有关详细信息,请参阅官方文档。

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

Java 8使用两个新概念扩展了接口声明:default和static方法。默认方法使接口有点类似于特征,但服务有点不同的目标。它们允许向现有接口添加新方法,而不会破坏与为这些接口的旧版本编写的代码的二进制兼容性。

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

01 private interface Defaulable {
02     // Interfaces now allow default methods, the implementer may or
03     // may not implement (override) them.
04     default String notRequired() {
05         return "Default implementation";
06     }       
07 }
08         
09 private static class DefaultableImpl implements Defaulable {
10 }
11     
12 private static class OverridableImpl implements Defaulable {
13     @Override
14     public String notRequired() {
15         return "Overridden implementation";
16     }
17 }

接口Defaulable使用关键字default作为方法定义的一部分声明默认方法notRequired()。其中一个类DefaultableImpl实现了此接口,使默认方法实现保持原样。另一个是OverridableImpl,它会覆盖默认实现并提供自己的实现。

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

1 private interface DefaulableFactory {
2     // Interfaces now allow static methods
3     static Defaulable create( Supplier< Defaulable > supplier ) {
4         return supplier.get();
5     }
6 }

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

1 public static void main( String[] args ) {
2     Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );
3     System.out.println( defaulable.notRequired() );
4         
5     defaulable = DefaulableFactory.create( OverridableImpl::new );
6     System.out.println( defaulable.notRequired() );
7 }

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

1 Default implementation
2 Overridden implementation

JVM上的默认方法实现非常高效,并且由方法调用的字节代码指令支持。默认方法允许现有Java接口在不破坏编译过程的情况下发展。好的例子是添加到java.util.Collection接口的过多方法:stream()parallelStream()forEach()removeIf(),...

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

2.3。方法参考

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

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

01 public static class Car {
02     public static Car create( final Supplier< Car > supplier ) {
03         return supplier.get();
04     }             
05         
06     public static void collide( final Car car ) {
07         System.out.println( "Collided " + car.toString() );
08     }
09         
10     public void follow( final Car another ) {
11         System.out.println( "Following the " + another.toString() );
12     }
13         
14     public void repair() {  
15         System.out.println( "Repaired " this.toString() );
16     }
17 }

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

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

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

1 cars.forEach( Car::collide );

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

1 cars.forEach( Car::repair );

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

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

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

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

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

2.4。重复注释

由于Java 5引入了注释支持,因此该功能变得非常流行并且被广泛使用。但是,注释使用的一个限制是,相同的注释不能在同一位置多次声明。Java 8打破了这个规则并引入了重复注释。它允许相同的注释在声明的位置重复多次。

重复注释本身应该使用@Repeatable注释进行注释。实际上,它不是语言变化,而是更多的编译器技巧,因为技术下面保持不变。我们来看看快速示例:

01 package com.javacodegeeks.java8.repeatable.annotations;
02
03 import java.lang.annotation.ElementType;
04 import java.lang.annotation.Repeatable;
05 import java.lang.annotation.Retention;
06 import java.lang.annotation.RetentionPolicy;
07 import java.lang.annotation.Target;
08
09 public class RepeatingAnnotations {
10     @Target( ElementType.TYPE )
11     @Retention( RetentionPolicy.RUNTIME )
12     public @interface Filters {
13         Filter[] value();
14     }
15     
16     @Target( ElementType.TYPE )
17     @Retention( RetentionPolicy.RUNTIME )
18     @Repeatable( Filters.class )
19     public @interface Filter {
20         String value();
21     };
22     
23     @Filter"filter1" )
24     @Filter"filter2" )
25     public interface Filterable {       
26     }
27     
28     public static void main(String[] args) {
29         for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
30             System.out.println( filter.value() );
31         }
32     }
33 }

我们可以看到,有一个注释类Filter使用@Repeatable(Filters。)注释。该过滤器仅仅是一个持有人筛选注释,但Java编译器力图隐藏从开发它的存在。因此,Filterable接口具有两次定义的Filter注释(没有提到过滤器)。

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

程序输出看起来像这样:

1 filter1
2 filter2

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

2.5。更好的类型推断

Java 8编译器在类型推断上有了很大的改进。在许多情况下,显式类型参数可以由编译器推断,使代码更清晰。让我们看看其中一个例子。

01 package com.javacodegeeks.java8.type.inference;
02
03 public class Value< T > {
04     public static< T > T defaultValue() {
05         return null;
06     }
07     
08     public T getOrDefault( T value, T defaultValue ) {
09         return ( value != null ) ? value : defaultValue;
10     }
11 }

这是Value 类型的用法。

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

Value的类型参数defaultValue()是推断的,不需要提供。在Java 7中,相同的示例将不会编译,应该重写为Value。 defaultValue()

2.6。扩展注释支持

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

01 package com.javacodegeeks.java8.annotations;
02
03 import java.lang.annotation.ElementType;
04 import java.lang.annotation.Retention;
05 import java.lang.annotation.RetentionPolicy;
06 import java.lang.annotation.Target;
07 import java.util.ArrayList;
08 import java.util.Collection;
09
10 public class Annotations {
11     @Retention( RetentionPolicy.RUNTIME )
12     @Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )
13     public @interface NonEmpty {       
14     }
15         
16     public static class Holder< @NonEmpty T > extends @NonEmpty Object {
17         public void method() throws @NonEmpty Exception {          
18         }
19     }
20         
21     @SuppressWarnings"unused" )
22     public static void main(String[] args) {
23         final Holder< String > holder = new @NonEmpty Holder< String >();      
24         @NonEmpty Collection< @NonEmpty String > strings = new ArrayList<>();      
25     }
26 }

的ElementType。TYPE_USEElementType。TYPE_PARAMETER是两种新的元素类型,用于描述适用的注释上下文。该注释处理API也经历了一些小的改动,以识别Java编程语言中那些新型的注解。

 

3. Java编译器的新功能

3.1 参数名称

从字面上看,Java开发人员正在发明不同的方法来保存Java字节码中的方法参数名称,并使它们在运行时可用(例如,Paranamer库)。最后,Java 8将这一要求强大的功能融入语言(使用Reflection API和Parameter.getName()方法)和字节代码(使用新的javac编译器参数 - 参数)。

01 package com.javacodegeeks.java8.parameter.names;
02
03 import java.lang.reflect.Method;
04 import java.lang.reflect.Parameter;
05
06 public class ParameterNames {
07     public static void main(String[] args) throws Exception {
08         Method method = ParameterNames.class.getMethod( "main", String[].class );
09         forfinal Parameter parameter: method.getParameters() ) {
10             System.out.println( "Parameter: " + parameter.getName() );
11         }
12     }
13 }

如果你在不使用-parameters参数的情况下编译这个类,然后运行这个程序,你会看到类似的东西:

1 Parameter: arg0

使用-parameters参数传递给编译器时,程序输出将不同(将显示参数的实际名称):

1 Parameter: args

对于有经验的Maven用户,可以使用maven-compiler-plugin的配置部分将-parameters参数添加到编译器中

01 <plugin><font>font>
02     <groupId>org.apache.maven.pluginsgroupId><font>font>
03     <artifactId>maven-compiler-pluginartifactId><font>font>
04     <version>3.1version><font>font>
05     <configuration><font>font>
06         <compilerArgument>-parameterscompilerArgument><font>font>
07         <source>1.8source><font>font>
08         <target>1.8target><font>font>
09     configuration><font>font>
10 plugin>

使用Java 8的最新Eclipse Kepler SR2版本(请查看此下载说明)支持提供了有用的配置选项来控制此编译器设置,如下图所示。

图片1。配置Eclipse项目以支持新的Java 8 compiler -parameters参数。

此外,为了验证参数名称的可用性,Parameter类提供了一个方便的方法isNamePresent()

 

4. Java库中的新功能

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

4.1 可选的

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

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

我们将看一下Optional usages的两个小例子:可空值和不允许null的值。

1 Optional< String > fullName = Optional.ofNullable( null );<font>font>
2 System.out.println( "Full Name is set? " + fullName.isPresent() );        <font>font>
3 System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) ); <font>font>
4 System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );

isPresent()方法返回如果这种情况下可选择具有非空值和虚假,否则。所述orElseGet()方法提供的情况下,回退机制可选具有通过接受函数生成默认一个值。的地图()方法变换的当前可选的值,并返回新可选实例。的OrElse运算()方法类似于orElseGet() ,但代替函数它接受默认值。这是该程序的输出:

1 Full Name is set? false
2 Full Name: [none]
3 Hey Stranger!

让我们简要介绍另一个例子:

1 Optional< String > firstName = Optional.of( "Tom" );<font>font>
2 System.out.println( "First Name is set? " + firstName.isPresent() );        <font>font>
3 System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) ); <font>font>
4 System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );<font>font>
5 System.out.println();

这是输出:

1 First Name is set? true
2 First Name: Tom
3 Hey Tom!

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

4.2 流

新添加的Stream API(java.util.stream)将实际的功能样式编程引入Java。这是迄今为止Java库中最全面的补充,旨在通过允许Java开发人员编写有效,干净和简洁的代码来提高Java开发人员的工作效率。

Stream API使集合处理大大简化(但它不仅限于Java集合,我们稍后会看到)。让我们从名为Task的简单类开始。

01 public class Streams  {
02     private enum Status {
03         OPEN, CLOSED
04     };
05     
06     private static final class Task {
07         private final Status status;
08         private final Integer points;
09
10         Task( final Status status, final Integer points ) {
11             this.status = status;
12             this.points = points;
13         }
14         
15         public Integer getPoints() {
16             return points;
17         }
18         
19         public Status getStatus() {
20             return status;
21         }
22         
23         @Override
24         public String toString() {
25             return String.format( "[%s, %d]", status, points );
26         }
27     }
28 }

任务有一些点(或伪复杂度)的概念,可以是OPENCLOSED。然后让我们介绍一小部分任务。

1 final Collection< Task > tasks = Arrays.asList(
2     new Task( Status.OPEN, 5 ),
3     new Task( Status.OPEN, 13 ),
4     new Task( Status.CLOSED, 8 )
5 );

我们要解决的第一个问题是所有OPEN任务总共有多少点?在Java 8之前,通常的解决方案是某种foreach迭代。但在Java 8中,答案是流:支持顺序和并行聚合操作的一系列元素。

1 // Calculate total points of all active tasks using sum()
2 final long totalPointsOfOpenTasks = tasks
3     .stream()
4     .filter( task -> task.getStatus() == Status.OPEN )
5     .mapToInt( Task::getPoints )
6     .sum();
7         
8 System.out.println( "Total points: " + totalPointsOfOpenTasks );

控制台上的输出看起来像这样:

1 Total points: 18

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

在继续下一个示例之前,有一些关于流的注意事项(这里有更多细节)。流操作分为中间操作和终端操作。

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

终端操作(例如forEachsum)可以遍历流以产生结果或副作用。在执行终端操作之后,流管道被认为已消耗,并且不能再使用。在几乎所有情况下,终端操作都很渴望,完成对底层数据源的遍历。

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

1 // Calculate total points of all tasks
2 final double totalPoints = tasks
3    .stream()
4    .parallel()
5    .map( task -> task.getPoints() ) // or map( Task::getPoints )
6    .reduce( 0, Integer::sum );
7     
8 System.out.println( "Total points (all tasks): " + totalPoints );

它与第一个示例非常相似,只是我们尝试并行处理所有任务并使用reduce方法计算最终结果。

这是控制台输出:

1 Total points (all tasks): 26.0

通常,需要通过某些标准对集合元素进行分组。Streams可以帮助解决这个问题,以及下面的示例演示。

1 // Group tasks by their status
2 final Map< Status, List< Task > > map = tasks
3     .stream()
4     .collect( Collectors.groupingBy( Task::getStatus ) );
5 System.out.println( map );

此示例的控制台输出如下所示:

1 {CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}

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

01 // Calculate the weight of each tasks (as percent of total points)
02 final Collection< String > result = tasks
03     .stream()                                        // Stream< String >
04     .mapToInt( Task::getPoints )                     // IntStream
05     .asLongStream()                                  // LongStream
06     .mapToDouble( points -> points / totalPoints )   // DoubleStream
07     .boxed()                                         // Stream< Double >
08     .mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream
09     .mapToObj( percentage -> percentage + "%" )      // Stream< String>
10     .collect( Collectors.toList() );                 // List< String >
11         
12 System.out.println( result );

控制台输出就在这里:

1 [19%, 50%, 30%]

最后,正如我们之前提到的,Stream API不仅仅是关于Java集合。逐行读取文本文件等典型的I / O操作是从流处理中受益的非常好的候选者。这是一个确认这一点的小例子。

1 final Path path = new File( filename ).toPath();
2 try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {
3     lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );
4 }

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

流API连同lambda表达式和方法引用的出炉接口的默认和静态方法是在软件开发了Java 8应对现代范式。有关更多详细信息,请参阅官方文档。

4.3 日期/时间API(JSR 310)

Java 8通过提供New Date-Time API(JSR 310)进行了更多的日期和时间管理。日期和时间操作是Java开发人员最痛苦的问题之一。标准的java.util.Date后跟java.util.Calendar并没有改善这种情况(可以说,使它更令人困惑)。

That is how Joda-Time was born: the great alternative date/time API for Java. The Java 8’s New Date-Time API (JSR 310)was heavily influenced by Joda-Time and took the best of it. The new java.time package contains all the classes for date, time, date/time, time zones, instants, duration, and clocks manipulation. In the design of the API the immutability has been taken into account very seriously: no change allowed (the tough lesson learnt from java.util.Calendar). If the modification is required, the new instance of respective class will be returned.

Let us take a look on key classes and examples of their usages. The first class is Clock which provides access to the current instant, date and time using a time-zone. Clock can be used instead of System.currentTimeMillis() and TimeZone.getDefault().

1 // Get the system clock as UTC offset
2 final Clock clock = Clock.systemUTC();
3 System.out.println( clock.instant() );
4 System.out.println( clock.millis() );

The sample output on a console:

1 2014-04-12T15:19:29.282Z
2 1397315969360

我们将要看的其他新类是LocaleDateLocalTimeLocaleDate仅保留ISO-8601日历系统中没有时区的日期部分。LocaleTime分别保留ISO- 8601日历系统中的时间 Zone。无论LocaleDateLocaleTime可以从创建时钟

01 // Get the local date and local time
02 final LocalDate date = LocalDate.now();
03 final LocalDate dateFromClock = LocalDate.now( clock );
04         
05 System.out.println( date );
06 System.out.println( dateFromClock );
07         
08 // Get the local date and local time
09 final LocalTime time = LocalTime.now();
10 final LocalTime timeFromClock = LocalTime.now( clock );
11         
12 System.out.println( time );
13 System.out.println( timeFromClock );

控制台上的示例输出:

1 2014-04-12
2 2014-04-12
3 11:25:54.568
4 15:25:54.568

LocalDateTime结合在一起LocaleDate本地时间,并拥有一个日期时间,但没有在ISO-8601日历系统的时区。一个简单的例子如下所示。

1 // Get the local date/time
2 final LocalDateTime datetime = LocalDateTime.now();
3 final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );
4         
5 System.out.println( datetime );
6 System.out.println( datetimeFromClock );

控制台上的示例输出:

1 2014-04-12T11:37:52.309
2 2014-04-12T15:37:52.309

如果您需要特定时区的日期/时间,ZonedDateTime可以提供帮助。它在ISO-8601日历系统中保存带有时间和时区的日期。以下是不同时区的几个示例。

1 // Get the zoned date/time
2 final ZonedDateTime zonedDatetime = ZonedDateTime.now();
3 final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
4 final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );
5         
6 System.out.println( zonedDatetime );
7 System.out.println( zonedDatetimeFromClock );
8 System.out.println( zonedDatetimeFromZone );

控制台上的示例输出:

1 2014-04-12T11:47:01.017-04:00[America/New_York]
2 2014-04-12T15:47:01.017Z
3 2014-04-12T08:47:01.017-07:00[America/Los_Angeles]

最后,让我们看一下持续时间类:以秒和纳秒为单位的时间量。它可以很容易地计算两个日期之间的差异。让我们来看看。

1 // Get duration between two dates
2 final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16000 );
3 final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16235959 );
4
5 final Duration duration = Duration.between( from, to );
6 System.out.println( "Duration in days: " + duration.toDays() );
7 System.out.println( "Duration in hours: " + duration.toHours() );

上面的示例计算2014 年4月16日2015年4月16日两个日期之间的持续时间(以天和小时为单位)。以下是控制台上的示例输出:

1 Duration in days: 365
2 Duration in hours: 8783

关于Java 8的新日期/时间API的整体印象是非常非常积极的。部分原因是,由于经过实战证明的基础(Joda-Time),部分原因在于它最终得到了认真的处理并且听到了开发人员的声音。有关详细信息,请参阅官方文档。

4.4 Nashorn JavaScript引擎

Java 8附带了新的Nashorn JavaScript引擎,允许在JVM上开发和运行某些类型的JavaScript应用程序。Nashorn JavaScript引擎只是javax.script.ScriptEngine的另一个实现,遵循相同的规则集,允许Java和JavaScript互操作性。这是一个小例子。

1 ScriptEngineManager manager = new ScriptEngineManager();
2 ScriptEngine engine = manager.getEngineByName( "JavaScript" );
3         
4 System.out.println( engine.getClass().getName() );
5 System.out.println( "Result:" + engine.eval( "function f() { return 1; }; f() + 1;") );

控制台上的示例输出:

1 jdk.nashorn.api.scripting.NashornScriptEngine
2 Result: 2

稍后我们将在专用于新Java工具的部分中回到Nashorn 。

4.5。Base64编码

最后,Base64编码的支持已经进入Java 8标准库,并发布了Java 8。它非常容易使用,如以下示例所示。

01 package com.javacodegeeks.java8.base64;
02
03 import java.nio.charset.StandardCharsets;
04 import java.util.Base64;
05
06 public class Base64s {
07     public static void main(String[] args) {
08         final String text = "Base64 finally in Java 8!";
09         
10         final String encoded = Base64
11             .getEncoder()
12             .encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
13         System.out.println( encoded );
14         
15         final String decoded = new String(
16             Base64.getDecoder().decode( encoded ),
17             StandardCharsets.UTF_8 );
18         System.out.println( decoded );
19     }
20 }

程序运行的控制台输出显示编码和解码文本:

1 QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==
2 Base64 finally in Java 8!

也有由类的Base64提供URL友好编码器/解码器和MIME型编码器/解码器(Base64的。getUrlEncoder() / Base64的。getUrlDecoder() Base64的。getMimeEncoder() / Base64的。getMimeDecoder() )。

4.6。并行数组

Java 8版本添加了许多新方法以允许并行数组处理。可以说,最重要的是parallelSort(),它可以显着加速多核机器上的排序。以下小例子演示了这个新方法系列(parallelXxx)的实际应用。

01 package com.javacodegeeks.java8.parallel.arrays;
02
03 import java.util.Arrays;
04 import java.util.concurrent.ThreadLocalRandom;
05
06 public class ParallelArrays {
07     public static void main( String[] args ) {
08         long[] arrayOfLong = new long 20000 ];       
09         
10         Arrays.parallelSetAll( arrayOfLong,
11             index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
12         Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
13             i -> System.out.print( i + " " ) );
14         System.out.println();
15         
16         Arrays.parallelSort( arrayOfLong );    
17         Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
18             i -> System.out.print( i + " " ) );
19         System.out.println();
20     }
21 }

这个小代码片段使用方法parallelSetAll()来填充具有20000个随机值的数组。之后,正在应用parallelSort()。程序在排序之前和之后输出前10个元素,以确保数组真正有序。示例程序输出可能看起来像那样(请注意,数组元素是随机生成的):

1 Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378
2 Sorted: 39 220 263 268 325 607 655 678 723 793

4.7 并发

java.util.concurrent.ConcurrentHashMap类中添加了新方法,以支持基于新添加的流工具和lambda表达式的聚合操作。此外,java.util.concurrent.ForkJoinPool类中添加了新方法以支持公共池(另请参阅我们关于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

1 function f() {
2      return 1;
3 };
4
5 print( f() + 1 );

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

1 jjs func.js

控制台上的输出将是:

1 2

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

5.2 类依赖性分析器:jdeps

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

作为示例,让我们看一下流行的Spring Framework库的依赖关系报告。简而言之,让我们只分析一个JAR文件:org.springframework.core-3.0.5.RELEASE.jar

1 jdeps org.springframework.core-3.0.5.RELEASE.jar

这个命令输出了很多,所以我们将看看它的一部分。依赖项按包分组。如果类路径上没有依赖项,则显示为未找到

01 org.springframework.core-3.0.5.RELEASE.jar -> C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar
02    org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)
03       -> java.io                                           
04       -> java.lang                                         
05       -> java.lang.annotation                              
06       -> java.lang.ref                                     
07       -> java.lang.reflect                                 
08       -> java.util                                         
09       -> java.util.concurrent                              
10       -> org.apache.commons.logging                         not found
11       -> org.springframework.asm                            not found
12       -> org.springframework.asm.commons                    not found
13    org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)
14       -> java.lang                                         
15       -> java.lang.annotation                              
16       -> java.lang.reflect                                 
17       -> java.util

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

6. Java运行时(JVM)中的新功能

所述的PermGen空间不见了,已被替换为元空间(JEP 122)。JVM选项-XX:PermSize-XX:MaxPermSize已分别替换为-XX:MetaSpaceSize-XX:MaxMetaspaceSize

 

7.结论

未来就在这里:Java 8通过提供功能来提升开发人员的工作效率,从而推动这一伟大的平台向前发展。现在将生产系统转移到Java 8还为时过早,但在接下来的几个月里,它的采用应该慢慢开始增长。尽管如此,现在是时候开始准备你的代码库以便与Java 8兼容,并准备好在Java 8被证明足够安全和​​稳定时转换开关。

作为社区Java 8接受的确认,最近Pivotal发布了Spring Framework 4.0.3,支持生产就绪Java 8。

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

欢迎您对有关令人兴奋的新Java 8功能的评论做出贡献!

 

8.资源

一些额外的资源深入讨论了Java 8功能的不同方面:

  • 关于JCG的Java 8教程示例:https://examples.javacodegeeks.com/ ? s = java + 8
  • JDK 8中的新功能:http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html
  • Java教程: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教程:http://winterbe.com/posts/2014/03/16/java-8-tutorial/
  • JDK 8命令行静态依赖项检查程序:http://marxsoftware.blogspot.ca/2014/03/jdeps.html
  • JDK 8的Illogating Javadoc:http://marxsoftware.blogspot.ca/2014/03/illuminating-javadoc-of-jdk-8.html
  • Java 8的黑暗面:http://blog.jooq.org/2014/04/04/java-8-friday-the-dark-side-of-java-8/
  • 在Eclipse Kepler SR2中安装Java™8支持:http://www.eclipse.org/downloads/java8/
  • Java 8:http://www.baeldung.com/java8
  • Oracle Nashorn。JVM的下一代JavaScript引擎:http://www.oracle.com/technetwork/articles/java/jf14-nashorn-2126515.html

你可能感兴趣的:(Unix&Unix-Like,Java,J2EE)