java8新特性

本文参考自官方文档What’s New in JDK 8,内容有所筛选!

目录

  • Java Programming Language
    • Lambda Expressions(Lambda表达式)
    • Method references (方法引用)
    • Default methods (默认方法)
    • Repeating Annotations (重复注释)
    • Improved type inference(更好的类型推断)
    • Method parameter reflection(方法参数反射)
  • Collections(集合)
  • Tool(工具)
    • Javac tool
    • Javadoc tool
  • Internationalization(国际化)
  • Date-Time Package (日期时间)
  • IO and NIO
  • java.lang and java.util Packages
    • Parallel Array Sorting(并行数组排序)
    • Standard Encoding and Decoding Base64(标准编码和解码Base64)
    • Unsigned Arithmetic Support(无符号算术支持)
  • JDBC
    • The JDBC-ODBC Bridge has been removed.
    • JDBC 4.2 introduces new features.
  • Concurrency(并发)
  • HotSpot

正文

Java编程语言

  • Lambda表达式

    Lambda表达式是一种新的语言功能,已在此版本中引入。它们使您能够将功能视为方法参数,或将代码视为数据。使用Lambda表达式可以更简洁地表达单方法接口(称为函数接口)的实例。

    Lambda表达式可由逗号分隔的参数列表、->符号和语句块组成(其中参数列表中的参数e的类型可以由编译器推理得出的,也可以显式指定该参数的类型),例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //(params) -> expression
    //(params) -> statement
    //(params) -> { statements }
    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 );
    } );

    Lambda表达式可以引用类成员和局部变量(会将这些变量隐式得转换成final),但是不能在lambda内部修改定义在域外的变量。同时Lambda表达式有返回值,返回值的类型也由编译器推理得出。如果Lambda表达式中的语句块只有一行,则可以不用使用return语句,例如

    1
    2
    3
    4
    5
    6
    7
    8
    (final) String separator = ",";
    Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.print( e + separator ) );
    //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表达式。java.lang.Runnable和java.util.concurrent.Callable是函数式接口的最佳例子。在实践中,函数式接口非常脆弱:只要某个开发者在该接口中添加一个函数,则该接口就不再是函数式接口进而导致编译失败。为了克服这种代码层面的脆弱性,并显式说明某个接口是函数式接口,Java 8 提供了一个特殊的注解@FunctionalInterface(Java 库中的所有相关接口都已经带有这个注解了),举个简单的函数式接口的定义:

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

    不过有一点需要注意,默认方法和静态方法不会破坏函数式接口的定义,因此如下的代码是合法的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public interface FunctionalDefaultMethods {
    void method();
    default void defaultMethod() {
    }
    }
    public interface FunctionalStaticMethods {
    void method();
    Static void defaultMethod() {
    }
    }

    lambda表达式替换匿名类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // Java 8之前:
    new Thread(new Runnable() {
    @Override
    public void run() {
    System.out.println("Before Java8, too much code for too little to do");
    }
    }).start();
    //Java 8方式:
    new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();
    // Java 8之前:
    Collections.sort(list, new Comparator<Object>() {
    public int compare(Object arg0, Object arg1) {
    String s1 = (String)arg0;
    String s2 = (String)arg1;
    return s1.compareTo( s2 );
    }
    });
    //Java 8方式:
    Collections.sort(list,(arg0,arg1) -> {
    String s1 = (String)arg0;
    String s2 = (String)arg1;
    return s1.compareTo( s2 );
    });

    ####
    Stream stream()
    Stream filter(Predicate<? super T> predicate)
    Stream map(Function<? super T,? extends R> mapper)
    void forEach(Consumer<? super T> action)

    1
  • 方法引用

    方法引用为已有名称的方法提供易读的lambda表达式。lambda表达式内可以使用方法引用,仅当该方法不修改lambda表达式提供的参数。

    1. (引用静态方法)ContainingClass :: staticMethodName

      1
      Arrays.sort(rosterAsArray, MyComparisonProvider::staticCompare);
    2. (引用特定对象的实例方法)containsObject :: instanceMethodName

      1
      2
      //ComparisonProvider myComparisonProvider = new ComparisonProvider();
      Arrays.sort(rosterAsArray, myComparisonProvider::normalCompare);
    3. (引用特定类型任意对象的实例方法)ContainingType :: methodName

      1
      2
      3
      String[] stringArray = { "Barbara", "James", "Mary", "John",
      "Patricia", "Robert", "Michael", "Linda" };
      Arrays.sort(stringArray, String::compareToIgnoreCase);
    4. (引用一个构造函数)ClassName :: new

      1
      Set<Person> rosterSet = transferElements(roster, HashSet::new);
  • 默认方法

    默认方法可以将新功能添加到库的接口,并确保与为这些接口的旧版本编写的代码的二进制兼容性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public interface TimeClient {
    //通过接口直接调用该静态方法
    static void dispaly () {
    System.out.println("static");
    }
    //该接口的实现类不强制要求重写default方法,使得接口可以添加新功能而不影响其实现类
    default void dispaly() {
    System.out.println("default");
    }
    }
  • 重复注释

    重复注释提供了将同一注释类型多次应用于相同的声明或类型使用的功能。Java8之前的注解有一个很大的限制是:在同一个地方不能多次使用同一个注解。Java 8打破了这个限制,引入了重复注解的概念,允许在同一个地方多次使用同一个注解。在Java 8中使用@Repeatable注解定义重复注解,实际上,这并不是语言层面的改进,而是编译器做的一个trick,底层的技术仍然相同。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Schedule(dayOfMonth="last")
    @Schedule(dayOfWeek="Fri", hour="23")
    public void doPeriodicCleanup() { ... }
    //The annotation type must be marked with the @Repeatable meta-annotation
    @Repeatable(Schedules.class)
    public @interface Schedule {
    String dayOfMonth() default "first";
    String dayOfWeek() default "Mon";
    int hour() default 12;
    }
  • 更好的类型推断

    1
    2
    3
    4
    5
    6
    7
    8
    //Java 8方式:
    List<String> stringList = new ArrayList<>();
    stringList.add("A");
    stringList.addAll(Arrays.asList());
    //Java 8之前:
    List<String> stringList = new ArrayList<>();
    stringList.add("A");
    stringList.addAll(Arrays.<String>asList());
  • 方法参数反射

    您可以使用方法java.lang.reflect.Executable.getParameters获取任何方法或构造函数的形式参数的名称。 (类Method和构造函数扩展了类Executable,因此继承了方法Executable.getParameters。)但是,.class文件默认不存储形式参数名称。 要将正式参数名称存储在特定的.class文件中,从而使Reflection API能够检索正式的参数名称,请使用javac编译器的-parameters选项编译源文件。

    1
    2
    3
    4
    5
    6
    7
    8
    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() );
    }
    }
    }

集合

  • 新的java.util.stream包中的类提供了一个Stream API来支持元素流上的函数式操作。 Stream API集成到Collections API中,可以对集合进行批量操作,例如顺序或并行的map-reduce转换。

    java.util.stream.Stream部分方法如下

    • Stream filter(Predicate<? super T> predicate)(过滤)
    • Stream map(Function<? super T,? extends R> mapper)(转换)
    • IntStream mapToInt(ToIntFunction<? super T> mapper)
    • Stream distinct()(返回由此流的不同元素,去重)
    • Stream sorted()(默认排序)
    • Stream sorted(Comparator<? super T> comparator)(排序)
    • Stream peek(Consumer<? super T> action)

      1
      2
      3
      4
      5
      6
      7
      //This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline:
      Stream.of("one", "two", "three", "four")
      .filter(e -> e.length() > 3)
      .peek(e -> System.out.println("Filtered value: " + e))
      .map(String::toUpperCase)
      .peek(e -> System.out.println("Mapped value: " + e))
      .collect(Collectors.toList());
    • Stream limit(long maxSize)(限制)

    • Stream skip(long n)(跳过前n个元素)
    • void forEach(Consumer<? super T> action)(迭代)
    • void forEachOrdered(Consumer<? super T> action)
    • Object[] toArray()(转换成数组)

      1
      Person[] men = people.stream().filter(p -> p.getGender() == MALE).toArray(Person[]::new);
    • T reduce(T identity,BinaryOperator accumulator)(合并)

    • Optional reduce(BinaryOperator accumulator)
    • U reduce(U identity,BiFunction accumulator,BinaryOperator combiner)
    • R collect(Supplier supplier,BiConsumer accumulator,BiConsumer combiner)

      1
      2
      3
      4
      //The following will accumulate strings into an ArrayList:
      List<String> asList = stringStream.collect(ArrayList::new, ArrayList::add,ArrayList::addAll);
      //The following will take a stream of strings and concatenates them into a single string:
      String concat = stringStream.collect(StringBuilder::new, StringBuilder::append,StringBuilder::append).toString();
    • R collect(Collector<? super T,A,R> collector)

      1
      2
      3
      4
      5
      6
      //The following will accumulate strings into an ArrayList:
      List<String> asList = stringStream.collect(Collectors.toList());
      //The following will classify Person objects by city:按照城市分组
      Map<String, List<Person>>peopleByCity= personStream.collect(Collectors.groupingBy(Person::getCity));
      //The following will classify Person objects by state and city, cascading two Collectors together:按照国家和城市两项进行分组
      Map<String, Map<String, List<Person>>> peopleByStateAndCity= personStream.collect(Collectors.groupingBy(Person::getState,Collectors.groupingBy(Person::getCity)));
    • Optional min(Comparator<? super T> comparator)(最小值)

    • Optional max(Comparator<? super T> comparator)(最大值)
    • long count()(计数)
    • boolean anyMatch(Predicate<? super T> predicate)(匹配)
    • boolean allMatch(Predicate<? super T> predicate)
    • boolean noneMatch(Predicate<? super T> predicate)
    • Optional findFirst()
    • Optional findAny()
    • static Stream empty()
    • static Stream of(T t)(返回包含单个元素的顺序流。)
    • static Stream of(T… values)(返回顺序排列的流,其元素是指定的值。)
    • static Stream concat(Stream<? extends T> a,Stream<? extends T> b)(连接)
    1. forEach对列表进行迭代

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // Java 8之前:
      List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
      for (String feature : features) {
      System.out.println(feature);
      }
      // Java 8之后:
      List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
      features.forEach(n -> System.out.println(n));
      // 使用Java 8的方法引用更方便
      features.forEach(System.out::println);
    2. map将对象进行转换,reduce结果合并

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      // Java 8之前:
      List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
      for (Integer cost : costBeforeTax) {
      double price = cost + .12*cost;
      System.out.println(price);
      }
      // Java 8之后:
      List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
      costBeforeTax.stream().map((cost) -> cost + .12*cost).forEach(System.out::println);
      // Java 8之前:
      List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
      double total = 0;
      for (Integer cost : costBeforeTax) {
      double price = cost + .12*cost;
      total = total + price;
      }
      System.out.println("Total : " + total);
      // Java 8之后:
      List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
      double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get();
      System.out.println("Total : " + bill);
    3. filter元素过滤

      1
      2
      3
      // 创建一个字符串列表,每个字符串长度大于2
      List<String> filtered = strList.stream().filter(x -> x.length()> 2).collect(Collectors.toList());
      System.out.printf("Original List : %s, filtered list : %s %n", strList, filtered);
  • 具有关键冲突的HashMap性能改进

工具

  • Javac工具
    javac命令的-parameters选项可用于存储形式参数名称并使Reflection API检索正式参数名称。
    Java语言规范(JLS)第15.21节中的相等运算符的类型规则现在可以通过javac命令正确执行。
    
  • Javadoc工具
    javadoc工具支持新的DocTree API,使您能够将Javadoc注释作为抽象语法树来遍历。
    javadoc工具支持新的Javadoc Access API,使您可以直接从Java应用程序调用Javadoc工具,而无需执行新的过程。有关更多信息,请参阅javadoc什么是新页面。
    现在,javadoc工具支持检查javadoc注释的内容,以解决可能导致运行javadoc时生成的文件中的各种问题(例如无效的HTML或可访问性问题)的问题。该功能默认启用,也可以通过新的-Xdoclint选项进行控制。有关更多详细信息,请参阅运行“javadoc -X”的输出。在javac工具中也可以使用此功能,但默认情况下它未启用。
    

国际化

  • Unicode增强功能,包括对Unicode 6.2.0的支持
  • 采用Unicode CLDR数据和java.locale.providers系统属性
  • 新的日历和区域设置API
  • 能够将自定义资源包安装为扩展
  • 日期时间包 - 一组提供全面日期 - 时间模型的新包。

IO和NIO

  • 基于Solaris事件端口机制的Solaris新增SelectorProvider实现。要使用,使用设置为值sun.nio.ch.EventPortSelectorProvider的系统属性java.nio.channels.spi.Selector运行。
  • 减小 /jre/lib/charsets.jar文件的大小
  • 提高了java.lang.String(byte [],*)构造函数和java.lang.String.getBytes()方法的性能。

java.lang和java.util包

  • 并行数组排序
  • 标准编码和解码Base64
  • 无符号算术支持

JDBC

- JDBC-ODBC Bridge已被删除。
- JDBC 4.2引入了新功能。

并发

  • 类和接口已被添加到java.util.concurrent包中。
  • 已经将方法添加到java.util.concurrent.ConcurrentHashMap类中,以支持基于新添加的流设施和lambda表达式的聚合操作。
  • 已将类添加到java.util.concurrent.atomic包以支持可伸缩的可更新变量。
  • 方法已被添加到java.util.concurrent.ForkJoinPool类中以支持公共池。
  • 已添加java.util.concurrent.locks.StampedLock类以提供基于能力的锁,其中有三种控制读/写访问的模式。

HotSpot

  • 硬件内在函数被添加到使用高级加密标准(AES)。 UseAES和UseAESIntrinsics标志可用于启用英特尔硬件的基于硬件的AES内在函数。硬件必须是2010年或更新的Westmere硬件。例如,要启用硬件AES,请使用以下标志:-XX:+ UseAES -XX:+ UseAESIntrinsics,要禁用硬件AES,请使用以下标志:-XX:-UseAES -XX:-UseAESIntrinsics
  • 去除PermGen。
  • Java编程语言中的缺省方法由方法调用的字节码指令支持。