java8新特性简述

Java8发布时间是2014年3月19日,距离今日已经很久了,那么Java8新特性你了解吗?

java8是Java的一次重大升级,巨大的里程碑式的改进!!

Java语言新特性:

1.与传统结合 -- Lambda(闭包)表达式和函数式接口以及注解

注意:要能写成Lambda 其对象类一定支持函数式接口的规范,这个规范是jdk检验的,那么什么是函数式接口的规范,原理是什么???

叙说背景和原理:

随着回调模式和函数式编程风格的日益流行,我们需要在Java中提供一种尽可能轻量级的将代码封装为数据(Model code as data)的方法。匿名内部类并不是一个好的选择,因为:

  1. 语法过于冗余
  2. 匿名类中的this和变量名容易使人产生误解
  3. 类型载入和实例创建语义不够灵活
  4. 无法捕获非final的局部变量
  5. 无法对控制流进行抽象

上面的多数问题均在Java SE 8中得以解决:

  • 通过提供更简洁的语法和局部作用域规则,Java SE 8彻底解决了问题1和问题2
  • 通过提供更加灵活而且便于优化的表达式语义,Java SE 8绕开了问题3
  • 通过允许编译器推断变量的“常量性”(finality),Java SE 8减轻了问题4带来的困扰

不过,Java SE 8的目标并非解决所有上述问题。因此捕获可变变量(问题4)和非局部控制流(问题5)并不在Java SE 8的范畴之内。(尽管我们可能会在未来提供对这些特性的支持)

尽管匿名内部类有着种种限制和问题,但是它有一个良好的特性,它和Java类型系统结合的十分紧密:每一个函数对象都对应一个接口类型。之所以说这个特性是良好的,是因为:

  • 接口是Java类型系统的一部分
  • 接口天然就拥有其运行时表示(Runtime representation)
  • 接口可以通过Javadoc注释来表达一些非正式的协定(contract),例如,通过注释说明该操作应可交换(commutative)

上面提到的ActionListener接口只有一个方法,大多数回调接口都拥有这个特征:比如Runnable接口和Comparator接口。我们把这些只拥有一个方法的接口称为函数式接口。(之前它们被称为SAM类型,即单抽象方法类型(Single Abstract Method))

我们并不需要额外的工作来声明一个接口是函数式接口:编译器会根据接口的结构自行判断(判断过程并非简单的对接口方法计数:一个接口可能冗余的定义了一个Object已经提供的方法,比如toString(),或者定义了静态方法或默认方法,这些都不属于函数式接口方法的范畴)。不过API作者们可以通过@FunctionalInterface注解来显式指定一个接口是函数式接口(以避免无意声明了一个符合函数式标准的接口),加上这个注解之后,编译器就会验证该接口是否满足函数式接口的要求。

实现函数式类型的另一种方式是引入一个全新的结构化函数类型,我们也称其为“箭头”类型。例如,一个接收StringObject并返回int的函数类型可以被表示为(String, Object) -> int。我们仔细考虑了这个方式,但出于下面的原因,最终将其否定:

  • 它会为Java类型系统引入额外的复杂度,并带来结构类型(Structural Type)指名类型(Nominal Type)的混用。(Java几乎全部使用指名类型)
  • 它会导致类库风格的分歧——一些类库会继续使用回调接口,而另一些类库会使用结构化函数类型
  • 它的语法会变得十分笨拙,尤其在包含受检异常(checked exception)之后
  • 每个函数类型很难拥有其运行时表示,这意味着开发者会受到类型擦除(erasure)的困扰和局限。比如说,我们无法对方法m(T->U)m(X->Y)进行重载(Overload)
java7中支持的函数式接口:

  • java.lang.Runnable
  • java.util.concurrent.Callable
  • java.security.PrivilegedAction
  • java.util.Comparator
  • java.io.FileFilter
  • java.nio.file.PathMatcher
  • java.lang.reflect.InvocationHandler
  • java.beans.PropertyChangeListener
  • java.awt.event.ActionListener
  • javax.swing.event.ChangeListener
Java 8中新增加接口:

JDK 1.8 新增加的函数接口:

  • java.util.function

java.util.function 它包含了很多类,用来支持 Java的 函数式编程,该包中的函数式接口有:

序号 接口 & 描述
1 BiConsumer<T,U>

代表了一个接受两个输入参数的操作,并且不返回任何结果

2 BiFunction<T,U,R>

代表了一个接受两个输入参数的方法,并且返回一个结果

3 BinaryOperator<T>

代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果

4 BiPredicate<T,U>

代表了一个两个参数的boolean值方法

5 BooleanSupplier

代表了boolean值结果的提供方

6 Consumer<T>

代表了接受一个输入参数并且无返回的操作

7 DoubleBinaryOperator

代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。

8 DoubleConsumer

代表一个接受double值参数的操作,并且不返回结果。

9 DoubleFunction<R>

代表接受一个double值参数的方法,并且返回结果

10 DoublePredicate

代表一个拥有double值参数的boolean值方法

11 DoubleSupplier

代表一个double值结构的提供方

12 DoubleToIntFunction

接受一个double类型输入,返回一个int类型结果。

13 DoubleToLongFunction

接受一个double类型输入,返回一个long类型结果

14 DoubleUnaryOperator

接受一个参数同为类型double,返回值类型也为double 。

15 Function<T,R>

接受一个输入参数,返回一个结果。

16 IntBinaryOperator

接受两个参数同为类型int,返回值类型也为int 。

17 IntConsumer

接受一个int类型的输入参数,无返回值 。

18 IntFunction<R>

接受一个int类型输入参数,返回一个结果 。

19 IntPredicate

:接受一个int输入参数,返回一个布尔值的结果。

20 IntSupplier

无参数,返回一个int类型结果。

21 IntToDoubleFunction

接受一个int类型输入,返回一个double类型结果 。

22 IntToLongFunction

接受一个int类型输入,返回一个long类型结果。

23 IntUnaryOperator

接受一个参数同为类型int,返回值类型也为int 。

24 LongBinaryOperator

接受两个参数同为类型long,返回值类型也为long。

25 LongConsumer

接受一个long类型的输入参数,无返回值。

26 LongFunction<R>

接受一个long类型输入参数,返回一个结果。

27 LongPredicate

R接受一个long输入参数,返回一个布尔值类型结果。

28 LongSupplier

无参数,返回一个结果long类型的值。

29 LongToDoubleFunction

接受一个long类型输入,返回一个double类型结果。

30 LongToIntFunction

接受一个long类型输入,返回一个int类型结果。

31 LongUnaryOperator

接受一个参数同为类型long,返回值类型也为long。

32 ObjDoubleConsumer<T>

接受一个object类型和一个double类型的输入参数,无返回值。

33 ObjIntConsumer<T>

接受一个object类型和一个int类型的输入参数,无返回值。

34 ObjLongConsumer<T>

接受一个object类型和一个long类型的输入参数,无返回值。

35 Predicate<T>

接受一个输入参数,返回一个布尔值结果。

36 Supplier<T>

无参数,返回一个结果。

37 ToDoubleBiFunction<T,U>

接受两个输入参数,返回一个double类型结果

38 ToDoubleFunction<T>

接受一个输入参数,返回一个double类型结果

39 ToIntBiFunction<T,U>

接受两个输入参数,返回一个int类型结果。

40 ToIntFunction<T>

接受一个输入参数,返回一个int类型结果。

41 ToLongBiFunction<T,U>

接受两个输入参数,返回一个long类型结果。

42 ToLongFunction<T>

接受一个输入参数,返回一个long类型结果。

43 UnaryOperator<T>

接受一个参数为类型T,返回值类型也为T。

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表达式可以引用类成员和局部变量(会将这些变量隐式得转换成 final 的),例如下列两个代码块的效果完全相同:
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;
} );
上述的forEach(),sort() 就是一个Consumer<String>函数式接口,传入String值。

以上是函数式接口的lambda表达,接着看几个依据函数式接口的方法引用:

public 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,实例如下:

    cars . forEach ( Car :: collide ) ;
  • 特定类的任意对象的方法引用:它的语法是Class::method实例如下:

    cars . forEach ( Car :: repair ) ;
  • 特定对象的方法引用:它的语法是instance::method实例如下:

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

Java思想:

Java8 新增加上述的函数式接口是为了干什么?如果你在未实际开发中使用这些接口,而是在了解这些函数式接口就能体会到,那么你对Java的思想的理解还是挺到位的!

上述函数式接口无非就是传入,提供的操作(返回值不是强制的),注意:因为既然是函数式接口,那么必然是接口实现的过程,只是语法表达上使用函数式罢了。既然接口要么就是回调开始端传入参数或者对象,也就是“传入”。要么就是接口回调执行端执行操作之后返回对象(就是提供对象)或者返回值。

那么是不是有人问:传入值和返回值(对象),直接在函数式表达中声明不就可以了,其实这样也可以,只是iava8多了一层对传入和返回的封装(对应做成了函数式接口),就是上面罗列的函数式接口。

@FunctionInterface 注解声明函数式接口,就是声明一个函数式接口,里面的抽象方法,就是可以使用Lambda表达式;但是注意函数参数类型。


2.接口的默认方法,静态方法

Java8中允许接口存在静态方法,增加了默认方法。默认方法就是可以在接口中有自己的函数体(注意不是虚方法),其他扩展接口,那么直接覆盖默认方法。

3.处理时间、日期的API

4.base64编码

5.nashorn js 引擎

提到nashorn,必须提到jjs(nashorn命令行工具),在cmd中你为什么配置环境变量,就可以输入命令行,编译Java。为什么呢,因为jdk中包含命令行工具,那么Jdk8中,包含了nashorn命令行工具--jjs。


在配置Jdk8环境变量之后,直接在cmd中输入jjs.

执行js脚本:


创建并保存sample.js在 C:> JAVA 文件夹。

sample.js
print('Hello World!');

打开控制台并使用下面的命令。

C:\JAVA>jjs sample.js

看到结果

Hello World!

在Java中调用js:

import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptException;

public class Java8Tester {
   public static void main(String args[]){
      ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); 
      ScriptEngine nashorn = scriptEngineManager.getEngineByName("nashorn"); 
      String name = "Mahesh"; 

      Integer result = null;
      try {
         nashorn.eval("print('" + name + "')");
         result = (Integer) nashorn.eval("10 + 2");   
      }catch(ScriptException e){
         System.out.println("Error executing script: "+ e.getMessage());
      }
      System.out.println(result.toString());
   } 
}

6.并发与并行数组

7.util下面的stream接口

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+       +------+   +------+   +---+   +-------+

以上的流程转换为 Java 代码为:

List<Integer> transactionsIds = 
widgets.stream()
             .filter(b -> b.getColor() == RED)
             .sorted((x,y) -> x.getWeight() - y.getWeight())
             .mapToInt(Widget::getWeight)
             .sum();

什么是 Stream?

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • <strong元素队列< strong="">元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

生成流

在 Java 8 中, 集合接口有两个方法来生成流:

  • stream() − 为集合创建串行流。

  • parallelStream() − 为集合创建并行流。

List < String > strings = Arrays . asList ( " abc " , " " , " bc " , " efg " , " abcd " , " " , " jkl " ) ; List < String > filtered = strings . stream ( ) . filter ( string -> ! string . isEmpty ( ) ) . collect ( Collectors . toList ( ) ) ;

forEach

Stream 提供了新的方法 'forEach' 来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数:

Random random = new Random ( ) ; random . ints ( ) . limit ( 10 ) . forEach ( System . out :: println ) ;

map

map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:

List < Integer > numbers = Arrays . asList ( 3 , 2 , 2 , 3 , 7 , 3 , 5 ) ; // 获取对应的平方数 List < Integer > squaresList = numbers . stream ( ) . map ( i -> i * i ) . distinct ( ) . collect ( Collectors . toList ( ) ) ;

filter

filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:

List < String > strings = Arrays . asList ( " abc " , " " , " bc " , " efg " , " abcd " , " " , " jkl " ) ; // 获取空字符串的数量 int count = strings . stream ( ) . filter ( string -> string . isEmpty ( ) ) . count ( ) ;

limit

limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据:

Random random = new Random ( ) ; random . ints ( ) . limit ( 10 ) . forEach ( System . out :: println ) ;

sorted

sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:

Random random = new Random ( ) ; random . ints ( ) . limit ( 10 ) . sorted ( ) . forEach ( System . out :: println ) ;

并行(parallel)程序

parallelStream 是流并行处理程序的代替方法。以下实例我们使用 parallelStream 来输出空字符串的数量:

List < String > strings = Arrays . asList ( " abc " , " " , " bc " , " efg " , " abcd " , " " , " jkl " ) ; // 获取空字符串的数量 int count = strings . parallelStream ( ) . filter ( string -> string . isEmpty ( ) ) . count ( ) ;

我们可以很容易的在顺序运行和并行直接切换。


Collectors

Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:

List < String > strings = Arrays . asList ( " abc " , " " , " bc " , " efg " , " abcd " , " " , " jkl " ) ; List < String > filtered = strings . stream ( ) . filter ( string -> ! string . isEmpty ( ) ) . collect ( Collectors . toList ( ) ) ; System . out . println ( " 筛选列表: " + filtered ) ; String mergedString = strings . stream ( ) . filter ( string -> ! string . isEmpty ( ) ) . collect ( Collectors . joining ( " , " ) ) ; System . out . println ( " 合并字符串: " + mergedString ) ;

统计

另外,一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上,它们可以用来产生类似如下的统计结果。

List < Integer > numbers = Arrays . asList ( 3 , 2 , 2 , 3 , 7 , 3 , 5 ) ; IntSummaryStatistics stats = integers . stream ( ) . mapToInt ( ( x ) -> x ) . summaryStatistics ( ) ; System . out . println ( " 列表中最大的数 : " + stats . getMax ( ) ) ; System . out . println ( " 列表中最小的数 : " + stats . getMin ( ) ) ; System . out . println ( " 所有数之和 : " + stats . getSum ( ) ) ; System . out . println ( " 平均数 : " + stats . getAverage ( ) ) ;

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值