从Java8发布到如今有好几年了,而Java9也提上发布日程了(没记错的话好像就是这个月2017年7月,也许会再度跳票吧,不过不要紧,稳定大于一切,稳定了再发布也行),如今才开始去真正学习,说来也是惭愧。虽然目前工做环境仍然以Java6为主,不过Java8目前已经是大势所趋了。Java8带来了许多使人激动的新特性,如lambda表达式,StreamsAPI与并行集合计算,新的时间日期API(借鉴joda-time),字节码支持保存方法参数名(对于框架开发真的是很是赞的一个特性),Optional类解决空指针问题(虽然Guava中早就有这个了)等。javascript
语法:html
v->System.out.println(v)java
(v)->System.out.println(v)git
(String v)->System.out.println(v)程序员
(v)->{System.out.println(v);return v+1;}github
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6); numbers.forEach((Integer value) -> System.out.println(value));
注意:lambda表达式内若是引用了外部的局部变量,那么这个局部变量必须是final的,若是不是,编译器会自动给加上final。好比下面这段是错误的数据库
//这是错误的 int num = 2; Function<Integer, Integer> stringConverter = (from) -> from * num; num++;//会报错,由于此时num已是final声明的了 System.out.println(stringConverter.apply(3));
仅有一个抽象方法的接口(注:Java8以后接口也能够有非抽象方法,因此此处强调只有一个抽象方法的接口)apache
可选注解:@FunctionalInterface , 做用:编译器会检查,javadoc文档中会特别说明。编程
这里须要强调的是,函数式接口只能有一个抽象方法,而不是指只能有一个方法。这分两点来讲明。首先,在Java 8中,接口运行存在实例方法(见默认方法一节),其次任何被java.lang.Object实现的方法,都不能视为抽象方法,所以,下面的NonFunc接口不是函数式接口,由于equals()方法在java.lang.Object中已经实现。 segmentfault
标准函数式接口
新的 java.util.function 包定义旨在使用 lambdas 的普遍函数式接口。这些接口分为几大类:
Function:接受一个参数,基于参数值返回结果
Predicate:接受一个参数,基于参数值返回一个布尔值
BiFunction:接受两个参数,基于参数值返回结果
Supplier:不接受参数,返回一个结果
Consumer:接受一个参数,无结果 (void)
interface NonFunc { boolean equals(Object obj); }
同理,下面实现的IntHandler接口符合函数式接口要求,虽然看起来它不像,但实际上它是一个彻底符合规范的函数式接口。
@FunctionalInterface public static interface IntHandler{ void handle(int i); boolean equals(Object obj); }
在Java 8以前的版本,接口只能包含抽象方法,Java 8 对接口作了进一步的加强。在接口中能够添加使用 default 关键字修饰的非抽象方法。还能够在接口中定义静态方法。
现在,接口看上去与抽象类的功能愈来愈相似了。这一改进使得Java 8拥有了相似于多继承的能力。一个对象实例,将拥有来自于多个不一样接口的实例方法。
好比,对于接口IHorse,实现以下:
public interface IHorse{ void eat(); default void run(){ System.out.println(“hourse run”); } }
在Java 8中,使用default
关键字,能够在接口内定义实例方法。注意,这个方法是并不是抽象方法,而是拥有特定逻辑的具体实例方法。
全部的动物都能自由呼吸,因此,这里能够再定义一个IAnimal接口,它也包含一个默认方法breath()。
public interface IAnimal { default void breath(){ System.out.println(“breath”); } }
骡是马和驴的杂交物种,所以骡(Mule)能够实现为IHorse,同时骡也是动物,所以有:
public class Mule implements IHorse,IAnimal{ @Override public void eat() { System.out.println(“Mule eat”); } public static void main(String[] args) { Mule m=new Mule(); m.run(); m.breath(); } }
注意上述代码中Mule实例同时拥有来自不一样接口的实现方法。在这Java 8以前是作不到的。从某种程度上说,这种模式能够弥补Java单一继承的一些不便。但同时也要知道,它也将遇到和多继承相同的问题,若是IDonkey也存在一个默认的run()方法,那么同时实现它们的Mule,就会不知所措,由于它不知道应该以哪一个方法为准。此时,因为IHorse和IDonkey拥有相同的默认实例方法,故编译器会抛出一个错误:Duplicate default methods named run with the parameters () and () are inherited from the types IDonkey and IHorse
接口默认实现对于整个函数式编程的流式表达式
很是重要。好比,你们熟悉的java.util.Comparator接口,它在JDK 1.2时就已经被引入。在Java 8中,Comparator接口新增了若干个默认方法,用于多个比较器的整合。其中一个经常使用的默认以下:
default Comparator<T> thenComparing(Comparator<? super T> other) { Objects.requireNonNull(other); return (Comparator<T> & Serializable) (c1, c2) -> { int res = compare(c1, c2); return (res != 0) ? res : other.compare(c1, c2); }; }
有个这个默认方法,在进行排序时,咱们就能够很是方便得进行元素的多条件排序,好比,以下代码构造一个比较器,它先按照字符串长度排序,继而按照大小写不敏感的字母顺序排序。
Comparator<String> cmp = Comparator.comparingInt(String::length) .thenComparing(String.CASE_INSENSITIVE_ORDER);
在接口中,还容许定义静态的方法。接口中的静态方法能够直接用接口来调用。
例如,下面接口中定义了一个静态方法 find,该方法能够直接用 StaticFunInterface .find() 来调用。
public interface StaticFunInterface { public static int find(){ return 1; } } public class TestStaticFun { public static void main(String[] args){ //接口中定义了静态方法 find 直接被调用 StaticFunInterface.fine(); } }
说明:虽然我知道了default方法是为了便于集合接口(新的StreamsAPI)向后兼容而设计的,可是这个接口静态方法暂时还没体会其做用,可能得接触多了才会明白吧。
静态方法引用:ClassName::methodName
实例上的实例方法引用:instanceReference::methodName (这里还可使用this)
超类上的实例方法引用:super::methodName
类型上的实例方法引用:ClassName::methodName
构造方法引用:Class::new
数组构造方法引用:TypeName[]::new
public class InstanceMethodRef { public static void main(String[] args) { List<User> users=new ArrayList<User>(); for(int i=1;i<10;i++){ users.add(new User(i,”billy”+Integer.toString(i))); } users.stream().map(User::getName).forEach(System.out::println); } }
注意几点:
对于第一个方法引用User::getName,表示User类的实例方法。在执行时,Java会自动识别流中的元素(这里指User实例)是做为调用目标仍是调用方法的参数。
通常来讲,若是使用的是静态方法,或者调用目标明确,那么流内的元素会自动做为参数使用。若是函数引用表示实例方法,而且不存在调用目标,那么流内元素就会自动做为调用目标。
所以,若是一个类中存在同名的实例方法和静态函数,那么编译器就会感到很困惑,由于此时,它不知道应该使用哪一个方法进行调用。
打开 Collection Api能够看到多了一个 stream() default 方法:
default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); }
Stream 容许以声明方式处理集合等能够转换为 Stream<T> 的数据, 他有不少特色:
内部迭代 :与原有的 Iterator 不一样, Stream 将迭代操做(相似 for / for-each )所有固化到了Api内部实现, 用户只需传入表达计算逻辑的 lambda 表达式(能够理解为 Supplier 、 Function 这些的 @FunctionalInterface 的实现), Stream 便会自动迭代- 数据触发计算逻辑并生成结果. 内部迭代主要解决了两方面的问题: 避免集合处理时的套路和晦涩 ; 便于库内部实现的多核并行优化 .
流水线 :不少 Stream 操做会再返回一个 Stream , 这样多个操做就能够连接起来, 造成一个大的流水线, 使其看起来像是 对数据源进行数据库式查询 , 这也就让自动优化成为可能, 如 隐式并行 .
隐式并行 :如将 .stream() 替换为 .parallelStream() , Stream 则会自动启用Fork/Join框架, 并行执行各条流水线, 并最终自动将结果进行合并.
延迟计算 :因为 Stream 大部分的操做(如 filter() 、 generate() 、 map() …)都是接受一段 lambda 表达式, 逻辑相似接口实现(能够当作是 回调 ), 所以代码并非当即执行的, 除非流水线上触发一个终端操做, 不然中间操做不会执行任何处理.
短路求值 :有些操做不须要处理整个流就可以拿到结果, 不少像 anyMatch() 、 allMatch() 、 limit() , 只要找到一个元素他们的工做就能够结束, 也就没有必要执行后面的操做, 所以若是后面有大量耗时的操做, 此举可大大节省性能.
Stream 构成
一个流管道(Stream pipeline)一般由3部分构成: 数据源(Source) -> 中间操做/转换(Transforming) -> 终端操做/执行(Operations) : Stream 由数据源生成, 经由中间操做串联起来的一条流水线的转换, 最后由终端操做触发执行拿到结果.
除了前面介绍过的 collection.stream() , 流的生成方式多种多样, 可简单归纳为3类: 通用流 、 数值流 、 其余 , 其中以 通用流最为经常使用, 数值流是Java为 int 、 long 、 double 三种数值类型防 拆装箱 成本所作的优化:
A、通用流
Arrays.stream(T[] array)
Stream.empty()
Stream.generate(Supplier<T> s) 返回无限无序流,其中每一个元素由Supplier<T>生成.
Stream.iterate(T seed, UnaryOperator<T> f) 返回无限有序流。
Stream.of(T... values)
Stream.concat(Stream<? extends T> a, Stream<? extends T> b) 建立一个懒惰链接的流,其元素是第一个流的全部元素,后跟第二个流的全部元素.
StreamSupport.stream(Spliterator<T> spliterator, boolean parallel) 从Spliterator建立一个新的顺序流或并行流。.
B、数值流
Arrays.stream(Xxx[] array) Returns a sequential Int/Long/DoubleStream with the specified array as its source.
XxxStream.empty() Returns an empty sequential Int/Long/DoubleStream .
XxxStream.generate(XxxSupplier s) Returns an infinite sequential unordered stream where each element is generated by the provided Int/Long/DoubleSupplier .
XxxStream.iterate(Xxx seed, XxxUnaryOperator f) Returns an infinite sequential ordered Int/Long/DoubleStream like as Stream.iterate(T seed, UnaryOperator<T> f)
XxxStream.of(Xxx... values) Returns a sequential ordered stream whose elements are the specified values.
XxxStream.concat(XxxStream a, XxxStream b) Creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream.
Int/LongStream.range(startInclusive, endExclusive) Returns a sequential ordered Int/LongStream from startInclusive (inclusive) to endExclusive (exclusive) by an incremental step of 1.
Int/LongStream.rangeClosed(startInclusive, endInclusive) Returns a sequential ordered Int/LongStream from startInclusive (inclusive) to endInclusive (inclusive) by an incremental step of 1.
C、其余
C.一、I/O Stream
BufferedReader.lines()
C.二、File Stream
Files.lines(Path path)
Files.find(Path start, int maxDepth, BiPredicate<Path,BasicFileAttributes> matcher, FileVisitOption... options)
DirectoryStream<Path> newDirectoryStream(Path dir)
Files.walk(Path start, FileVisitOption... options)
C.三、Jar
JarFile.stream()
C.四、Random
Random.ints()
Random.longs()
Random.doubles()
C.五、Pattern
splitAsStream(CharSequence input) …
另外, 三种数值流之间, 以及数值流与通用流之间均可以相互转换:
数值流转换: doubleStream.mapToInt(DoubleToIntFunction mapper) 、 intStream.asLongStream() …
数值流转通用流: longStream.boxed() 、 intStream.mapToObj(IntFunction<? extends U> mapper) …
通用流转数值流: stream.flatMapToInt(Function<? super T,? extends IntStream> mapper) 、 stream.mapToDouble(ToDoubleFunction<? super T> mapper) …
全部的中间操做都会返回另外一个 Stream , 这让多个操做能够连接起来组成中间操做链, 从而造成一条流水线, 所以它的特色就是前面提到的 延迟执行 : 触发流水线上触发一个终端操做, 不然中间操做不执行任何处理.
filter(Predicate<? super T> predicate)
distinct() Returns a stream consisting of the distinct elements (according to Object.equals(Object)
) of this stream.
limit(long maxSize)
skip(long n)
sorted(Comparator<? super T> comparator)
map(Function<? super T,? extends R> mapper) Returns a stream consisting of the results of applying the given function to the elements of this stream.
flatMap(Function<? super T,? extends Stream<? extends R>> mapper) Returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the - provided mapping function to each element.
peek(Consumer<? super T> action) Returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream.
这里着重讲解下 flatMap()
假设咱们有这样一个字符串list: List<String> strs = Arrays.asList("hello", "alibaba", "world"); 如何列出里面各不相同的字符呢?
Stream<Stream<String>> streamStream = strs.stream() .map(str -> Arrays.stream(str.split("")));
咱们将 String 分解成 String[] 后再由 Arrays.stream() 将 String[] 映射成 Stream<String> , 但这个结果是咱们不想看到的: 咱们明明想要的是 Stream<String> 却获得的是 Stream<Stream<String>> , 他把咱们想要的结果包到 Stream 里面了. 这时候就须要咱们的 flatMap() 出场了:
Stream<String> stringStream = strs.stream() .flatMap(str -> Arrays.stream(str.split("")));
flatMap() 把 Stream 中的层级结构扁平化了, 将内层 Stream 内的元素抽取出来, 最终新的 Stream 就没有内层 Stream 了.
能够简单归纳为: flatMap() 方法让你把一个流中的每一个值都换成另外一个 Stream , 而后把全部的 Stream 链接起来成为一个 Stream .
终端操做不只担负着触发流水线执行的任务, 他还须要拿到流水线执行的结果, 其结果为任何不是流的值.
count()
max(Comparator<? super T> comparator)
min(Comparator<? super T> comparator)
allMatch(Predicate<? super T> predicate)
anyMatch(Predicate<? super T> predicate)
noneMatch(Predicate<? super T> predicate)
findAny()
findFirst()
reduce(BinaryOperator<T> accumulator) Performs a reduction on the elements of this stream, using an associative accumulation function, and returns an Optional describing the reduced value, if any.
toArray()
forEach(Consumer<? super T> action)
forEachOrdered(Consumer<? super T> action) Performs an action for each element of this stream, in the encounter order of the stream if the stream has a defined encounter order.
collect(Collector<? super T,A,R> collector) Performs a mutable reduction operation on the elements of this stream using a Collector .
像 IntStream / LongStream / DoubleStream 还提供了 average() 、 sum() 、 summaryStatistics() 这样的操做, 拿到一个对 Stream 进行汇总了的结果.
java.util.stream.Stream 接口继承自 java.util.stream.BaseStream 接口, 而 BaseStream 接口也提供了不少工具方法(如将串行流转换为并行流的 parallel() 方法)供咱们使用:
S onClose(Runnable closeHandler) Returns an equivalent stream with an additional close handler .
void close()
S unordered()
Iterator<T> iterator()
Spliterator<T> spliterator() Returns a spliterator for the elements of this stream.
S sequential()
S parallel()
boolean isParallel()
简单点的:
static int [] arr={1,4,3,6,5,7,2,9}; public static void main(String[]args){ //Array.stream()方法返回了一个流对象。相似于集合或者数组,流对象也是一个对象的集合,它将给予咱们遍历处理流内元素的功能 Arrays.stream(arr).forEach((x)->System.out.println(x)); }
复杂点的:
public void joiningList() { // 生成一段[0,20)序列 List<Integer> list = IntStream.range(0, 20) .boxed() .collect(Collectors.toList()); // 将list内的偶数提取反向排序后聚合为一个String String string = list.stream() .filter(n -> n % 2 == 0) .sorted(Comparator.comparing((Integer i) -> i).reversed()) .limit(3) .peek((i) -> System.out.println("remained: " + i)) .map(String::valueOf) .collect(Collectors.joining()); System.out.println(string); }
public class StreamLambda { private List<Transaction> transactions; @Before public void setUp() { Trader raoul = new Trader("Raoul", "Cambridge"); Trader mario = new Trader("Mario", "Milan"); Trader alan = new Trader("Alan", "Cambridge"); Trader brian = new Trader("Brian", "Cambridge"); transactions = Arrays.asList( new Transaction(brian, 2011, 300), new Transaction(raoul, 2012, 1000), new Transaction(raoul, 2011, 400), new Transaction(mario, 2012, 710), new Transaction(mario, 2012, 700), new Transaction(alan, 2012, 950) ); } @Test public void action() { // 1. 打印2011年发生的全部交易, 并按交易额排序(从低到高) transactions.stream() .filter(transaction -> transaction.getYear() == 2011) .sorted(Comparator.comparing(Transaction::getValue)) .forEachOrdered(System.out::println); // 2. 找出交易员都在哪些不一样的城市工做过 Set<String> distinctCities = transactions.stream() .map(transaction -> transaction.getTrader().getCity()) .collect(Collectors.toSet()); // or .distinct().collect(Collectors.toList()) System.out.println(distinctCities); // 3. 找出全部来自于剑桥的交易员, 并按姓名排序 Trader[] traders = transactions.stream() .map(Transaction::getTrader) .filter(trader -> trader.getCity().equals("Cambridge")) .distinct() .sorted(Comparator.comparing(Trader::getName)) .toArray(Trader[]::new); System.out.println(Arrays.toString(traders)); // 4. 返回全部交易员的姓名字符串, 并按字母顺序排序 String names = transactions.stream() .map(transaction -> transaction.getTrader().getName()) .distinct() .sorted(Comparator.naturalOrder()) .reduce("", (str1, str2) -> str1 + " " + str2); System.out.println(names); // 5. 返回全部交易员的姓名字母串, 并按字母顺序排序 String letters = transactions.stream() .map(transaction -> transaction.getTrader().getName()) .distinct() .map(name -> name.split("")) .flatMap(Arrays::stream) .sorted() .collect(Collectors.joining()); System.out.println(letters); // 6. 有没有交易员是在米兰工做 boolean workMilan = transactions.stream() .anyMatch(transaction -> transaction.getTrader().getCity().equals("Milan")); System.out.println(workMilan); // 7. 打印生活在剑桥的交易员的全部交易额总和 long sum = transactions.stream() .filter(transaction -> transaction.getTrader().getCity().equals("Cambridge")) .mapToLong(Transaction::getValue) .sum(); System.out.println(sum); // 8. 全部交易中,最高的交易额是多少 OptionalInt max = transactions.stream() .mapToInt(Transaction::getValue) .max(); // or transactions.stream().map(Transaction::getValue).max(Comparator.naturalOrder()); System.out.println(max.orElse(0)); // 9. 找到交易额最小的交易 Optional<Transaction> min = transactions.stream() .min(Comparator.comparingInt(Transaction::getValue)); System.out.println(min.orElseThrow(IllegalArgumentException::new)); } }
在Java8中,能够在接口不变的状况下,将流改成并行流。这样,就能够很天然地使用多线程进行集合中的数据处理。
demo:咱们但愿能够统计一个1~1000000内全部的质数的数量。
IntStream.range(1,1000000).parallel().filter(PrimeUtil::isPrime).count();
从集合获得并行流
List<Student> ss =new ArrayList<Student>(); ... double ave = ss.parallelStream().mapToInt(s->s.score).average().getAsDouble(); int[]arr = new int [10000000]; Arrarys.parallelSort(arr);
一、Stream.generate
经过实现 Supplier
接口,你能够本身来控制流的生成。这种情形一般用于随机数、常量的 Stream,或者须要先后元素间维持着某种状态信息的 Stream。把 Supplier 实例传递给 Stream.generate() 生成的 Stream,默认是串行(相对 parallel 而言)但无序的(相对 ordered 而言)。因为它是无限的,在管道中,必须利用 limit 之类的操做限制 Stream 大小。
//生成 10 个随机整数 Random seed = new Random(); Supplier<Integer> random = seed::nextInt; Stream.generate(random).limit(10).forEach(System.out::println); //Another way IntStream.generate(() -> (int) (System.nanoTime() % 100)). limit(10).forEach(System.out::println);
注意几个关键词:默认串行、无序、无限(须要进行短路求值操做如limit)。
二、Stream.iterate
iterate 跟 reduce 操做很像,接受一个种子值,和一个 UnaryOperator(例如 f)。而后种子值成为 Stream 的第一个元素,f(seed) 为第二个,f(f(seed)) 第三个,以此类推。
//生成一个等差数列 0 3 6 9 12 15 18 21 24 27 Stream.iterate(0, n -> n + 3).limit(10). forEach(x -> System.out.print(x + " "));.
与 Stream.generate 相仿,在 iterate 时候管道必须有 limit 这样的操做来限制 Stream 大小。
java.util.stream.Collectors 类的主要做用就是辅助进行各种有用的 reduction 操做,例如转变输出为 Collection,把 Stream 元素进行归组。
一、groupingBy/partitioningBy
//按照年龄归组 Map<Integer, List<Person>> personGroups = Stream.generate(new PersonSupplier()). limit(100). collect(Collectors.groupingBy(Person::getAge)); Iterator it = personGroups.entrySet().iterator(); while (it.hasNext()) { Map.Entry<Integer, List<Person>> persons = (Map.Entry) it.next(); System.out.println("Age " + persons.getKey() + " = " + persons.getValue().size()); } //按照未成年人和成年人归组 Map<Boolean, List<Person>> children = Stream.generate(new PersonSupplier()). limit(100). collect(Collectors.partitioningBy(p -> p.getAge() < 18)); System.out.println("Children number: " + children.get(true).size()); System.out.println("Adult number: " + children.get(false).size()); //在使用条件“年龄小于 18”进行分组后能够看到,不到 18 岁的未成年人是一组,成年人是另一组。partitioningBy 实际上是一种特殊的 groupingBy,它依照条件测试的是否两种结果来构造返回的数据结构,get(true) 和 get(false) 能即为所有的元素对象。
其实这里不少观念和Spark的RDD操做很类似。
总之,Stream 的特性能够概括为:
不是数据结构,它没有内部存储,它只是用操做管道从 source(数据结构、数组、generator function、IO channel)抓取数据。
它也毫不修改本身所封装的底层数据结构的数据。例如 Stream 的 filter 操做会产生一个不包含被过滤元素的新 Stream,而不是从 source 删除那些元素。
全部 Stream 的操做必须以 lambda 表达式为参数
不支持索引访问,你能够请求第一个元素,但没法请求第二个,第三个,或最后一个。
很容易生成数组或者 List
惰性化,Intermediate 操做永远是惰性化的。
不少 Stream 操做是向后延迟的,一直到它弄清楚了最后须要多少数据才会开始。
并行能力,当一个 Stream 是并行化的,就不须要再写多线程代码,全部对它的操做会自动并行进行的。
能够是无限的,集合有固定大小,Stream 则没必要。limit(n) 和 findFirst() 这类的 short-circuiting 操做能够对无限的 Stream 进行运算并很快完成。
对于注解,Java 8 主要有两点改进:类型注解和重复注解。
Java 8 的类型注解扩展了注解使用的范围。在该版本以前,注解只能是在声明的地方使用。如今几乎能够为任何东西添加注解:局部变量、类与接口,就连方法的异常也能添加注解。新增的两个注释的程序元素类型 ElementType.TYPE_USE 和 ElementType.TYPE_PARAMETER 用来描述注解的新场合。
ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中。而 ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中(例如声明语句、泛型和强制转换语句中的类型)。
对类型注解的支持,加强了经过静态分析工具发现错误的能力。原先只能在运行时发现的问题能够提早在编译的时候被排查出来。Java 8 自己虽然没有自带类型检测的框架,但能够经过使用 Checker Framework 这样的第三方工具,自动检查和确认软件的缺陷,提升生产效率。
在Java8以前使用注解的一个限制是相同的注解在同一位置只能声明一次,不能声明屡次。Java 8 引入了重复注解机制,这样相同的注解能够在同一地方声明屡次。重复注解机制自己必须用 @Repeatable
注解。
增长了一些新的 IO/NIO 方法,使用这些方法能够从文件或者输入流中获取流(java.util.stream.Stream),经过对流的操做,能够简化文本行处理、目录遍历和文件查找。
新增的 API 以下:
BufferedReader.line(): 返回文本行的流 Stream<String>
File.lines(Path, Charset):返回文本行的流 Stream<String>
File.list(Path): 遍历当前目录下的文件和目录
File.walk(Path, int, FileVisitOption): 遍历某一个目录下的全部文件和指定深度的子目录
File.find(Path, int, BiPredicate, FileVisitOption... ): 查找相应的文件
下面就是用流式操做列出当前目录下的全部文件和目录:
Files.list(new File(".").toPath()) .forEach(System.out::println);
Java 的日期与时间 API 问题由来已久,Java 8 以前的版本中关于时间、日期及其余时间日期格式化类因为线程安全、重量级、序列化成本高等问题而饱受批评。Java 8 吸取了 Joda-Time 的精华,以一个新的开始为 Java 建立优秀的 API。新的 java.time 中包含了全部关于时钟(Clock),本地日期(LocalDate)、本地时间(LocalTime)、本地日期时间(LocalDateTime)、时区(ZonedDateTime)和持续时间(Duration)
的类。历史悠久的 Date 类新增了 toInstant() 方法,用于把 Date 转换成新的表示形式
。这些新增的本地化时间日期 API 大大简化了了日期时间和本地化的管理。
Java日期/时间API包含如下相应的包。
java.time包:这是新的Java日期/时间API的基础包,全部的主要基础类都是这个包的一部分,如:LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration等等。全部这些类都是不可变的和线程安全的,在绝大多数状况下,这些类可以有效地处理一些公共的需求。
java.time.chrono包:这个包为非ISO的日历系统定义了一些泛化的API,咱们能够扩展AbstractChronology类来建立本身的日历系统。
java.time.format包:这个包包含可以格式化和解析日期时间对象的类,在绝大多数状况下,咱们不该该直接使用它们,由于java.time包中相应的类已经提供了格式化和解析的方法
。
java.time.temporal包:这个包包含一些时态对象,咱们能够用其找出关于日期/时间对象的某个特定日期或时间
,好比说,能够找到某月的第一天或最后一天。你能够很是容易地认出这些方法,由于它们都具备“withXXX”的格式。
java.time.zone包:这个包包含支持不一样时区以及相关规则的类。
例如,下面是对 LocalDate,LocalTime 的简单应用:
//LocalDate只保存日期系统的日期部分,有时区信息,LocalTime只保存时间部分,没有时区信息。LocalDate和LocalTime均可以从Clock对象建立。 //LocalDate LocalDate localDate = LocalDate.now(); //获取本地日期 localDate = LocalDate.ofYearDay(2014, 200); // 得到 2014 年的第 200 天 System.out.println(localDate.toString());//输出:2014-07-19 localDate = LocalDate.of(2014, Month.SEPTEMBER, 10); //2014 年 9 月 10 日 System.out.println(localDate.toString());//输出:2014-09-10 //LocalTime LocalTime localTime = LocalTime.now(); //获取当前时间 System.out.println(localTime.toString());//输出当前时间 localTime = LocalTime.of(10, 20, 50);//得到 10:20:50 的时间点 System.out.println(localTime.toString());//输出: 10:20:50 //Clock 时钟,Clock类能够替换 System.currentTimeMillis() 和 TimeZone.getDefault(). 如:Clock.systemDefaultZone().millis() Clock clock = Clock.systemDefaultZone();//获取系统默认时区 (当前瞬时时间 ) long millis = clock.millis();// //LocalDateTime类合并了LocalDate和LocalTime,它保存有ISO-8601日期系统的日期和时间,可是没有时区信息。 final LocalDateTime datetime = LocalDateTime.now(); final LocalDateTime datetimeFromClock = LocalDateTime.now( clock ); System.out.println( datetime ); System.out.println( datetimeFromClock ); //若是您须要一个类持有日期时间和时区信息,可使用ZonedDateTime,它保存有ISO-8601日期系统的日期和时间,并且有时区信息。 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 );
Duration类,Duration持有的时间精确到纳秒。它让咱们很容易计算两个日期中间的差别。让咱们来看一下:
// 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() );
上面的例子计算了两个日期(2014年4月16日和2014年5月16日)之间的持续时间(基于天数和小时)
日期API操做
//日期算术操做,多很多天期/时间API类都实现了一系列工具方法,如:加/减天数、周数、月份数,等等。还有其余的工具方法可以使用TemporalAdjuster调整日期,并计算两个日期间的周期。 LocalDate today = LocalDate.now(); //Get the Year, check if it's leap year System.out.println("Year "+today.getYear()+" is Leap Year? "+today.isLeapYear()); //Compare two LocalDate for before and after System.out.println("Today is before 01/01/2015? "+today.isBefore(LocalDate.of(2015,1,1))); //Create LocalDateTime from LocalDate System.out.println("Current Time="+today.atTime(LocalTime.now())); //plus and minus operations System.out.println("10 days after today will be "+today.plusDays(10)); System.out.println("3 weeks after today will be "+today.plusWeeks(3)); System.out.println("20 months after today will be "+today.plusMonths(20)); System.out.println("10 days before today will be "+today.minusDays(10)); System.out.println("3 weeks before today will be "+today.minusWeeks(3)); System.out.println("20 months before today will be "+today.minusMonths(20)); //Temporal adjusters for adjusting the dates System.out.println("First date of this month= "+today.with(TemporalAdjusters.firstDayOfMonth())); LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear()); System.out.println("Last date of this year= "+lastDayOfYear); Period period = today.until(lastDayOfYear); System.out.println("Period Format= "+period); System.out.println("Months remaining in the year= "+period.getMonths());
解析和格式化:将一个日期格式转换为不一样的格式,以后再解析一个字符串,获得日期时间对象
//Format examples LocalDate date = LocalDate.now(); //default format System.out.println("Default format of LocalDate="+date); //specific format System.out.println(date.format(DateTimeFormatter.ofPattern("d::MMM::uuuu"))); System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE)); LocalDateTime dateTime = LocalDateTime.now(); //default format System.out.println("Default format of LocalDateTime="+dateTime); //specific format System.out.println(dateTime.format(DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss"))); System.out.println(dateTime.format(DateTimeFormatter.BASIC_ISO_DATE)); Instant timestamp = Instant.now(); //default format System.out.println("Default format of Instant="+timestamp); //Parse examples LocalDateTime dt = LocalDateTime.parse("27::Apr::2014 21::39::48", DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss")); System.out.println("Default format after parsing = "+dt);
旧的日期时间支持转换:
//Date to Instant Instant timestamp = new Date().toInstant(); //Now we can convert Instant to LocalDateTime or other similar classes LocalDateTime date = LocalDateTime.ofInstant(timestamp, ZoneId.of(ZoneId.SHORT_IDS.get("PST"))); System.out.println("Date = "+date); //Calendar to Instant Instant time = Calendar.getInstance().toInstant(); System.out.println(time); //TimeZone to ZoneId ZoneId defaultZone = TimeZone.getDefault().toZoneId(); System.out.println(defaultZone); //ZonedDateTime from specific Calendar ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime(); System.out.println(gregorianCalendarDateTime); //Date API to Legacy classes Date dt = Date.from(Instant.now()); System.out.println(dt); TimeZone tz = TimeZone.getTimeZone(defaultZone); System.out.println(tz); GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime); System.out.println(gc);
Future是Java 5添加的类,用来描述一个异步计算的结果。你可使用isDone方法检查计算是否完成,或者使用get阻塞住调用线程,直到计算完成返回结果,你也可使用cancel方法中止任务的执行。
虽然Future以及相关使用方法提供了异步执行任务的能力,可是对于结果的获取倒是很不方便,只能经过阻塞或者轮询的方式获得任务的结果。阻塞的方式显然和咱们的异步编程的初衷相违背,轮询的方式又会耗费无谓的CPU资源,并且也不能及时地获得计算结果,为何不能用观察者设计模式当计算结果完成及时通知监听者呢?
其实Google guava早就提供了通用的扩展Future:ListenableFuture、SettableFuture 以及辅助类Futures等,方便异步编程。
final String name = ...; inFlight.add(name); ListenableFuture<Result> future = service.query(name); future.addListener(new Runnable() { public void run() { processedCount.incrementAndGet(); inFlight.remove(name); lastProcessed.set(name); logger.info("Done with {0}", name); } }, executor);
在Java 8中, 新增长了一个包含50个方法左右的类: CompletableFuture,提供了很是强大的Future的扩展功能,能够帮助咱们简化异步编程的复杂性,提供了函数式编程的能力,能够经过回调的方式处理计算结果,而且提供了转换和组合CompletableFuture的方法。
CompletableFuture类实现了CompletionStage和Future接口,因此你仍是能够像之前同样经过阻塞或者轮询的方式得到结果,尽管这种方式不推荐使用。
尽管Future能够表明在另外的线程中执行的一段异步代码,可是你仍是能够在自己线程中执行:
public class BasicMain { public static CompletableFuture<Integer> compute() { final CompletableFuture<Integer> future = new CompletableFuture<>(); return future; } public static void main(String[] args) throws Exception { final CompletableFuture<Integer> f = compute(); class Client extends Thread { CompletableFuture<Integer> f; Client(String threadName, CompletableFuture<Integer> f) { super(threadName); this.f = f; } @Override public void run() { try { System.out.println(this.getName() + ": " + f.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } new Client("Client1", f).start(); new Client("Client2", f).start(); System.out.println("waiting"); //上面的代码中future没有关联任何的Callback、线程池、异步任务等,若是客户端调用future.get就会一致傻等下去。除非咱们显式指定complete f.complete(100); //f.completeExceptionally(new Exception()); System.in.read(); } }
CompletableFuture.complete()、CompletableFuture.completeExceptionally只能被调用一次。可是咱们有两个后门方法能够重设这个值:obtrudeValue、obtrudeException,可是使用的时候要当心,由于complete已经触发了客户端,有可能致使客户端会获得不指望的结果。
一、建立CompletableFuture对象。
public static <U> CompletableFuture<U> completedFuture(U value)
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
注意:以Async结尾而且没有指定Executor的方法会使用ForkJoinPool.commonPool()
做为它的线程池执行异步代码。由于方法的参数类型都是函数式接口,因此可使用lambda表达式实现异步任务
二、计算结果完成时的处理
当CompletableFuture的计算结果完成,或者抛出异常的时候,咱们能够执行特定的Action。主要是下面的方法:
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)
注意:方法不以Async结尾,意味着Action使用相同的线程执行,而Async可能会使用其它的线程去执行(若是使用相同的线程池,也可能会被同一个线程选中执行)
public class Main { private static Random rand = new Random(); private static long t = System.currentTimeMillis(); static int getMoreData() { System.out.println("begin to start compute"); try { Thread.sleep(10000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("end to start compute. passed " + (System.currentTimeMillis() - t)/1000 + " seconds"); return rand.nextInt(1000); } public static void main(String[] args) throws Exception { CompletableFuture<Integer> future = CompletableFuture.supplyAsync(Main::getMoreData); Future<Integer> f = future.whenComplete((v, e) -> { System.out.println(v); System.out.println(e); }); System.out.println(f.get()); System.in.read(); } }
下面一组方法虽然也返回CompletableFuture对象,可是对象的值和原来的CompletableFuture计算的值不一样。当原先的CompletableFuture的值计算完成或者抛出异常的时候,会触发这个CompletableFuture对象的计算,结果由BiFunction参数计算而得。
所以这组方法兼有whenComplete和转换的两个功能。
public <U> CompletableFuture<U> handle(BiFunction<? super T,Throwable,? extends U> fn)
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn)
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn, Executor executor)
一样,不以Async结尾的方法由原来的线程计算,以Async结尾的方法由默认的线程池ForkJoinPool.commonPool()或者指定的线程池executor运行。
三、转换
CompletableFuture能够做为monad(单子)和functor。因为回调风格的实现,咱们没必要由于等待一个计算完成而阻塞着调用线程,而是告诉CompletableFuture当计算完成的时候请执行某个function。并且咱们还能够将这些操做串联起来,或者将CompletableFuture组合起来。
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
这一组函数的功能是当原来的CompletableFuture计算完后,将结果传递给函数fn,将fn的结果做为新的CompletableFuture计算结果。所以它的功能至关于将CompletableFuture<T>转换成CompletableFuture<U>
它们与handle方法的区别在于handle方法会处理正常计算值和异常,所以它能够屏蔽异常,避免异常继续抛出。而thenApply方法只是用来处理正常值,所以一旦有异常就会抛出。
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { return 100; }); CompletableFuture<String> f = future.thenApplyAsync(i -> i * 10).thenApply(i -> i.toString()); System.out.println(f.get()); //"1000"
须要注意的是,这些转换并非立刻执行的,也不会阻塞,而是在前一个stage完成后继续执行。
四、纯消费(执行Action)
上面的方法是当计算完成的时候,会生成新的计算结果(thenApply, handle),或者返回一样的计算结果whenComplete,CompletableFuture还提供了一种处理结果的方法,只对结果执行Action,而不返回新的计算值,所以计算值为Void:
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor)
看它的参数类型也就明白了,它们是函数式接口Consumer,这个接口只有输入,没有返回值。
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { return 100; }); CompletableFuture<Void> f = future.thenAccept(System.out::println); System.out.println(f.get());
thenAcceptBoth
以及相关方法提供了相似的功能,当两个CompletionStage都正常完成计算的时候,就会执行提供的action,它用来组合另一个异步的结果。 runAfterBoth
是当两个CompletionStage都正常完成计算的时候,执行一个Runnable,这个Runnable并不使用计算的结果。
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T,? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T,? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T,? super U> action, Executor executor)
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action)
例子以下:
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { return 100; }); CompletableFuture<Void> f = future.thenAcceptBoth(CompletableFuture.completedFuture(10), (x, y) -> System.out.println(x * y)); System.out.println(f.get());
更完全地,下面一组方法当计算完成的时候会执行一个Runnable,与thenAccept不一样,Runnable并不使用CompletableFuture计算的结果。
public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor)
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { return 100; }); CompletableFuture<Void> f = future.thenRun(() -> System.out.println("finished")); System.out.println(f.get());
所以,你能够根据方法的参数的类型来加速你的记忆。Runnable类型的参数会忽略计算的结果,Consumer是纯消费计算结果,BiConsumer会组合另一个CompletionStage纯消费,Function会对计算结果作转换,BiFunction会组合另一个CompletionStage的计算结果作转换。
五、组合compose
public <U> CompletableFuture<U> thenCompose(Function<? super T,? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T,? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T,? extends CompletionStage<U>> fn, Executor executor)
这一组方法接受一个Function做为参数,这个Function的输入是当前的CompletableFuture的计算值,返回结果将是一个新的CmpletableFuture,这个新的CompletableFuture会组合原来的CompletableFuture和函数返回的CompletableFuture。
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor)
两个CompletionStage是并行执行的,它们之间并无前后依赖顺序,other并不会等待先前的CompletableFuture执行完毕后再执行。
六、Either
thenAcceptBoth和runAfterBoth是当两个CompletableFuture都计算完成,而咱们下面要了解的方法是当任意一个CompletableFuture计算完成的时候就会执行。
public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor)
public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T,U> fn)
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T,U> fn)
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T,U> fn, Executor executor)
下面这个例子有时会输出100,有时候会输出200,哪一个Future先完成就会根据它的结果计算。
Random rand = new Random(); CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(10000 + rand.nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } return 100; }); CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(10000 + rand.nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } return 200; }); CompletableFuture<String> f = future.applyToEither(future2,i -> i.toString());
七、辅助方法 allOf 和 anyOf
用来组合多个CompletableFuture。
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
allOf方法是当全部的CompletableFuture都执行完后执行计算。
anyOf方法是当任意一个CompletableFuture执行完后就会执行计算,计算的结果相同。
八、更进一步
若是你用过Guava的Future类,你就会知道它的Futures辅助类提供了不少便利方法,用来处理多个Future,而不像Java的CompletableFuture,只提供了allOf、anyOf两个方法。
好比有这样一个需求,将多个CompletableFuture组合成一个CompletableFuture,这个组合后的CompletableFuture的计算结果是个List,它包含前面全部的CompletableFuture的计算结果,guava的Futures.allAsList能够实现这样的功能.
可是对于java CompletableFuture,咱们须要一些辅助方法:
public static <T> CompletableFuture<List<T>> sequence(List<CompletableFuture<T>> futures) { CompletableFuture<Void> allDoneFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])); return allDoneFuture.thenApply(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.<T>toList())); } public static <T> CompletableFuture<Stream<T>> sequence(Stream<CompletableFuture<T>> futures) { List<CompletableFuture<T>> futureList = futures.filter(f -> f != null).collect(Collectors.toList()); return sequence(futureList); }
Java Future转CompletableFuture:
public static <T> CompletableFuture<T> toCompletable(Future<T> future, Executor executor) { return CompletableFuture.supplyAsync(() -> { try { return future.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } }, executor); }
github有多个项目能够实现Java CompletableFuture与其它Future (如Guava ListenableFuture)之间的转换,如spotify/futures-extra、future-converter、scala/scala-java8-compat 等。
你们可能都有这样的经历:调用一个方法获得了返回值却不能直接将返回值做为参数去调用别的方法。咱们首先要判断这个返回值是否为null,只有在非空的前提下才能将其做为其余方法的参数。
Java 8引入了一个新的Optional类。Optional类的Javadoc描述以下:这是一个能够为null的容器对象。若是值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
一、of:为非null的值建立一个Optional。须要注意的是,建立对象时传入的参数不能为null。若是传入参数为null,则抛出NullPointerException
//调用工厂方法建立Optional实例 Optional<String> name = Optional.of("Sanaulla"); //错误写法,传入参数为null,抛出NullPointerException. Optional<String> someNull = Optional.of(null);
二、ofNullable:为指定的值建立一个Optional,若是指定的值为null,则返回一个空的Optional。
ofNullable与of方法类似,惟一的区别是能够接受参数为null的状况
三、isPresent:若是值存在返回true,不然返回false。
四、get:若是Optional有值则将其返回,不然抛出NoSuchElementException。
五、ifPresent:若是Optional实例有值则为其调用consumer,不然不作处理
要理解ifPresent方法,首先须要了解Consumer类。简答地说,Consumer类包含一个抽象方法。该抽象方法对传入的值进行处理,但没有返回值。Java8支持不用接口直接经过lambda表达式传入参数。
//ifPresent方法接受lambda表达式做为参数。 //lambda表达式对Optional的值调用consumer进行处理。 name.ifPresent((value) -> { System.out.println("The length of the value is: " + value.length()); });
六、orElse:若是有值则将其返回,不然返回orElse方法传入的参数。
七、orElseGet:orElseGet与orElse方法相似,区别在于获得的默认值。orElse方法将传入的字符串做为默认值,orElseGet方法能够接受Supplier接口的实现用来生成默认值
。示例以下:
//orElseGet与orElse方法相似,区别在于orElse传入的是默认值, //orElseGet能够接受一个lambda表达式生成默认值。 //输出:Default Value System.out.println(empty.orElseGet(() -> "Default Value")); //输出:Sanaulla System.out.println(name.orElseGet(() -> "Default Value"));
八、orElseThrow:若是有值则将其返回,不然抛出supplier接口建立的异常。
在orElseGet方法中,咱们传入一个Supplier接口。然而,在orElseThrow中咱们能够传入一个lambda表达式或方法,若是值不存在来抛出异常。示例以下:
try { //orElseThrow与orElse方法相似。与返回默认值不一样, //orElseThrow会抛出lambda表达式或方法生成的异常 empty.orElseThrow(ValueAbsentException::new); } catch (Throwable ex) { //输出: No value present in the Optional instance System.out.println(ex.getMessage()); }
九、map:若是有值,则对其执行调用mapping函数获得返回值。若是返回值不为null,则建立包含mapping返回值的Optional做为map方法返回值,不然返回空Optional。
map方法用来对Optional实例的值执行一系列操做。
//map方法执行传入的lambda表达式参数对Optional实例的值进行修改。 //为lambda表达式的返回值建立新的Optional实例做为map方法的返回值。 Optional<String> upperName = name.map((value) -> value.toUpperCase()); System.out.println(upperName.orElse("No value found"));
十、flatMap:若是有值,为其执行mapping函数返回Optional类型返回值,不然返回空Optional。flatMap与map(Funtion)方法相似,区别在于flatMap中的mapper返回值必须是Optional。调用结束时,flatMap不会对结果用Optional封装。
//flatMap与map(Function)很是相似,区别在于传入方法的lambda表达式的返回类型。 //map方法中的lambda表达式返回值能够是任意类型,在map函数返回以前会包装为Optional。 //但flatMap方法中的lambda表达式返回值必须是Optionl实例。 upperName = name.flatMap((value) -> Optional.of(value.toUpperCase())); System.out.println(upperName.orElse("No value found"));//输出SANAULLA
十一、filter:若是有值而且知足断言条件返回包含该值的Optional,不然返回空Optional。
//filter方法检查给定的Option值是否知足某些条件。 //若是知足则返回同一个Option实例,不然返回空Optional。 Optional<String> longName = name.filter((value) -> value.length() > 6); System.out.println(longName.orElse("The name is less than 6 characters"));//输出Sanaulla //另外一个例子是Optional值不知足filter指定的条件。 Optional<String> anotherName = Optional.of("Sana"); Optional<String> shortName = anotherName.filter((value) -> value.length() > 6); //输出:name长度不足6字符 System.out.println(shortName.orElse("The name is less than 6 characters"));
一、参数名字
很长时间以来,Java程序员想尽办法把参数名字保存在java字节码里,而且让这些参数名字在运行时可用。Java 8 终于把这个需求加入到了Java语言(使用反射API和Parameter.getName()
方法)和字节码里(使用java编译命令javac的–parameters参数
)。
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.isNamePresent()
来验证参数名是否是可用。
若是你编译这个class的时候没有添加参数–parameters,运行的时候你会获得这个结果:
Parameter: arg0
编译的时候添加了–parameters参数的话,运行结果会不同:
Parameter: args
对于有经验的Maven使用者,–parameters参数能够添加到maven-compiler-plugin的配置部分:
<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>
eclipse中也能够进行相关配置:
Java 8提供了一个新的Nashorn javascript引擎,它容许咱们在JVM上运行特定的javascript应用。Nashorn javascript引擎只是javax.script.ScriptEngine另外一个实现,并且规则也同样,容许Java和JavaScript互相操做。这里有个小例子:
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;");
推荐参考:http://www.importnew.com/2266...
对Base64的支持最终成了Java 8标准库的一部分,很是简单易用:
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 ); } }
新的Base64API也支持URL和MINE的编码解码。 (Base64.getUrlEncoder() / Base64.getUrlDecoder(), Base64.getMimeEncoder() / Base64.getMimeDecoder()).
Java 8新增长了不少方法支持并行的数组处理。最重要的大概是parallelSort()
这个方法显著地使排序在多核计算机上速度加快。下面的小例子演示了这个新的方法(parallelXXX)的行为。
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(); } }
这一小段代码使用parallelSetAll() t方法填充这个长度是2000的数组,而后使用parallelSort() 排序。这个程序输出了排序前和排序后的10个数字来验证数组真的已经被排序了。
在新增Stream机制与lambda的基础之上,在java.util.concurrent.ConcurrentHashMap中加入了一些新方法来支持汇集操做。同时也在java.util.concurrent.ForkJoinPool类中加入了一些新方法来支持共有资源池(common pool)
新增的java.util.concurrent.locks.StampedLock
类提供一直基于容量的锁,这种锁有三个模型来控制读写操做(它被认为是不太有名的java.util.concurrent.locks.ReadWriteLock类的替代者)。
在java.util.concurrent.atomic包中还增长了下面这些类:
DoubleAccumulator
DoubleAdder
LongAccumulator
LongAdder
Jdeps是一个功能强大的命令行工具,它能够帮咱们显示出包层级或者类层级java类文件的依赖关系。它接受class文件、目录、jar文件做为输入,默认状况下,jdeps会输出到控制台。
JVM内存永久区(Perm)已经被metaspace
替换(JEP 122)。JVM参数 -XX:PermSize 和 –XX:MaxPermSize被XX:MetaSpaceSize 和 -XX:MaxMetaspaceSize代替。
写给大忙人的JavaSE8