Java 8新特性终极指南,java新特性终极指南
分享于 点击 49979 次 点评:140
Java 8新特性终极指南,java新特性终极指南
http://www.importnew.com/11908.html
http://blog.csdn.net/rongbo_j/article/details/49644689
[-]
- 前言
- Java语言中的新特性
- Java编译器新特性
- Java库的新特性
- 新的Java工具
- JVM新特性
- 相关资源
1.前言
毫无疑问,Java 8的发布是自从Java5以来Java世界中最重大的事件,它在编译器、工具类和Java虚拟机等方面为Java语言带来的很多新特性。在本文中我们將一起关注下这些新变化,使用实际的例子展示它们的使用场景。 本教程涉及到一下几个部分的内容:- 语法规范
- 编译器
- 类库
- 工具
- Java虚拟机
2.Java语言中的新特性
Java 8 是一个主发行版本,为了实现每一位Java开发人员所期待的特性而花费了很长时间,本节將会涉及到Java 8 中的大部分新特性。2.1.Lambda表达式和函数式编程接口
Lambda表达式是整个Java 8体系中最让人期待的特性,它允许我们將函数作为一个方法的参数传递到方法体中或者將一段代码作为数据,这些概念有过函数式编程经验的会比较熟悉,很多基于Java虚拟机平台的语言(Groovy、Scala...)都引入了Lambda表达式。
Lambda表达式的设计研讨,相关社区贡献的巨大的时间和精力,最后进行了相应的取舍,形成了简洁而紧凑的语法结构。在它最简单的形式中,可以使用逗号分割参数列表,使用->符号,例如:
- Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );
- Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );
- Arrays.asList( "a", "b", "d" ).forEach( e -> {
- System.out.print( e );
- System.out.print( e );
- } );
- String separator = ",";
- Arrays.asList( "a", "b", "d" ).forEach(
- ( String e ) -> System.out.print( e + separator ) );
[java] view plain copy print?
- final String separator = ",";
- Arrays.asList( "a", "b", "d" ).forEach(
- ( String e ) -> System.out.print( e + separator ) );
- 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.Runnable和java.util.concurrent.Callable就是两个很好的函数式接口的例子。在编程实践中,函数式接口是相当容易被破坏的,如果有人在接口的定义中添加了另外一个方法,它將不再是函数式接口而且编译过程可能会失败。为了解决这种易破坏性并且显式的声明该接口属于函数式接口,Java 8新增了一个特殊的注解@FunctionalInterface,我们来看一下一个简单的函数式接口的定义:
[java] view plain copy print?
- @FunctionalInterface
- public interface Functional {
- void method();
- }
[java] view plain copy print?
- @FunctionalInterface
- public interface FunctionalDefaultMethods {
- void method();
- default void defaultMethod() {
- }
- }
2.2.接口的default和static方法
Java 8 扩展了接口的声明,引入了两个新的概念default和static方法,default 方法使得接口有些相似的特征但又服务于不同的目标。它允许在不打破旧版本接口二进制兼容性的前提下添加新的方法。 default方法和abstract修饰的方法的不同在于,abstract方法在子类中必须实现它,但是default方法不需要。相反,每个接口中必须提供default方法默认的实现并且所有接口的实现类都会默认继承它(如果需要可以覆盖这个默认实现)。让我们看看下面的例子: [java] view plain copy print?- 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";
- }
- }
- 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
2.3.方法引用(Method References)
方法引用提供了一种非常有用的语法去直接引用类或对象的方法(或构造函数)。与lambda表达式结合使用,方法引用使语言结构看起来简洁紧凑。 下面的例子中,Car类中定义了一些不同的方法,我们一起看一下Java8支持的四种不同类型的方法引用。 [java] view plain copy print?- public static 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() );
- }
- }
- final Car car = Car.create( Car::new );
- final List< Car > cars = Arrays.asList( car );
[java] view plain copy print?
- cars.forEach( Car::collide );
- cars.forEach( Car::repair );
- final Car police = Car.create( Car::new );
- cars.forEach( police::follow );
- 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
2.4.重复注解(Repeating annotations)
自从Java 5 引入注解以来,这个特性变得非常流行并且被广泛使用。然而,注解的一个使用限制是,在同一个位置,相同的注解不能声明两次。Java 8解除了这个规则并且引入了重复注解的概念,它允许相同的注解在同一个位置声明多次。
重复注解本身在定义时需要使用@Repeatable注解修饰。实际上,这个并不算是语言的改变,而是一个编译器欺骗,底层技术保持不变。我们来看一个案例:
[java] view
plain copy
print?
- 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() );
- }
- }
- }
- filter1
- filter2
2.5.更佳的类型引用
Java 8编译器在类型引用上做了很大的改进。在很多情况下,,显式的类型参数可以由编译器推断以保持代码的简洁,我们来看一下下面的例子。 [java] view plain copy print?- public class Value< T > {
- public static< T > T defaultValue() {
- return null;
- }
- public T getOrDefault( T value, T defaultValue ) {
- return ( value != null ) ? value : defaultValue;
- }
- }
- public class TypeInference {
- public static void main(String[] args) {
- final Value< String > value = new Value<>();
- value.getOrDefault( "22", Value.defaultValue() );
- }
- }
2.6.扩展注解支持
Java 8 扩展了注解的使用场景。现在,它几乎可以对Java中任何元素进行注解:局部变量、泛型、超类、甚至是函数的异常声明。一些使用案例如下: [java] view plain copy print?- 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<>();
- }
- }
3.Java编译器新特性
3.1.参数名
资深的Java开发人员发明使用不同的方式在Java字节码中保存方法参数名,以便在运行时使用。 例如paranamer 库,地址:https://github.com/paul-hammant/paranamer 最终,Java 8在语言(使用反射Api和Parameter.getName()方法)和字节码(使用新的javac编译器的-parameters参数)中增加了这一特性[java] view plain copy print?
- 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() );
- }
- }
- }
- Parameter: arg0
- Parameter: args
- <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>
此外,验证参数名称的可用性,可以使用Parameter类的isNamePresent()方法。
4.Java库的新特性
为了更好的支持现代并发、函数式编程、时间日期等,Java 8新增一些新的类,并且扩展了一些现有的类。4.1.Optional
著名的空指针异常(NullPointerException)是迄今为止最流行的导致Java应用程序失败的原因。很久以前,伟大的Google Guava项目提供了Optional去解决空指针异常,冗余的检查null的代码,鼓励着程序员写更加简洁的代码。受到Google Guava的启示,Optional成为Java 8 库的一部分。 Optional只是一个容器而已,它能够持有某种类型T的值或者null。它提供了一些非常有用的方法明确的避开null的检查。请查看Java 8官方文档来了解相关细节。我们將会一起看一下Optional的使用案例:可以为空的引用,但实际使用中不能让它为空 [java] view plain copy print?
- 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!" ) );
- 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.流(Streams)
新增的Stream API(java.util.stream)將实际的函数式编程引入到Java中。这是迄今为止最全面的新增Java库,目的是让程序员使用它们编写更有效、干净、简洁的代码。Stream API使得集合处理更加简单。让我们从下面的Task类开始: [java] view plain copy print?- 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 );
- }
- }
- }
- final Collection< Task > tasks = Arrays.asList(
- new Task( Status.OPEN, 5 ),
- new Task( Status.OPEN, 13 ),
- new Task( Status.CLOSED, 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
stream的另一个价值是并行操作,我们来看下面一个计算所有Task对象points之和的例子: [java] view plain copy print?
- // 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 );
- 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 * 100 ) ) // LongStream
- .mapToObj( percentage -> percentage + "%" ) // Stream< String>
- .collect( Collectors.toList() ); // List< String >
- System.out.println( result );
- [19%, 50%, 30%]
- 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 );
- }
4.3.日期/时间 API(JSR310)
Java 8提供新的时间-日期API(JSR310),使人更容易接收日期和时间的管理方式。操作时间和日期是Java开发人员最为糟糕的一个痛点。 标准的java.util.Date和java.util.Calendar没有变动。 这就是为什么Joda-Time诞生:Java中可供选择的时间/日期API。Java 8新的日期-时间API深受Joda-Time的影响并采取了最好的。新的java.time包中包含所有日期、时间、时区和时钟操作的类。不变性的API的设计已经考虑非常认真:不允许改变(从java.util.Calendar中吸取教训)。如果需要修改,將返回各自类的新实例。 让我们看一看关键类和它们的使用案例。第一个类是Clock,它能够使用根据时区获取当前时间和日期。Clock能够取代System.currentTimeMilis()和TimeZone.getDefault()。 [java] view plain copy print?- // 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
- // 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
- // 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
这里有几个使用不同时区的例子: [java] view plain copy print?
- // 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]
- // 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() );
- Duration in days: 365
- Duration in hours: 8783
4.4.犀牛JavaScript引擎
Java 8 新增了犀牛JavaScript引擎,运行在JVM中运行特定的JavaScript程序。犀牛JavaScript引擎是javax.script.ScriptEngine的另外一个实现,遵循相同的规则集,允许Java和JavaScript相互操作。下面是一个使用案例: [java] view plain copy print?- 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
4.5.Base64编码
最后,Java 8发行版的标准库中新增了Base64编码支持,它的使用较为简单,例如: [java] view plain copy print?- 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!
4.6.并行的数组工具类Arrays(Parallel Arrays)
Java 8发行版新增了一些新的方法,允许并行的数组处理,其中最重要的一个是parallelSort()方法,在多核处理器上能显著的提高数组排序速度。下面的例子演示了新的parallelXxx系列方法的使用: [java] view plain copy print?- 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();
- }
- }
- 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类也增加了方法以支持线程池。增加了用于控制读写加锁操作的类java.util.concurrent.locks.StampedLock,和java.util.concurrent.locks.ReadWriteLock相比会是更好的选择。 java.util.concurrent.atomic包下新增如下几个类:
- DoubleAccumulator
- DoubleAdder
- LongAccumulator
- LongAdder
5.新的Java工具
Java 8中新增了一些命令行工具,在本节中我们来看一下它们的有趣之处。5.1.犀牛引擎:jjs
jjs是一个基于犀牛引擎的命令行工具,它接收一个JavaScript源文件作为参数,然后运行它们。例如,我们创建一个func.js文件,内容如下: [java] view plain copy print?- function f() {
- return 1;
- };
- print( f() + 1 );
- 2
5.2.类依赖关系分析工具:jdeps
jdeps是一个非常优秀的控制台工具,能够显示Java class文件类层级和包层级的依赖关系。它接收.class文件、目录、jar文件作为参数,输出依赖关系到控制台。 例如,我们使用它分析spring框架中core模块,在控制台输入: [java] view plain copy print?- jdeps org.springframework.core-3.0.5.RELEASE.jar
- 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.JVM新特性
垃圾回收机制中的永久带(PermGen)被元空间(Metaspace)取代。JVM参数-XX:PermSize和-XX:MaxPermSize被替换为-XX:MetaSpaceSize和-XX:MaxMetaspaceSize。7.相关资源
- 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
相关文章
- 暂无相关文章
用户点评