github博文传送门php
Java8中新增了许多的新特性,在这里本人研究学习了几个较为经常使用的特性,在这里与你们进行分享。(这里推荐深刻理解Java 8用于理解基础知识)本文分为如下几个章节:html
Lambda 表达式,也可称为闭包。Lambda 容许把函数做为一个方法的参数(函数做为参数传递进方法中)。使用 Lambda 表达式可使代码变的更加简洁紧凑。 Lambda表达式能够替代之前普遍使用的内部匿名类,各类回调,好比事件响应器、传入Thread类的Runnable等。java
lambda 表达式的语法格式以下:git
(parameters) -> expression 或 (parameters) ->{ statements; }
这里以经常使用的list排序功能为例:github
private static List<People> peopleList = new ArrayList<People>(); { peopleList.add(new People("a",17)); peopleList.add(new People("b",16)); peopleList.add(new People("c",19)); peopleList.add(new People("d",15)); } @Test public void testLambda(){ System.out.println("排序前:"+peopleList); //第一种,传统匿名Compartor接口排序 Collections.sort(peopleList, new Comparator<People>() { @Override public int compare(People o1, People o2) { return o1.getAge().compareTo(o2.getAge()); } }); System.out.println("匿名接口方法——排序后:"+peopleList); //第二种,使用Lambda表达式来代替匿名接口方法 //1.声明式,不使用大括号,只能够写单条语句 Collections.sort(peopleList,(People a,People b)->a.getAge().compareTo(b.getAge())); System.out.println("Lambda表达式一、排序:"+peopleList);; //2.不声明式,使用大括号,能够写多条语句 Collections.sort(peopleList,(a,b)->{ System.out.print("——————————————"); return a.getAge().compareTo(b.getAge()); }); System.out.println(); System.out.println("Lambda表达式二、排序:"+peopleList); //第三种,使用Lambda表达式调用类的静态方法 Collections.sort(peopleList,(a,b)->People.sortByName(a,b)); System.out.println("Lambda表达式调用静态方法:"+peopleList); //第四种,使用Lambda表达式调用类的实例方法 Collections.sort(peopleList,(a,b)->new People().sortByAge(a,b)); System.out.println("Lambda表达式调用实例方法:"+peopleList); }
对应的运行结果:
(注意:在Lambda表达式中只能对final的对象进行操做,声明的对象也为final)express
有的朋友应该已经观察到了,Lambda 表达式与C中的函数指针,JavaScript的匿名function均有些类似。其实,Lambda表达式本质上是一个匿名的方法,只不过它的目标类型必须是“函数接口(functional interface)”,这是Java8引入的新概念,在接下来会进行更加详细的介绍。编程
在一些Lambda中可能只是单纯的调用方法,好比前例中的3、四,在这种状况下,就可使用方法引用的方式来提升可读性。api
Class::staticMethodName
instance::instanceMethodName
Class::method
Class::new
@Test public void testMethodReference() { //第一种,引用类的静态方法 Collections.sort(peopleList, People::sortByName); System.out.println("引用类的静态方法:" + peopleList); //第二种,引用类的实例方法 Collections.sort(peopleList, new People()::sortByAge); System.out.println("引用类的实例方法:" + peopleList); //第三种,特定类的方法调用() Integer[] a = new Integer[]{3, 1, 2, 4, 6, 5}; Arrays.sort(a, Integer::compare); System.out.println("特定类的方法引用:" + Arrays.toString(a)); //第四种,引用类的构造器 Car car = Car.create(Car::new); System.out.println("引用类的构造器:" + car); }
public static Car create(Supplier<Car> supplier){ return supplier.get(); }
在Java8以前的时代,为已存在接口增长一个通用的实现是十分困难的,接口一旦发布以后就等于定型,若是这时在接口内增长一个方法,那么就会破坏全部实现接口的对象。
默认方法(以前被称为 虚拟扩展方法 或 守护方法)的目标便是解决这个问题,使得接口在发布以后仍能被逐步演化。数组
public interface vehicle { default void print(){ System.out.println("我是一辆车!"); } }
public interface vehicle { static void blowHorn() { System.out.println("按喇叭!!!"); } }
注:静态方法与默认方法都可以有多个,默认方法能够被覆盖。promise
“函数接口(functional interface)”,就是除去默认方法以及继承的抽象方法,只有显式声明一个抽象方法的接口。它使用@FunctionalInterface注解在类上进行标注,也能够省略,Java会自动识别。接下来介绍一些常见的函数接口:
该接口包含方法boolean test(T t),该接口通常用于条件的检测,内部包含三个默认方法:and、or、negate、,即与或非,用于各式的条件判断。例:
Predicate<Integer> predicate = x -> x > 3; predicate.test(10);//true predicate.negate().test(10);//false predicate.or(x -> x < 1).and(x -> x > -1).negate().test(-1);//true
注意:在这里与或非的判断顺序是从左到右的,调用的顺序会影响结果。
Comparator是Java中的经典接口,在排序中较为经常使用。Java8在此之上添加了一些新的默认方法,来丰富该接口的功能。例:
Integer[] a = new Integer[]{3, 1, 2, 4, 6, 5}; Comparator<Integer> comparator = Integer::compare; Arrays.sort(a, comparator); System.out.println("升序:" + Arrays.toString(a)); Arrays.sort(a,comparator.reversed()); System.out.println("降序:"+Arrays.toString(a));
结果
升序:[1, 2, 3, 4, 5, 6] 降序:[6, 5, 4, 3, 2, 1]
该类只包含方法:
T get();
Supplier接口是在1.8中新出现的函数接口,用于支持函数式编程。它用于返回一个任意泛型的实例对象,与工厂的功能相似。
该接口表示一个接受单个输入参数而且没有返回值的操做。不像其余函数式接口,Consumer接口指望执行修改内容的操做。例如 ,咱们须要一个批量修改People的方法,利用Predicate和Consumer就能够这么写
在People内增长updateMany方法:
public static List updateMany(List<People> peopleList, Predicate<People> predicate, Consumer<People> consumer) { for (int i = 0; i < peopleList.size(); i++) { if (predicate.test(peopleList.get(i))) { consumer.accept(peopleList.get(i)); } } return peopleList; }
调用:
//批量修改,将age<18的对象的age改成18 People.updateMany(peopleList, p -> p.getAge() < 18, p -> p.setAge(18)); System.out.println("修改后的结果:" + peopleList);
经过这种方式,能够将内部的判断逻辑与修改代码放至外部调用,而将for、if等语句封装至内部,提升代码的可读性。
其余的还有一些函数接口,如Runnable,InvocationHandler等,在这里就不阐述了。有兴趣的你们能够自行查询资料。Stream、Function、Optional也是函数接口,将在下面进行详细介绍。
Java8提供的java.util.function包的核心函数接口有4个。
Function接口是为Java8提供了函数式编程的基础,apply方法与Consumer的accept方法功能相似,可是提供了返回及类型转换的可能,功能更增强大;再经过andThen与compose方法可使Function组成Function功能链,进行多级数据处理及转换。
R apply(T t) – 将Function对象应用到输入的参数上,而后返回计算结果。
default
default
static
R apply(T t);
接收类型:T
返回类型:R
类型转换:T→R
Function接口的核心方法,能够执行任意的操做,且具备返回值。接收一个T类型的对象,在通过处理后,返回一个R类型的对象。主要功能为类型转换及数据处理。
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); }
接收类型:Function<? super V, ? extends T>
返回类型:Function<V, R>
类型转换:(V+)→(T-)→T→R
apply执行顺序:before→this
此处“V+”指代“? super V”,表示包含V在内的V的任意父类;"T-"指代“? extends T”,表示包含T在内的T的任意子类。compose方法返回一个Function<V,R>,这个Function先执行before的apply方法,将V+类型的数据转换为T-类型,再将T-做为参数传递给this的apply方法,将T类型转换为R类型。
经过compose方法,能够在某个Function执行以前插入一个Function执行。因为返回类型依旧为Function,能够重复调用compose方法造成方法链。
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); }
接收类型:Function<? super R, ? extends V>
返回类型:Function<T, V>
类型转换:T→R→(R+)→(V-)
apply执行顺序:this→after
此处“R+”指代“? super R”,表示包含R在内的R的任意父类;"V-"指代“? extends V”,表示包含V在内的V的任意子类。andThen方法返回一个Function<T,V>,这个Function先执行this的apply方法,将T类型的数据转换为R类型,再将R做为参数传递给after的apply方法,将R+类型转换为V-类型。
经过andThen方法,能够在某个Function执行以后插入一个Function执行。因为返回类型依旧为Function,能够重复调用andThen方法造成方法链。
static <T> Function<T, T> identity() { return t -> t; }
接收类型:无
返回类型:Function<T, T>
类型转换:T→T
该方法的说明是:返回一个函数,它老是返回输入参数。调用该方法能够获得一个返回输入参数的Funtion,这个Function就能够单纯的用来作数据处理,而不用类型转换。
Java8中提供了Stream API,即流式处理。能够经过将List、Set、Array等对象转换成流进行操做。Stream内的流操做分为两种:中间操做和最终操做,中间操做会返回一个全新的Stream对象,意味着你的操做不会影响最初的流;最终操做会将流进行转换或者操做,返回非Stream的对象。Stream能够替代传统的循环操做,从线程上区别,Stream分为串行(Stream)和并行(parallelStream),关于Stream的性能分析能够查看这篇文章《Stream性能分析》。下面来看下Strea内的一些方法:
distinct
java Stream<T> distinct();
去除Stream中重复的对象,并返回一个流。(使用对象的equals方法)
skip
java Stream<T> skip(long n);
跳过Stream中的前n个对象,将其余对象返回一个Stream。若是n超过了Stream中对象的个数,则会返回一个空的Stream。
limit
java Stream<T> limit(long maxSize);
截取Stream的前maxSize个对象,并造成一个新Stream。
filter
java Stream<T> filter(Predicate<? super T> predicate);
根据给定的predicate来过滤对象,返回知足条件的对象构成的Stream。
map
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
经过给定的mapper,将T类型的流转换为R类型的Stream。
flatMap
java <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
flatMap也是将Stream进行转换,flatMap与map的区别在于 flatMap是将一个Stream中的每一个值都转成一个个Stream,而后再将这些流扁平化成为一个Stream。
例(转自:Java8 新特性之流式数据处理):
假设咱们有一个字符串数组String[] strs = {"java8", "is", "easy", "to", "use"};,咱们但愿输出构成这一数组的全部非重复字符,那么咱们可能首先会想到以下实现:
java List<String[]> distinctStrs = Arrays.stream(strs) .map(str -> str.split("")) // 映射成为Stream<String[]> .distinct() .collect(Collectors.toList());
在执行map操做之后,咱们获得是一个包含多个字符串(构成一个字符串的字符数组)的流,此时执行distinct操做是基于在这些字符串数组之间的对比,因此达不到咱们但愿的目的,此时的输出为:
[j, a, v, a, 8] [i, s] [e, a, s, y] [t, o] [u, s, e]
distinct只有对于一个包含多个字符的流进行操做才能达到咱们的目的,即对Stream
java List<String> distinctStrs = Arrays.stream(strs) .map(str -> str.split("")) // 映射成为Stream<String[]> .flatMap(Arrays::stream) // 扁平化为Stream<String> .distinct() .collect(Collectors.toList());
flatMap将由map映射获得的Stream<String[]>,转换成由各个字符串数组映射成的流Stream
sorted
java Stream<T> sorted(); Stream<T> sorted(Comparator<? super T> comparator);
sorted方法能够对Stream进行排序。排序的对象必须实现Comparable,若是没实现会抛出ClassCastException;不提供comparator时,则会调用compareTo方法。
peek
java Stream<T> peek(Consumer<? super T> action);
对流中的每一个对象执行提供的action操做。
在Stack中,peek用于查看一个对象。在流中也是同样,用于在流循环时,根据给定的action进行查看对象。虽然能够进行元素修改操做,但不建议。
综合例:
java Integer[] a = new Integer[]{3, 1, 2, 5, 11, 4, 6, 5, 3, 1}; List<Integer> aList = Arrays.stream(a) .distinct() .skip(1) .filter((e) -> e < 6) .peek(e -> System.out.println("循环1次")) .limit(4) .sorted() .collect(Collectors.toList()); System.out.println(aList);
输出:
循环1次 循环1次 循环1次 循环1次 [1, 2, 4, 5]
Optional<T> min(Comparator<? super T> comparator); Optional<T> max(Comparator<? super T> comparator);
根据给定的comparator返回Stream中的max或min。
long count();
返回Stream中对象的个数。
boolean anyMatch(Predicate<? super T> predicate); boolean allMatch(Predicate<? super T> predicate); boolean noneMatch(Predicate<? super T> predicate);
根据给定的predicate判断Stream是否匹配条件。
<R, A> R collect(Collector<? super T, A, R> collector);
根据给定的collector对Stream中的元素进行操做,返回复杂数据结构的对象。用于将Stream中的对象转换成咱们想要的结构,如list、map、set等。
前例中就使用collect(Collectors.toList())将Stream中的对象转换成List。
Optional<T> reduce(BinaryOperator<T> accumulator); T reduce(T identity, BinaryOperator<T> accumulator);
若是咱们不知但愿单纯的返回List这样的类型,而是但愿将整个Stream通过一些操做后,规约成一个对象返回,就能够用到规约操做。reduce方法有两个参数,其中accumulator表明着规约的操做,即用何种的方式进行参数化处理;identity则是accumulator的标识值(具体用处暂不明)。
例:求和
java Integer[] a = new Integer[]{3, 1, 2, 5, 11, 4, 6, 5, 3, 1}; int sum = Arrays.stream(a) .distinct() .filter((e) -> e < 6) .reduce(0, (x, y) -> x + y);//或.reduce(0, Integer::sum); System.out.println(sum);//15
Object[] toArray();
将Stream中的对象返回成一个Object数组。
void forEach(Consumer<? super T> action);
顾名思义,对Stream中每一个元素进行action操做,与peek相似,但forEach是一个最终操做,通常在结束时查看对象使用。
Optional<T> findFirst(); Optional<T> findAny();
findFirst能够返回Stream中第一个对象,并将它封装在Optional中。
findAny则不是返回第一个对象,而是任意一个对象。在顺序Stream中findFirst和findAny的结果是一致的,但在并行Stream中,findFirst存在着限制,故在并行Stream中须要使用findAny(findAny源码注释中写的是some element?)。一样将对象封装在Optional中。
在java8以前的编程中,咱们老是须要进行if(obj=null)来防止NullPointException,而在java8后,提供了Optional类,它一方面用于防止NullPotinException的判断,另外一方面则为流式编程与函数式变成提供了更好的支持;Optional是一个包含对象的容器,它能够包含null值。在Optional类中封装了许多的方法,来让咱们更好的处理咱们的代码。接下来看看Optional中几个经常使用的方法:
empty & of & ofNullable
java public static <T> Optional<T> empty(){...} public static <T> Optional<T> of(T value) {return new Optional<T>(value);} public static <T> Optional<T> ofNullable(T value){return value == null ? empty() : of(value);}
首先,Optioanl的构造方法是私有的,只能经过以上三个静态方法来获取Optional的实例。empty方法会返回Optional中的常量EMPTY对象,通常在compare时使用,注意这里的EMPTY是单例的并且为常量;通常咱们须要构造一个Optional,使用of或ofNullable方法,of方法会将咱们的传值构造一个新的Optional返回,而ofNullable则在接收null时返回EMPTY实例。
isPresent
java public boolean isPresent() {return value != null;} public void ifPresent(Consumer<? super T> consumer) {if (value != null)consumer.accept(value);}
isPresent方法用于判断Optional包含的value是否为null,第一种方法返回一个boolean;第二种方法则根据判断,为null则什么都不执行,不为null则执行一个consumer操做。
map & flatMap
java public<U> Optional<U> map(Function<? super T, ? extends U> mapper){...} public<U> Optional<U> flatMap(Function<? super T, Optional<U> mapper){...}
map与flatMap与Stream中用法与功能大体相同,都是转换及合并转换,再也不赘述。
get
java public T get() {...}
get方法用于获取value。须要注意的是,若是value为null,则会抛出NoSuchElementException。
filter
java public Optional<T> filter(Predicate<? super T> predicate) {...}
filter方法也是获取value,它能够传入一个predicate,用于判断value是否知足条件。若是value为null,则会返回this;若是predicate.test为true,则返回this,不然会返回EMPTY。
orElse & orElseGet & orElseGet
java public T orElse(T other) {return value != null ? value : other;} public T orElseGet(Supplier<? extends T> other) {return value != null ? value : other.get();} public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X{...}
这三个方法都用于获取value,同时能够在value==null的状况下作出不一样的操做。orElse能够传入一个other,当value==null时则返回null;orElseGet则是使用Supplier,为null时调用get方法;orElseThrow则是接收一个Supplier包含某种异常的exceptionSupplier,为null时则会调用get方法抛出一个异常。
Java8使用新的日期时间API覆盖旧的日期时间API的,处理了如下缺点。
JAVA8引入了java.time包,一个新的日期时间API。限于篇幅与精力问题,这里不对java.time进行过多的介绍,这里推荐几篇我的以为不错的博文以供研究:
Java 类库的新特性之日期时间API (Date/Time API )
Java 8 之 java.time 包